VirtualBox

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

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

Main/GuestOSTypeImpl,Global,Machine: New Windows 11 guest OS type, and also a new recommended CPU count field for every guest OS.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 540.0 KB
Line 
1/* $Id: MachineImpl.cpp 91470 2021-09-29 19:55:51Z 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 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1153{
1154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1162{
1163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 HRESULT rc = i_checkStateDependency(MutableStateDep);
1166 if (FAILED(rc)) return rc;
1167
1168 i_setModified(IsModified_MachineData);
1169 mHWData.backup();
1170 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1176{
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aPointingHIDType = mHWData->mPointingHIDType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1185{
1186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 HRESULT rc = i_checkStateDependency(MutableStateDep);
1189 if (FAILED(rc)) return rc;
1190
1191 i_setModified(IsModified_MachineData);
1192 mHWData.backup();
1193 mHWData->mPointingHIDType = aPointingHIDType;
1194
1195 return S_OK;
1196}
1197
1198HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1199{
1200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1201
1202 *aChipsetType = mHWData->mChipsetType;
1203
1204 return S_OK;
1205}
1206
1207HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1208{
1209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1210
1211 HRESULT rc = i_checkStateDependency(MutableStateDep);
1212 if (FAILED(rc)) return rc;
1213
1214 if (aChipsetType != mHWData->mChipsetType)
1215 {
1216 i_setModified(IsModified_MachineData);
1217 mHWData.backup();
1218 mHWData->mChipsetType = aChipsetType;
1219
1220 // Resize network adapter array, to be finalized on commit/rollback.
1221 // We must not throw away entries yet, otherwise settings are lost
1222 // without a way to roll back.
1223 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1224 size_t oldCount = mNetworkAdapters.size();
1225 if (newCount > oldCount)
1226 {
1227 mNetworkAdapters.resize(newCount);
1228 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1229 {
1230 unconst(mNetworkAdapters[slot]).createObject();
1231 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1232 }
1233 }
1234 }
1235
1236 return S_OK;
1237}
1238
1239HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1240{
1241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 *aIommuType = mHWData->mIommuType;
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::setIommuType(IommuType_T aIommuType)
1249{
1250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 HRESULT rc = i_checkStateDependency(MutableStateDep);
1253 if (FAILED(rc)) return rc;
1254
1255 if (aIommuType != mHWData->mIommuType)
1256 {
1257 if (aIommuType == IommuType_Intel)
1258 {
1259#ifndef VBOX_WITH_IOMMU_INTEL
1260 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1261 return E_UNEXPECTED;
1262#endif
1263 }
1264
1265 i_setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mIommuType = aIommuType;
1268 }
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1274{
1275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 aParavirtDebug = mHWData->mParavirtDebug;
1278 return S_OK;
1279}
1280
1281HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1282{
1283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 HRESULT rc = i_checkStateDependency(MutableStateDep);
1286 if (FAILED(rc)) return rc;
1287
1288 /** @todo Parse/validate options? */
1289 if (aParavirtDebug != mHWData->mParavirtDebug)
1290 {
1291 i_setModified(IsModified_MachineData);
1292 mHWData.backup();
1293 mHWData->mParavirtDebug = aParavirtDebug;
1294 }
1295
1296 return S_OK;
1297}
1298
1299HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1300{
1301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1302
1303 *aParavirtProvider = mHWData->mParavirtProvider;
1304
1305 return S_OK;
1306}
1307
1308HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1309{
1310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 HRESULT rc = i_checkStateDependency(MutableStateDep);
1313 if (FAILED(rc)) return rc;
1314
1315 if (aParavirtProvider != mHWData->mParavirtProvider)
1316 {
1317 i_setModified(IsModified_MachineData);
1318 mHWData.backup();
1319 mHWData->mParavirtProvider = aParavirtProvider;
1320 }
1321
1322 return S_OK;
1323}
1324
1325HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1326{
1327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1328
1329 *aParavirtProvider = mHWData->mParavirtProvider;
1330 switch (mHWData->mParavirtProvider)
1331 {
1332 case ParavirtProvider_None:
1333 case ParavirtProvider_HyperV:
1334 case ParavirtProvider_KVM:
1335 case ParavirtProvider_Minimal:
1336 break;
1337
1338 /* Resolve dynamic provider types to the effective types. */
1339 default:
1340 {
1341 ComObjPtr<GuestOSType> pGuestOSType;
1342 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1343 pGuestOSType);
1344 if (FAILED(hrc2) || pGuestOSType.isNull())
1345 {
1346 *aParavirtProvider = ParavirtProvider_None;
1347 break;
1348 }
1349
1350 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1351 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1352
1353 switch (mHWData->mParavirtProvider)
1354 {
1355 case ParavirtProvider_Legacy:
1356 {
1357 if (fOsXGuest)
1358 *aParavirtProvider = ParavirtProvider_Minimal;
1359 else
1360 *aParavirtProvider = ParavirtProvider_None;
1361 break;
1362 }
1363
1364 case ParavirtProvider_Default:
1365 {
1366 if (fOsXGuest)
1367 *aParavirtProvider = ParavirtProvider_Minimal;
1368 else if ( mUserData->s.strOsType == "Windows10"
1369 || mUserData->s.strOsType == "Windows10_64"
1370 || mUserData->s.strOsType == "Windows81"
1371 || mUserData->s.strOsType == "Windows81_64"
1372 || mUserData->s.strOsType == "Windows8"
1373 || mUserData->s.strOsType == "Windows8_64"
1374 || mUserData->s.strOsType == "Windows7"
1375 || mUserData->s.strOsType == "Windows7_64"
1376 || mUserData->s.strOsType == "WindowsVista"
1377 || mUserData->s.strOsType == "WindowsVista_64"
1378 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1379 || mUserData->s.strOsType.startsWith("Windows201"))
1380 && mUserData->s.strOsType.endsWith("_64"))
1381 || mUserData->s.strOsType == "Windows2012"
1382 || mUserData->s.strOsType == "Windows2012_64"
1383 || mUserData->s.strOsType == "Windows2008"
1384 || mUserData->s.strOsType == "Windows2008_64")
1385 {
1386 *aParavirtProvider = ParavirtProvider_HyperV;
1387 }
1388 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1389 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1390 || mUserData->s.strOsType == "Linux"
1391 || mUserData->s.strOsType == "Linux_64"
1392 || mUserData->s.strOsType == "ArchLinux"
1393 || mUserData->s.strOsType == "ArchLinux_64"
1394 || mUserData->s.strOsType == "Debian"
1395 || mUserData->s.strOsType == "Debian_64"
1396 || mUserData->s.strOsType == "Fedora"
1397 || mUserData->s.strOsType == "Fedora_64"
1398 || mUserData->s.strOsType == "Gentoo"
1399 || mUserData->s.strOsType == "Gentoo_64"
1400 || mUserData->s.strOsType == "Mandriva"
1401 || mUserData->s.strOsType == "Mandriva_64"
1402 || mUserData->s.strOsType == "OpenSUSE"
1403 || mUserData->s.strOsType == "OpenSUSE_64"
1404 || mUserData->s.strOsType == "Oracle"
1405 || mUserData->s.strOsType == "Oracle_64"
1406 || mUserData->s.strOsType == "RedHat"
1407 || mUserData->s.strOsType == "RedHat_64"
1408 || mUserData->s.strOsType == "Turbolinux"
1409 || mUserData->s.strOsType == "Turbolinux_64"
1410 || mUserData->s.strOsType == "Ubuntu"
1411 || mUserData->s.strOsType == "Ubuntu_64"
1412 || mUserData->s.strOsType == "Xandros"
1413 || mUserData->s.strOsType == "Xandros_64")
1414 {
1415 *aParavirtProvider = ParavirtProvider_KVM;
1416 }
1417 else
1418 *aParavirtProvider = ParavirtProvider_None;
1419 break;
1420 }
1421
1422 default: AssertFailedBreak(); /* Shut up MSC. */
1423 }
1424 break;
1425 }
1426 }
1427
1428 Assert( *aParavirtProvider == ParavirtProvider_None
1429 || *aParavirtProvider == ParavirtProvider_Minimal
1430 || *aParavirtProvider == ParavirtProvider_HyperV
1431 || *aParavirtProvider == ParavirtProvider_KVM);
1432 return S_OK;
1433}
1434
1435HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 aHardwareVersion = mHWData->mHWVersion;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1445{
1446 /* check known version */
1447 Utf8Str hwVersion = aHardwareVersion;
1448 if ( hwVersion.compare("1") != 0
1449 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1450 return setError(E_INVALIDARG,
1451 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1452
1453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 HRESULT rc = i_checkStateDependency(MutableStateDep);
1456 if (FAILED(rc)) return rc;
1457
1458 i_setModified(IsModified_MachineData);
1459 mHWData.backup();
1460 mHWData->mHWVersion = aHardwareVersion;
1461
1462 return S_OK;
1463}
1464
1465HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1466{
1467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1468
1469 if (!mHWData->mHardwareUUID.isZero())
1470 aHardwareUUID = mHWData->mHardwareUUID;
1471 else
1472 aHardwareUUID = mData->mUuid;
1473
1474 return S_OK;
1475}
1476
1477HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1478{
1479 if (!aHardwareUUID.isValid())
1480 return E_INVALIDARG;
1481
1482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1483
1484 HRESULT rc = i_checkStateDependency(MutableStateDep);
1485 if (FAILED(rc)) return rc;
1486
1487 i_setModified(IsModified_MachineData);
1488 mHWData.backup();
1489 if (aHardwareUUID == mData->mUuid)
1490 mHWData->mHardwareUUID.clear();
1491 else
1492 mHWData->mHardwareUUID = aHardwareUUID;
1493
1494 return S_OK;
1495}
1496
1497HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1498{
1499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1500
1501 *aMemorySize = mHWData->mMemorySize;
1502
1503 return S_OK;
1504}
1505
1506HRESULT Machine::setMemorySize(ULONG aMemorySize)
1507{
1508 /* check RAM limits */
1509 if ( aMemorySize < MM_RAM_MIN_IN_MB
1510 || aMemorySize > MM_RAM_MAX_IN_MB
1511 )
1512 return setError(E_INVALIDARG,
1513 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1514 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1515
1516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1517
1518 HRESULT rc = i_checkStateDependency(MutableStateDep);
1519 if (FAILED(rc)) return rc;
1520
1521 i_setModified(IsModified_MachineData);
1522 mHWData.backup();
1523 mHWData->mMemorySize = aMemorySize;
1524
1525 return S_OK;
1526}
1527
1528HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1529{
1530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
1532 *aCPUCount = mHWData->mCPUCount;
1533
1534 return S_OK;
1535}
1536
1537HRESULT Machine::setCPUCount(ULONG aCPUCount)
1538{
1539 /* check CPU limits */
1540 if ( aCPUCount < SchemaDefs::MinCPUCount
1541 || aCPUCount > SchemaDefs::MaxCPUCount
1542 )
1543 return setError(E_INVALIDARG,
1544 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1545 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1546
1547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
1549 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1550 if (mHWData->mCPUHotPlugEnabled)
1551 {
1552 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1553 {
1554 if (mHWData->mCPUAttached[idx])
1555 return setError(E_INVALIDARG,
1556 tr("There is still a CPU attached to socket %lu."
1557 "Detach the CPU before removing the socket"),
1558 aCPUCount, idx+1);
1559 }
1560 }
1561
1562 HRESULT rc = i_checkStateDependency(MutableStateDep);
1563 if (FAILED(rc)) return rc;
1564
1565 i_setModified(IsModified_MachineData);
1566 mHWData.backup();
1567 mHWData->mCPUCount = aCPUCount;
1568
1569 return S_OK;
1570}
1571
1572HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1573{
1574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1575
1576 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1577
1578 return S_OK;
1579}
1580
1581HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1582{
1583 HRESULT rc = S_OK;
1584
1585 /* check throttle limits */
1586 if ( aCPUExecutionCap < 1
1587 || aCPUExecutionCap > 100
1588 )
1589 return setError(E_INVALIDARG,
1590 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1591 aCPUExecutionCap, 1, 100);
1592
1593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1594
1595 alock.release();
1596 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1597 alock.acquire();
1598 if (FAILED(rc)) return rc;
1599
1600 i_setModified(IsModified_MachineData);
1601 mHWData.backup();
1602 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1603
1604 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1605 if (Global::IsOnline(mData->mMachineState))
1606 i_saveSettings(NULL);
1607
1608 return S_OK;
1609}
1610
1611HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1612{
1613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1614
1615 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1616
1617 return S_OK;
1618}
1619
1620HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1621{
1622 HRESULT rc = S_OK;
1623
1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 rc = i_checkStateDependency(MutableStateDep);
1627 if (FAILED(rc)) return rc;
1628
1629 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1630 {
1631 if (aCPUHotPlugEnabled)
1632 {
1633 i_setModified(IsModified_MachineData);
1634 mHWData.backup();
1635
1636 /* Add the amount of CPUs currently attached */
1637 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1638 mHWData->mCPUAttached[i] = true;
1639 }
1640 else
1641 {
1642 /*
1643 * We can disable hotplug only if the amount of maximum CPUs is equal
1644 * to the amount of attached CPUs
1645 */
1646 unsigned cCpusAttached = 0;
1647 unsigned iHighestId = 0;
1648
1649 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1650 {
1651 if (mHWData->mCPUAttached[i])
1652 {
1653 cCpusAttached++;
1654 iHighestId = i;
1655 }
1656 }
1657
1658 if ( (cCpusAttached != mHWData->mCPUCount)
1659 || (iHighestId >= mHWData->mCPUCount))
1660 return setError(E_INVALIDARG,
1661 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1662
1663 i_setModified(IsModified_MachineData);
1664 mHWData.backup();
1665 }
1666 }
1667
1668 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1669
1670 return rc;
1671}
1672
1673HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1674{
1675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1676
1677 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1678
1679 return S_OK;
1680}
1681
1682HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1683{
1684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1685
1686 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1687 if (SUCCEEDED(hrc))
1688 {
1689 i_setModified(IsModified_MachineData);
1690 mHWData.backup();
1691 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1692 }
1693 return hrc;
1694}
1695
1696HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1697{
1698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1699 aCPUProfile = mHWData->mCpuProfile;
1700 return S_OK;
1701}
1702
1703HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1704{
1705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1706 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1707 if (SUCCEEDED(hrc))
1708 {
1709 i_setModified(IsModified_MachineData);
1710 mHWData.backup();
1711 /* Empty equals 'host'. */
1712 if (aCPUProfile.isNotEmpty())
1713 mHWData->mCpuProfile = aCPUProfile;
1714 else
1715 mHWData->mCpuProfile = "host";
1716 }
1717 return hrc;
1718}
1719
1720HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1721{
1722#ifdef VBOX_WITH_USB_CARDREADER
1723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1724
1725 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1726
1727 return S_OK;
1728#else
1729 NOREF(aEmulatedUSBCardReaderEnabled);
1730 return E_NOTIMPL;
1731#endif
1732}
1733
1734HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1735{
1736#ifdef VBOX_WITH_USB_CARDREADER
1737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1740 if (FAILED(rc)) return rc;
1741
1742 i_setModified(IsModified_MachineData);
1743 mHWData.backup();
1744 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1745
1746 return S_OK;
1747#else
1748 NOREF(aEmulatedUSBCardReaderEnabled);
1749 return E_NOTIMPL;
1750#endif
1751}
1752
1753HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1754{
1755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1756
1757 *aHPETEnabled = mHWData->mHPETEnabled;
1758
1759 return S_OK;
1760}
1761
1762HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1763{
1764 HRESULT rc = S_OK;
1765
1766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1767
1768 rc = i_checkStateDependency(MutableStateDep);
1769 if (FAILED(rc)) return rc;
1770
1771 i_setModified(IsModified_MachineData);
1772 mHWData.backup();
1773
1774 mHWData->mHPETEnabled = aHPETEnabled;
1775
1776 return rc;
1777}
1778
1779/** @todo this method should not be public */
1780HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1781{
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1785
1786 return S_OK;
1787}
1788
1789/**
1790 * Set the memory balloon size.
1791 *
1792 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1793 * we have to make sure that we never call IGuest from here.
1794 */
1795HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1796{
1797 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1798#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1799 /* check limits */
1800 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1801 return setError(E_INVALIDARG,
1802 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1803 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1804
1805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1806
1807 i_setModified(IsModified_MachineData);
1808 mHWData.backup();
1809 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1810
1811 return S_OK;
1812#else
1813 NOREF(aMemoryBalloonSize);
1814 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1815#endif
1816}
1817
1818HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1819{
1820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1821
1822 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1823 return S_OK;
1824}
1825
1826HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1827{
1828#ifdef VBOX_WITH_PAGE_SHARING
1829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1830
1831 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1832 i_setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1835 return S_OK;
1836#else
1837 NOREF(aPageFusionEnabled);
1838 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1839#endif
1840}
1841
1842HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1843{
1844 /* mBIOSSettings is constant during life time, no need to lock */
1845 aBIOSSettings = mBIOSSettings;
1846
1847 return S_OK;
1848}
1849
1850HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1851{
1852 /* mTrustedPlatformModule is constant during life time, no need to lock */
1853 aTrustedPlatformModule = mTrustedPlatformModule;
1854
1855 return S_OK;
1856}
1857
1858HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1859{
1860 /* mNvramStore is constant during life time, no need to lock */
1861 aNvramStore = mNvramStore;
1862
1863 return S_OK;
1864}
1865
1866HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1867{
1868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1869
1870 aRecordingSettings = mRecordingSettings;
1871
1872 return S_OK;
1873}
1874
1875HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1876{
1877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1878
1879 aGraphicsAdapter = mGraphicsAdapter;
1880
1881 return S_OK;
1882}
1883
1884HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1885{
1886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1887
1888 switch (aProperty)
1889 {
1890 case CPUPropertyType_PAE:
1891 *aValue = mHWData->mPAEEnabled;
1892 break;
1893
1894 case CPUPropertyType_LongMode:
1895 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1896 *aValue = TRUE;
1897 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1898 *aValue = FALSE;
1899#if HC_ARCH_BITS == 64
1900 else
1901 *aValue = TRUE;
1902#else
1903 else
1904 {
1905 *aValue = FALSE;
1906
1907 ComObjPtr<GuestOSType> pGuestOSType;
1908 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1909 pGuestOSType);
1910 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1911 {
1912 if (pGuestOSType->i_is64Bit())
1913 {
1914 ComObjPtr<Host> pHost = mParent->i_host();
1915 alock.release();
1916
1917 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1918 if (FAILED(hrc2))
1919 *aValue = FALSE;
1920 }
1921 }
1922 }
1923#endif
1924 break;
1925
1926 case CPUPropertyType_TripleFaultReset:
1927 *aValue = mHWData->mTripleFaultReset;
1928 break;
1929
1930 case CPUPropertyType_APIC:
1931 *aValue = mHWData->mAPIC;
1932 break;
1933
1934 case CPUPropertyType_X2APIC:
1935 *aValue = mHWData->mX2APIC;
1936 break;
1937
1938 case CPUPropertyType_IBPBOnVMExit:
1939 *aValue = mHWData->mIBPBOnVMExit;
1940 break;
1941
1942 case CPUPropertyType_IBPBOnVMEntry:
1943 *aValue = mHWData->mIBPBOnVMEntry;
1944 break;
1945
1946 case CPUPropertyType_SpecCtrl:
1947 *aValue = mHWData->mSpecCtrl;
1948 break;
1949
1950 case CPUPropertyType_SpecCtrlByHost:
1951 *aValue = mHWData->mSpecCtrlByHost;
1952 break;
1953
1954 case CPUPropertyType_HWVirt:
1955 *aValue = mHWData->mNestedHWVirt;
1956 break;
1957
1958 case CPUPropertyType_L1DFlushOnEMTScheduling:
1959 *aValue = mHWData->mL1DFlushOnSched;
1960 break;
1961
1962 case CPUPropertyType_L1DFlushOnVMEntry:
1963 *aValue = mHWData->mL1DFlushOnVMEntry;
1964 break;
1965
1966 case CPUPropertyType_MDSClearOnEMTScheduling:
1967 *aValue = mHWData->mMDSClearOnSched;
1968 break;
1969
1970 case CPUPropertyType_MDSClearOnVMEntry:
1971 *aValue = mHWData->mMDSClearOnVMEntry;
1972 break;
1973
1974 default:
1975 return E_INVALIDARG;
1976 }
1977 return S_OK;
1978}
1979
1980HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1981{
1982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 HRESULT rc = i_checkStateDependency(MutableStateDep);
1985 if (FAILED(rc)) return rc;
1986
1987 switch (aProperty)
1988 {
1989 case CPUPropertyType_PAE:
1990 i_setModified(IsModified_MachineData);
1991 mHWData.backup();
1992 mHWData->mPAEEnabled = !!aValue;
1993 break;
1994
1995 case CPUPropertyType_LongMode:
1996 i_setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1999 break;
2000
2001 case CPUPropertyType_TripleFaultReset:
2002 i_setModified(IsModified_MachineData);
2003 mHWData.backup();
2004 mHWData->mTripleFaultReset = !!aValue;
2005 break;
2006
2007 case CPUPropertyType_APIC:
2008 if (mHWData->mX2APIC)
2009 aValue = TRUE;
2010 i_setModified(IsModified_MachineData);
2011 mHWData.backup();
2012 mHWData->mAPIC = !!aValue;
2013 break;
2014
2015 case CPUPropertyType_X2APIC:
2016 i_setModified(IsModified_MachineData);
2017 mHWData.backup();
2018 mHWData->mX2APIC = !!aValue;
2019 if (aValue)
2020 mHWData->mAPIC = !!aValue;
2021 break;
2022
2023 case CPUPropertyType_IBPBOnVMExit:
2024 i_setModified(IsModified_MachineData);
2025 mHWData.backup();
2026 mHWData->mIBPBOnVMExit = !!aValue;
2027 break;
2028
2029 case CPUPropertyType_IBPBOnVMEntry:
2030 i_setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mIBPBOnVMEntry = !!aValue;
2033 break;
2034
2035 case CPUPropertyType_SpecCtrl:
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mSpecCtrl = !!aValue;
2039 break;
2040
2041 case CPUPropertyType_SpecCtrlByHost:
2042 i_setModified(IsModified_MachineData);
2043 mHWData.backup();
2044 mHWData->mSpecCtrlByHost = !!aValue;
2045 break;
2046
2047 case CPUPropertyType_HWVirt:
2048 i_setModified(IsModified_MachineData);
2049 mHWData.backup();
2050 mHWData->mNestedHWVirt = !!aValue;
2051 break;
2052
2053 case CPUPropertyType_L1DFlushOnEMTScheduling:
2054 i_setModified(IsModified_MachineData);
2055 mHWData.backup();
2056 mHWData->mL1DFlushOnSched = !!aValue;
2057 break;
2058
2059 case CPUPropertyType_L1DFlushOnVMEntry:
2060 i_setModified(IsModified_MachineData);
2061 mHWData.backup();
2062 mHWData->mL1DFlushOnVMEntry = !!aValue;
2063 break;
2064
2065 case CPUPropertyType_MDSClearOnEMTScheduling:
2066 i_setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mMDSClearOnSched = !!aValue;
2069 break;
2070
2071 case CPUPropertyType_MDSClearOnVMEntry:
2072 i_setModified(IsModified_MachineData);
2073 mHWData.backup();
2074 mHWData->mMDSClearOnVMEntry = !!aValue;
2075 break;
2076
2077 default:
2078 return E_INVALIDARG;
2079 }
2080 return S_OK;
2081}
2082
2083HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2084 ULONG *aValEcx, ULONG *aValEdx)
2085{
2086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2087 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2088 {
2089 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2090 it != mHWData->mCpuIdLeafList.end();
2091 ++it)
2092 {
2093 if (aOrdinal == 0)
2094 {
2095 const settings::CpuIdLeaf &rLeaf= *it;
2096 *aIdx = rLeaf.idx;
2097 *aSubIdx = rLeaf.idxSub;
2098 *aValEax = rLeaf.uEax;
2099 *aValEbx = rLeaf.uEbx;
2100 *aValEcx = rLeaf.uEcx;
2101 *aValEdx = rLeaf.uEdx;
2102 return S_OK;
2103 }
2104 aOrdinal--;
2105 }
2106 }
2107 return E_INVALIDARG;
2108}
2109
2110HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2111{
2112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2113
2114 /*
2115 * Search the list.
2116 */
2117 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2118 {
2119 const settings::CpuIdLeaf &rLeaf= *it;
2120 if ( rLeaf.idx == aIdx
2121 && ( aSubIdx == UINT32_MAX
2122 || rLeaf.idxSub == aSubIdx) )
2123 {
2124 *aValEax = rLeaf.uEax;
2125 *aValEbx = rLeaf.uEbx;
2126 *aValEcx = rLeaf.uEcx;
2127 *aValEdx = rLeaf.uEdx;
2128 return S_OK;
2129 }
2130 }
2131
2132 return E_INVALIDARG;
2133}
2134
2135
2136HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2137{
2138 /*
2139 * Validate input before taking locks and checking state.
2140 */
2141 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2142 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2143 if ( aIdx >= UINT32_C(0x20)
2144 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2145 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2146 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2147
2148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2149 HRESULT rc = i_checkStateDependency(MutableStateDep);
2150 if (FAILED(rc)) return rc;
2151
2152 /*
2153 * Impose a maximum number of leaves.
2154 */
2155 if (mHWData->mCpuIdLeafList.size() > 256)
2156 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2157
2158 /*
2159 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2160 */
2161 i_setModified(IsModified_MachineData);
2162 mHWData.backup();
2163
2164 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2165 {
2166 settings::CpuIdLeaf &rLeaf= *it;
2167 if ( rLeaf.idx == aIdx
2168 && ( aSubIdx == UINT32_MAX
2169 || rLeaf.idxSub == aSubIdx) )
2170 it = mHWData->mCpuIdLeafList.erase(it);
2171 else
2172 ++it;
2173 }
2174
2175 settings::CpuIdLeaf NewLeaf;
2176 NewLeaf.idx = aIdx;
2177 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2178 NewLeaf.uEax = aValEax;
2179 NewLeaf.uEbx = aValEbx;
2180 NewLeaf.uEcx = aValEcx;
2181 NewLeaf.uEdx = aValEdx;
2182 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2183 return S_OK;
2184}
2185
2186HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2187{
2188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2189
2190 HRESULT rc = i_checkStateDependency(MutableStateDep);
2191 if (FAILED(rc)) return rc;
2192
2193 /*
2194 * Do the removal.
2195 */
2196 bool fModified = mHWData.isBackedUp();
2197 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2198 {
2199 settings::CpuIdLeaf &rLeaf= *it;
2200 if ( rLeaf.idx == aIdx
2201 && ( aSubIdx == UINT32_MAX
2202 || rLeaf.idxSub == aSubIdx) )
2203 {
2204 if (!fModified)
2205 {
2206 fModified = true;
2207 i_setModified(IsModified_MachineData);
2208 mHWData.backup();
2209 // Start from the beginning, since mHWData.backup() creates
2210 // a new list, causing iterator mixup. This makes sure that
2211 // the settings are not unnecessarily marked as modified,
2212 // at the price of extra list walking.
2213 it = mHWData->mCpuIdLeafList.begin();
2214 }
2215 else
2216 it = mHWData->mCpuIdLeafList.erase(it);
2217 }
2218 else
2219 ++it;
2220 }
2221
2222 return S_OK;
2223}
2224
2225HRESULT Machine::removeAllCPUIDLeaves()
2226{
2227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2228
2229 HRESULT rc = i_checkStateDependency(MutableStateDep);
2230 if (FAILED(rc)) return rc;
2231
2232 if (mHWData->mCpuIdLeafList.size() > 0)
2233 {
2234 i_setModified(IsModified_MachineData);
2235 mHWData.backup();
2236
2237 mHWData->mCpuIdLeafList.clear();
2238 }
2239
2240 return S_OK;
2241}
2242HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2243{
2244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2245
2246 switch(aProperty)
2247 {
2248 case HWVirtExPropertyType_Enabled:
2249 *aValue = mHWData->mHWVirtExEnabled;
2250 break;
2251
2252 case HWVirtExPropertyType_VPID:
2253 *aValue = mHWData->mHWVirtExVPIDEnabled;
2254 break;
2255
2256 case HWVirtExPropertyType_NestedPaging:
2257 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2258 break;
2259
2260 case HWVirtExPropertyType_UnrestrictedExecution:
2261 *aValue = mHWData->mHWVirtExUXEnabled;
2262 break;
2263
2264 case HWVirtExPropertyType_LargePages:
2265 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2266#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2267 *aValue = FALSE;
2268#endif
2269 break;
2270
2271 case HWVirtExPropertyType_Force:
2272 *aValue = mHWData->mHWVirtExForceEnabled;
2273 break;
2274
2275 case HWVirtExPropertyType_UseNativeApi:
2276 *aValue = mHWData->mHWVirtExUseNativeApi;
2277 break;
2278
2279 case HWVirtExPropertyType_VirtVmsaveVmload:
2280 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2281 break;
2282
2283 default:
2284 return E_INVALIDARG;
2285 }
2286 return S_OK;
2287}
2288
2289HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2290{
2291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2292
2293 HRESULT rc = i_checkStateDependency(MutableStateDep);
2294 if (FAILED(rc)) return rc;
2295
2296 switch (aProperty)
2297 {
2298 case HWVirtExPropertyType_Enabled:
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301 mHWData->mHWVirtExEnabled = !!aValue;
2302 break;
2303
2304 case HWVirtExPropertyType_VPID:
2305 i_setModified(IsModified_MachineData);
2306 mHWData.backup();
2307 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2308 break;
2309
2310 case HWVirtExPropertyType_NestedPaging:
2311 i_setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2314 break;
2315
2316 case HWVirtExPropertyType_UnrestrictedExecution:
2317 i_setModified(IsModified_MachineData);
2318 mHWData.backup();
2319 mHWData->mHWVirtExUXEnabled = !!aValue;
2320 break;
2321
2322 case HWVirtExPropertyType_LargePages:
2323 i_setModified(IsModified_MachineData);
2324 mHWData.backup();
2325 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2326 break;
2327
2328 case HWVirtExPropertyType_Force:
2329 i_setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 mHWData->mHWVirtExForceEnabled = !!aValue;
2332 break;
2333
2334 case HWVirtExPropertyType_UseNativeApi:
2335 i_setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mHWVirtExUseNativeApi = !!aValue;
2338 break;
2339
2340 case HWVirtExPropertyType_VirtVmsaveVmload:
2341 i_setModified(IsModified_MachineData);
2342 mHWData.backup();
2343 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2344 break;
2345
2346 default:
2347 return E_INVALIDARG;
2348 }
2349
2350 return S_OK;
2351}
2352
2353HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2354{
2355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2356
2357 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2358
2359 return S_OK;
2360}
2361
2362HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2363{
2364 /** @todo (r=dmik):
2365 * 1. Allow to change the name of the snapshot folder containing snapshots
2366 * 2. Rename the folder on disk instead of just changing the property
2367 * value (to be smart and not to leave garbage). Note that it cannot be
2368 * done here because the change may be rolled back. Thus, the right
2369 * place is #saveSettings().
2370 */
2371
2372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2373
2374 HRESULT rc = i_checkStateDependency(MutableStateDep);
2375 if (FAILED(rc)) return rc;
2376
2377 if (!mData->mCurrentSnapshot.isNull())
2378 return setError(E_FAIL,
2379 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2380
2381 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2382
2383 if (strSnapshotFolder.isEmpty())
2384 strSnapshotFolder = "Snapshots";
2385 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2386 if (RT_FAILURE(vrc))
2387 return setErrorBoth(E_FAIL, vrc,
2388 tr("Invalid snapshot folder '%s' (%Rrc)"),
2389 strSnapshotFolder.c_str(), vrc);
2390
2391 i_setModified(IsModified_MachineData);
2392 mUserData.backup();
2393
2394 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2395
2396 return S_OK;
2397}
2398
2399HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2400{
2401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2402
2403 aMediumAttachments.resize(mMediumAttachments->size());
2404 size_t i = 0;
2405 for (MediumAttachmentList::const_iterator
2406 it = mMediumAttachments->begin();
2407 it != mMediumAttachments->end();
2408 ++it, ++i)
2409 aMediumAttachments[i] = *it;
2410
2411 return S_OK;
2412}
2413
2414HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2415{
2416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2417
2418 Assert(!!mVRDEServer);
2419
2420 aVRDEServer = mVRDEServer;
2421
2422 return S_OK;
2423}
2424
2425HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2426{
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 aAudioAdapter = mAudioAdapter;
2430
2431 return S_OK;
2432}
2433
2434HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2435{
2436#ifdef VBOX_WITH_VUSB
2437 clearError();
2438 MultiResult rc(S_OK);
2439
2440# ifdef VBOX_WITH_USB
2441 rc = mParent->i_host()->i_checkUSBProxyService();
2442 if (FAILED(rc)) return rc;
2443# endif
2444
2445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2446
2447 aUSBControllers.resize(mUSBControllers->size());
2448 size_t i = 0;
2449 for (USBControllerList::const_iterator
2450 it = mUSBControllers->begin();
2451 it != mUSBControllers->end();
2452 ++it, ++i)
2453 aUSBControllers[i] = *it;
2454
2455 return S_OK;
2456#else
2457 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2458 * extended error info to indicate that USB is simply not available
2459 * (w/o treating it as a failure), for example, as in OSE */
2460 NOREF(aUSBControllers);
2461 ReturnComNotImplemented();
2462#endif /* VBOX_WITH_VUSB */
2463}
2464
2465HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2466{
2467#ifdef VBOX_WITH_VUSB
2468 clearError();
2469 MultiResult rc(S_OK);
2470
2471# ifdef VBOX_WITH_USB
2472 rc = mParent->i_host()->i_checkUSBProxyService();
2473 if (FAILED(rc)) return rc;
2474# endif
2475
2476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 aUSBDeviceFilters = mUSBDeviceFilters;
2479 return rc;
2480#else
2481 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2482 * extended error info to indicate that USB is simply not available
2483 * (w/o treating it as a failure), for example, as in OSE */
2484 NOREF(aUSBDeviceFilters);
2485 ReturnComNotImplemented();
2486#endif /* VBOX_WITH_VUSB */
2487}
2488
2489HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2490{
2491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2492
2493 aSettingsFilePath = mData->m_strConfigFileFull;
2494
2495 return S_OK;
2496}
2497
2498HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2499{
2500 RT_NOREF(aSettingsFilePath);
2501 ReturnComNotImplemented();
2502}
2503
2504HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2505{
2506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2509 if (FAILED(rc)) return rc;
2510
2511 if (!mData->pMachineConfigFile->fileExists())
2512 // this is a new machine, and no config file exists yet:
2513 *aSettingsModified = TRUE;
2514 else
2515 *aSettingsModified = (mData->flModifications != 0);
2516
2517 return S_OK;
2518}
2519
2520HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2521{
2522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2523
2524 *aSessionState = mData->mSession.mState;
2525
2526 return S_OK;
2527}
2528
2529HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2530{
2531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2532
2533 aSessionName = mData->mSession.mName;
2534
2535 return S_OK;
2536}
2537
2538HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2539{
2540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 *aSessionPID = mData->mSession.mPID;
2543
2544 return S_OK;
2545}
2546
2547HRESULT Machine::getState(MachineState_T *aState)
2548{
2549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2550
2551 *aState = mData->mMachineState;
2552 Assert(mData->mMachineState != MachineState_Null);
2553
2554 return S_OK;
2555}
2556
2557HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2558{
2559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2560
2561 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2562
2563 return S_OK;
2564}
2565
2566HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2567{
2568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2569
2570 aStateFilePath = mSSData->strStateFilePath;
2571
2572 return S_OK;
2573}
2574
2575HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2576{
2577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2578
2579 i_getLogFolder(aLogFolder);
2580
2581 return S_OK;
2582}
2583
2584HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2585{
2586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 aCurrentSnapshot = mData->mCurrentSnapshot;
2589
2590 return S_OK;
2591}
2592
2593HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2594{
2595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2598 ? 0
2599 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2600
2601 return S_OK;
2602}
2603
2604HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2605{
2606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2607
2608 /* Note: for machines with no snapshots, we always return FALSE
2609 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2610 * reasons :) */
2611
2612 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2613 ? FALSE
2614 : mData->mCurrentStateModified;
2615
2616 return S_OK;
2617}
2618
2619HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2620{
2621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2622
2623 aSharedFolders.resize(mHWData->mSharedFolders.size());
2624 size_t i = 0;
2625 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2626 it = mHWData->mSharedFolders.begin();
2627 it != mHWData->mSharedFolders.end();
2628 ++it, ++i)
2629 aSharedFolders[i] = *it;
2630
2631 return S_OK;
2632}
2633
2634HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2635{
2636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2637
2638 *aClipboardMode = mHWData->mClipboardMode;
2639
2640 return S_OK;
2641}
2642
2643HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2644{
2645 HRESULT rc = S_OK;
2646
2647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2648
2649 alock.release();
2650 rc = i_onClipboardModeChange(aClipboardMode);
2651 alock.acquire();
2652 if (FAILED(rc)) return rc;
2653
2654 i_setModified(IsModified_MachineData);
2655 mHWData.backup();
2656 mHWData->mClipboardMode = aClipboardMode;
2657
2658 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2659 if (Global::IsOnline(mData->mMachineState))
2660 i_saveSettings(NULL);
2661
2662 return S_OK;
2663}
2664
2665HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2666{
2667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2668
2669 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2675{
2676 HRESULT rc = S_OK;
2677
2678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2679
2680 alock.release();
2681 rc = i_onClipboardFileTransferModeChange(aEnabled);
2682 alock.acquire();
2683 if (FAILED(rc)) return rc;
2684
2685 i_setModified(IsModified_MachineData);
2686 mHWData.backup();
2687 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2688
2689 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2690 if (Global::IsOnline(mData->mMachineState))
2691 i_saveSettings(NULL);
2692
2693 return S_OK;
2694}
2695
2696HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2697{
2698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 *aDnDMode = mHWData->mDnDMode;
2701
2702 return S_OK;
2703}
2704
2705HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2706{
2707 HRESULT rc = S_OK;
2708
2709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2710
2711 alock.release();
2712 rc = i_onDnDModeChange(aDnDMode);
2713
2714 alock.acquire();
2715 if (FAILED(rc)) return rc;
2716
2717 i_setModified(IsModified_MachineData);
2718 mHWData.backup();
2719 mHWData->mDnDMode = aDnDMode;
2720
2721 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2722 if (Global::IsOnline(mData->mMachineState))
2723 i_saveSettings(NULL);
2724
2725 return S_OK;
2726}
2727
2728HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aStorageControllers.resize(mStorageControllers->size());
2733 size_t i = 0;
2734 for (StorageControllerList::const_iterator
2735 it = mStorageControllers->begin();
2736 it != mStorageControllers->end();
2737 ++it, ++i)
2738 aStorageControllers[i] = *it;
2739
2740 return S_OK;
2741}
2742
2743HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2744{
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 *aEnabled = mUserData->s.fTeleporterEnabled;
2748
2749 return S_OK;
2750}
2751
2752HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2753{
2754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2755
2756 /* Only allow it to be set to true when PoweredOff or Aborted.
2757 (Clearing it is always permitted.) */
2758 if ( aTeleporterEnabled
2759 && mData->mRegistered
2760 && ( !i_isSessionMachine()
2761 || ( mData->mMachineState != MachineState_PoweredOff
2762 && mData->mMachineState != MachineState_Teleported
2763 && mData->mMachineState != MachineState_Aborted
2764 )
2765 )
2766 )
2767 return setError(VBOX_E_INVALID_VM_STATE,
2768 tr("The machine is not powered off (state is %s)"),
2769 Global::stringifyMachineState(mData->mMachineState));
2770
2771 i_setModified(IsModified_MachineData);
2772 mUserData.backup();
2773 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2774
2775 return S_OK;
2776}
2777
2778HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2779{
2780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2781
2782 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2788{
2789 if (aTeleporterPort >= _64K)
2790 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2791
2792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2793
2794 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2795 if (FAILED(rc)) return rc;
2796
2797 i_setModified(IsModified_MachineData);
2798 mUserData.backup();
2799 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2800
2801 return S_OK;
2802}
2803
2804HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2805{
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2809
2810 return S_OK;
2811}
2812
2813HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2814{
2815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2818 if (FAILED(rc)) return rc;
2819
2820 i_setModified(IsModified_MachineData);
2821 mUserData.backup();
2822 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2828{
2829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2830 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2831
2832 return S_OK;
2833}
2834
2835HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2836{
2837 /*
2838 * Hash the password first.
2839 */
2840 com::Utf8Str aT = aTeleporterPassword;
2841
2842 if (!aT.isEmpty())
2843 {
2844 if (VBoxIsPasswordHashed(&aT))
2845 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2846 VBoxHashPassword(&aT);
2847 }
2848
2849 /*
2850 * Do the update.
2851 */
2852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2853 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2854 if (SUCCEEDED(hrc))
2855 {
2856 i_setModified(IsModified_MachineData);
2857 mUserData.backup();
2858 mUserData->s.strTeleporterPassword = aT;
2859 }
2860
2861 return hrc;
2862}
2863
2864HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2865{
2866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2867
2868 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2869
2870 return S_OK;
2871}
2872
2873HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2874{
2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 /* Only allow it to be set to true when PoweredOff or Aborted.
2878 (Clearing it is always permitted.) */
2879 if ( aRTCUseUTC
2880 && mData->mRegistered
2881 && ( !i_isSessionMachine()
2882 || ( mData->mMachineState != MachineState_PoweredOff
2883 && mData->mMachineState != MachineState_Teleported
2884 && mData->mMachineState != MachineState_Aborted
2885 )
2886 )
2887 )
2888 return setError(VBOX_E_INVALID_VM_STATE,
2889 tr("The machine is not powered off (state is %s)"),
2890 Global::stringifyMachineState(mData->mMachineState));
2891
2892 i_setModified(IsModified_MachineData);
2893 mUserData.backup();
2894 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2900{
2901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2904
2905 return S_OK;
2906}
2907
2908HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2909{
2910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 HRESULT rc = i_checkStateDependency(MutableStateDep);
2913 if (FAILED(rc)) return rc;
2914
2915 i_setModified(IsModified_MachineData);
2916 mHWData.backup();
2917 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2923{
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 *aIOCacheSize = mHWData->mIOCacheSize;
2927
2928 return S_OK;
2929}
2930
2931HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2932{
2933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2934
2935 HRESULT rc = i_checkStateDependency(MutableStateDep);
2936 if (FAILED(rc)) return rc;
2937
2938 i_setModified(IsModified_MachineData);
2939 mHWData.backup();
2940 mHWData->mIOCacheSize = aIOCacheSize;
2941
2942 return S_OK;
2943}
2944
2945
2946/**
2947 * @note Locks objects!
2948 */
2949HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2950 LockType_T aLockType)
2951{
2952 /* check the session state */
2953 SessionState_T state;
2954 HRESULT rc = aSession->COMGETTER(State)(&state);
2955 if (FAILED(rc)) return rc;
2956
2957 if (state != SessionState_Unlocked)
2958 return setError(VBOX_E_INVALID_OBJECT_STATE,
2959 tr("The given session is busy"));
2960
2961 // get the client's IInternalSessionControl interface
2962 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2963 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2964 E_INVALIDARG);
2965
2966 // session name (only used in some code paths)
2967 Utf8Str strSessionName;
2968
2969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2970
2971 if (!mData->mRegistered)
2972 return setError(E_UNEXPECTED,
2973 tr("The machine '%s' is not registered"),
2974 mUserData->s.strName.c_str());
2975
2976 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2977
2978 SessionState_T oldState = mData->mSession.mState;
2979 /* Hack: in case the session is closing and there is a progress object
2980 * which allows waiting for the session to be closed, take the opportunity
2981 * and do a limited wait (max. 1 second). This helps a lot when the system
2982 * is busy and thus session closing can take a little while. */
2983 if ( mData->mSession.mState == SessionState_Unlocking
2984 && mData->mSession.mProgress)
2985 {
2986 alock.release();
2987 mData->mSession.mProgress->WaitForCompletion(1000);
2988 alock.acquire();
2989 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2990 }
2991
2992 // try again now
2993 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2994 // (i.e. session machine exists)
2995 && (aLockType == LockType_Shared) // caller wants a shared link to the
2996 // existing session that holds the write lock:
2997 )
2998 {
2999 // OK, share the session... we are now dealing with three processes:
3000 // 1) VBoxSVC (where this code runs);
3001 // 2) process C: the caller's client process (who wants a shared session);
3002 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3003
3004 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3005 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3006 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3007 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3008 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3009
3010 /*
3011 * Release the lock before calling the client process. It's safe here
3012 * since the only thing to do after we get the lock again is to add
3013 * the remote control to the list (which doesn't directly influence
3014 * anything).
3015 */
3016 alock.release();
3017
3018 // get the console of the session holding the write lock (this is a remote call)
3019 ComPtr<IConsole> pConsoleW;
3020 if (mData->mSession.mLockType == LockType_VM)
3021 {
3022 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3023 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3024 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3025 if (FAILED(rc))
3026 // the failure may occur w/o any error info (from RPC), so provide one
3027 return setError(VBOX_E_VM_ERROR,
3028 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3029 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3030 }
3031
3032 // share the session machine and W's console with the caller's session
3033 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3034 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3035 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3036
3037 if (FAILED(rc))
3038 // the failure may occur w/o any error info (from RPC), so provide one
3039 return setError(VBOX_E_VM_ERROR,
3040 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3041 alock.acquire();
3042
3043 // need to revalidate the state after acquiring the lock again
3044 if (mData->mSession.mState != SessionState_Locked)
3045 {
3046 pSessionControl->Uninitialize();
3047 return setError(VBOX_E_INVALID_SESSION_STATE,
3048 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3049 mUserData->s.strName.c_str());
3050 }
3051
3052 // add the caller's session to the list
3053 mData->mSession.mRemoteControls.push_back(pSessionControl);
3054 }
3055 else if ( mData->mSession.mState == SessionState_Locked
3056 || mData->mSession.mState == SessionState_Unlocking
3057 )
3058 {
3059 // sharing not permitted, or machine still unlocking:
3060 return setError(VBOX_E_INVALID_OBJECT_STATE,
3061 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3062 mUserData->s.strName.c_str());
3063 }
3064 else
3065 {
3066 // machine is not locked: then write-lock the machine (create the session machine)
3067
3068 // must not be busy
3069 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3070
3071 // get the caller's session PID
3072 RTPROCESS pid = NIL_RTPROCESS;
3073 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3074 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3075 Assert(pid != NIL_RTPROCESS);
3076
3077 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3078
3079 if (fLaunchingVMProcess)
3080 {
3081 if (mData->mSession.mPID == NIL_RTPROCESS)
3082 {
3083 // two or more clients racing for a lock, the one which set the
3084 // session state to Spawning will win, the others will get an
3085 // error as we can't decide here if waiting a little would help
3086 // (only for shared locks this would avoid an error)
3087 return setError(VBOX_E_INVALID_OBJECT_STATE,
3088 tr("The machine '%s' already has a lock request pending"),
3089 mUserData->s.strName.c_str());
3090 }
3091
3092 // this machine is awaiting for a spawning session to be opened:
3093 // then the calling process must be the one that got started by
3094 // LaunchVMProcess()
3095
3096 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3097 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3098
3099#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3100 /* Hardened windows builds spawns three processes when a VM is
3101 launched, the 3rd one is the one that will end up here. */
3102 RTPROCESS pidParent;
3103 int vrc = RTProcQueryParent(pid, &pidParent);
3104 if (RT_SUCCESS(vrc))
3105 vrc = RTProcQueryParent(pidParent, &pidParent);
3106 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3107 || vrc == VERR_ACCESS_DENIED)
3108 {
3109 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3110 mData->mSession.mPID = pid;
3111 }
3112#endif
3113
3114 if (mData->mSession.mPID != pid)
3115 return setError(E_ACCESSDENIED,
3116 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3117 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3118 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3119 }
3120
3121 // create the mutable SessionMachine from the current machine
3122 ComObjPtr<SessionMachine> sessionMachine;
3123 sessionMachine.createObject();
3124 rc = sessionMachine->init(this);
3125 AssertComRC(rc);
3126
3127 /* NOTE: doing return from this function after this point but
3128 * before the end is forbidden since it may call SessionMachine::uninit()
3129 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3130 * lock while still holding the Machine lock in alock so that a deadlock
3131 * is possible due to the wrong lock order. */
3132
3133 if (SUCCEEDED(rc))
3134 {
3135 /*
3136 * Set the session state to Spawning to protect against subsequent
3137 * attempts to open a session and to unregister the machine after
3138 * we release the lock.
3139 */
3140 SessionState_T origState = mData->mSession.mState;
3141 mData->mSession.mState = SessionState_Spawning;
3142
3143#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3144 /* Get the client token ID to be passed to the client process */
3145 Utf8Str strTokenId;
3146 sessionMachine->i_getTokenId(strTokenId);
3147 Assert(!strTokenId.isEmpty());
3148#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3149 /* Get the client token to be passed to the client process */
3150 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3151 /* The token is now "owned" by pToken, fix refcount */
3152 if (!pToken.isNull())
3153 pToken->Release();
3154#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3155
3156 /*
3157 * Release the lock before calling the client process -- it will call
3158 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3159 * because the state is Spawning, so that LaunchVMProcess() and
3160 * LockMachine() calls will fail. This method, called before we
3161 * acquire the lock again, will fail because of the wrong PID.
3162 *
3163 * Note that mData->mSession.mRemoteControls accessed outside
3164 * the lock may not be modified when state is Spawning, so it's safe.
3165 */
3166 alock.release();
3167
3168 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3169#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3170 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3171#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3172 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3173 /* Now the token is owned by the client process. */
3174 pToken.setNull();
3175#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3176 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3177
3178 /* The failure may occur w/o any error info (from RPC), so provide one */
3179 if (FAILED(rc))
3180 setError(VBOX_E_VM_ERROR,
3181 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3182
3183 // get session name, either to remember or to compare against
3184 // the already known session name.
3185 {
3186 Bstr bstrSessionName;
3187 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3188 if (SUCCEEDED(rc2))
3189 strSessionName = bstrSessionName;
3190 }
3191
3192 if ( SUCCEEDED(rc)
3193 && fLaunchingVMProcess
3194 )
3195 {
3196 /* complete the remote session initialization */
3197
3198 /* get the console from the direct session */
3199 ComPtr<IConsole> console;
3200 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3201 ComAssertComRC(rc);
3202
3203 if (SUCCEEDED(rc) && !console)
3204 {
3205 ComAssert(!!console);
3206 rc = E_FAIL;
3207 }
3208
3209 /* assign machine & console to the remote session */
3210 if (SUCCEEDED(rc))
3211 {
3212 /*
3213 * after LaunchVMProcess(), the first and the only
3214 * entry in remoteControls is that remote session
3215 */
3216 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3217 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3218 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3219
3220 /* The failure may occur w/o any error info (from RPC), so provide one */
3221 if (FAILED(rc))
3222 setError(VBOX_E_VM_ERROR,
3223 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3224 }
3225
3226 if (FAILED(rc))
3227 pSessionControl->Uninitialize();
3228 }
3229
3230 /* acquire the lock again */
3231 alock.acquire();
3232
3233 /* Restore the session state */
3234 mData->mSession.mState = origState;
3235 }
3236
3237 // finalize spawning anyway (this is why we don't return on errors above)
3238 if (fLaunchingVMProcess)
3239 {
3240 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3241 /* Note that the progress object is finalized later */
3242 /** @todo Consider checking mData->mSession.mProgress for cancellation
3243 * around here. */
3244
3245 /* We don't reset mSession.mPID here because it is necessary for
3246 * SessionMachine::uninit() to reap the child process later. */
3247
3248 if (FAILED(rc))
3249 {
3250 /* Close the remote session, remove the remote control from the list
3251 * and reset session state to Closed (@note keep the code in sync
3252 * with the relevant part in checkForSpawnFailure()). */
3253
3254 Assert(mData->mSession.mRemoteControls.size() == 1);
3255 if (mData->mSession.mRemoteControls.size() == 1)
3256 {
3257 ErrorInfoKeeper eik;
3258 mData->mSession.mRemoteControls.front()->Uninitialize();
3259 }
3260
3261 mData->mSession.mRemoteControls.clear();
3262 mData->mSession.mState = SessionState_Unlocked;
3263 }
3264 }
3265 else
3266 {
3267 /* memorize PID of the directly opened session */
3268 if (SUCCEEDED(rc))
3269 mData->mSession.mPID = pid;
3270 }
3271
3272 if (SUCCEEDED(rc))
3273 {
3274 mData->mSession.mLockType = aLockType;
3275 /* memorize the direct session control and cache IUnknown for it */
3276 mData->mSession.mDirectControl = pSessionControl;
3277 mData->mSession.mState = SessionState_Locked;
3278 if (!fLaunchingVMProcess)
3279 mData->mSession.mName = strSessionName;
3280 /* associate the SessionMachine with this Machine */
3281 mData->mSession.mMachine = sessionMachine;
3282
3283 /* request an IUnknown pointer early from the remote party for later
3284 * identity checks (it will be internally cached within mDirectControl
3285 * at least on XPCOM) */
3286 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3287 NOREF(unk);
3288 }
3289
3290 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3291 * would break the lock order */
3292 alock.release();
3293
3294 /* uninitialize the created session machine on failure */
3295 if (FAILED(rc))
3296 sessionMachine->uninit();
3297 }
3298
3299 if (SUCCEEDED(rc))
3300 {
3301 /*
3302 * tell the client watcher thread to update the set of
3303 * machines that have open sessions
3304 */
3305 mParent->i_updateClientWatcher();
3306
3307 if (oldState != SessionState_Locked)
3308 /* fire an event */
3309 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3310 }
3311
3312 return rc;
3313}
3314
3315/**
3316 * @note Locks objects!
3317 */
3318HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3319 const com::Utf8Str &aName,
3320 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3321 ComPtr<IProgress> &aProgress)
3322{
3323 Utf8Str strFrontend(aName);
3324 /* "emergencystop" doesn't need the session, so skip the checks/interface
3325 * retrieval. This code doesn't quite fit in here, but introducing a
3326 * special API method would be even more effort, and would require explicit
3327 * support by every API client. It's better to hide the feature a bit. */
3328 if (strFrontend != "emergencystop")
3329 CheckComArgNotNull(aSession);
3330
3331 HRESULT rc = S_OK;
3332 if (strFrontend.isEmpty())
3333 {
3334 Bstr bstrFrontend;
3335 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3336 if (FAILED(rc))
3337 return rc;
3338 strFrontend = bstrFrontend;
3339 if (strFrontend.isEmpty())
3340 {
3341 ComPtr<ISystemProperties> systemProperties;
3342 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3343 if (FAILED(rc))
3344 return rc;
3345 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3346 if (FAILED(rc))
3347 return rc;
3348 strFrontend = bstrFrontend;
3349 }
3350 /* paranoia - emergencystop is not a valid default */
3351 if (strFrontend == "emergencystop")
3352 strFrontend = Utf8Str::Empty;
3353 }
3354 /* default frontend: Qt GUI */
3355 if (strFrontend.isEmpty())
3356 strFrontend = "GUI/Qt";
3357
3358 if (strFrontend != "emergencystop")
3359 {
3360 /* check the session state */
3361 SessionState_T state;
3362 rc = aSession->COMGETTER(State)(&state);
3363 if (FAILED(rc))
3364 return rc;
3365
3366 if (state != SessionState_Unlocked)
3367 return setError(VBOX_E_INVALID_OBJECT_STATE,
3368 tr("The given session is busy"));
3369
3370 /* get the IInternalSessionControl interface */
3371 ComPtr<IInternalSessionControl> control(aSession);
3372 ComAssertMsgRet(!control.isNull(),
3373 ("No IInternalSessionControl interface"),
3374 E_INVALIDARG);
3375
3376 /* get the teleporter enable state for the progress object init. */
3377 BOOL fTeleporterEnabled;
3378 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3379 if (FAILED(rc))
3380 return rc;
3381
3382 /* create a progress object */
3383 ComObjPtr<ProgressProxy> progress;
3384 progress.createObject();
3385 rc = progress->init(mParent,
3386 static_cast<IMachine*>(this),
3387 Bstr(tr("Starting VM")).raw(),
3388 TRUE /* aCancelable */,
3389 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3390 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3391 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3392 2 /* uFirstOperationWeight */,
3393 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3394
3395 if (SUCCEEDED(rc))
3396 {
3397 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3398 if (SUCCEEDED(rc))
3399 {
3400 aProgress = progress;
3401
3402 /* signal the client watcher thread */
3403 mParent->i_updateClientWatcher();
3404
3405 /* fire an event */
3406 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3407 }
3408 }
3409 }
3410 else
3411 {
3412 /* no progress object - either instant success or failure */
3413 aProgress = NULL;
3414
3415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3416
3417 if (mData->mSession.mState != SessionState_Locked)
3418 return setError(VBOX_E_INVALID_OBJECT_STATE,
3419 tr("The machine '%s' is not locked by a session"),
3420 mUserData->s.strName.c_str());
3421
3422 /* must have a VM process associated - do not kill normal API clients
3423 * with an open session */
3424 if (!Global::IsOnline(mData->mMachineState))
3425 return setError(VBOX_E_INVALID_OBJECT_STATE,
3426 tr("The machine '%s' does not have a VM process"),
3427 mUserData->s.strName.c_str());
3428
3429 /* forcibly terminate the VM process */
3430 if (mData->mSession.mPID != NIL_RTPROCESS)
3431 RTProcTerminate(mData->mSession.mPID);
3432
3433 /* signal the client watcher thread, as most likely the client has
3434 * been terminated */
3435 mParent->i_updateClientWatcher();
3436 }
3437
3438 return rc;
3439}
3440
3441HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3442{
3443 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3444 return setError(E_INVALIDARG,
3445 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3446 aPosition, SchemaDefs::MaxBootPosition);
3447
3448 if (aDevice == DeviceType_USB)
3449 return setError(E_NOTIMPL,
3450 tr("Booting from USB device is currently not supported"));
3451
3452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3453
3454 HRESULT rc = i_checkStateDependency(MutableStateDep);
3455 if (FAILED(rc)) return rc;
3456
3457 i_setModified(IsModified_MachineData);
3458 mHWData.backup();
3459 mHWData->mBootOrder[aPosition - 1] = aDevice;
3460
3461 return S_OK;
3462}
3463
3464HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3465{
3466 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3467 return setError(E_INVALIDARG,
3468 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3469 aPosition, SchemaDefs::MaxBootPosition);
3470
3471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3472
3473 *aDevice = mHWData->mBootOrder[aPosition - 1];
3474
3475 return S_OK;
3476}
3477
3478HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3479 LONG aControllerPort,
3480 LONG aDevice,
3481 DeviceType_T aType,
3482 const ComPtr<IMedium> &aMedium)
3483{
3484 IMedium *aM = aMedium;
3485 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3486 aName.c_str(), aControllerPort, aDevice, aType, aM));
3487
3488 // request the host lock first, since might be calling Host methods for getting host drives;
3489 // next, protect the media tree all the while we're in here, as well as our member variables
3490 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3491 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3492
3493 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3494 if (FAILED(rc)) return rc;
3495
3496 /// @todo NEWMEDIA implicit machine registration
3497 if (!mData->mRegistered)
3498 return setError(VBOX_E_INVALID_OBJECT_STATE,
3499 tr("Cannot attach storage devices to an unregistered machine"));
3500
3501 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3502
3503 /* Check for an existing controller. */
3504 ComObjPtr<StorageController> ctl;
3505 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3506 if (FAILED(rc)) return rc;
3507
3508 StorageControllerType_T ctrlType;
3509 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3510 if (FAILED(rc))
3511 return setError(E_FAIL,
3512 tr("Could not get type of controller '%s'"),
3513 aName.c_str());
3514
3515 bool fSilent = false;
3516 Utf8Str strReconfig;
3517
3518 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3519 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3520 if ( mData->mMachineState == MachineState_Paused
3521 && strReconfig == "1")
3522 fSilent = true;
3523
3524 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3525 bool fHotplug = false;
3526 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3527 fHotplug = true;
3528
3529 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3530 return setError(VBOX_E_INVALID_VM_STATE,
3531 tr("Controller '%s' does not support hotplugging"),
3532 aName.c_str());
3533
3534 // check that the port and device are not out of range
3535 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3536 if (FAILED(rc)) return rc;
3537
3538 /* check if the device slot is already busy */
3539 MediumAttachment *pAttachTemp;
3540 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3541 aName,
3542 aControllerPort,
3543 aDevice)))
3544 {
3545 Medium *pMedium = pAttachTemp->i_getMedium();
3546 if (pMedium)
3547 {
3548 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3549 return setError(VBOX_E_OBJECT_IN_USE,
3550 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3551 pMedium->i_getLocationFull().c_str(),
3552 aControllerPort,
3553 aDevice,
3554 aName.c_str());
3555 }
3556 else
3557 return setError(VBOX_E_OBJECT_IN_USE,
3558 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3559 aControllerPort, aDevice, aName.c_str());
3560 }
3561
3562 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3563 if (aMedium && medium.isNull())
3564 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3565
3566 AutoCaller mediumCaller(medium);
3567 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3568
3569 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3570
3571 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3572 && !medium.isNull()
3573 && ( medium->i_getType() != MediumType_Readonly
3574 || medium->i_getDeviceType() != DeviceType_DVD)
3575 )
3576 return setError(VBOX_E_OBJECT_IN_USE,
3577 tr("Medium '%s' is already attached to this virtual machine"),
3578 medium->i_getLocationFull().c_str());
3579
3580 if (!medium.isNull())
3581 {
3582 MediumType_T mtype = medium->i_getType();
3583 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3584 // For DVDs it's not written to the config file, so needs no global config
3585 // version bump. For floppies it's a new attribute "type", which is ignored
3586 // by older VirtualBox version, so needs no global config version bump either.
3587 // For hard disks this type is not accepted.
3588 if (mtype == MediumType_MultiAttach)
3589 {
3590 // This type is new with VirtualBox 4.0 and therefore requires settings
3591 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3592 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3593 // two reasons: The medium type is a property of the media registry tree, which
3594 // can reside in the global config file (for pre-4.0 media); we would therefore
3595 // possibly need to bump the global config version. We don't want to do that though
3596 // because that might make downgrading to pre-4.0 impossible.
3597 // As a result, we can only use these two new types if the medium is NOT in the
3598 // global registry:
3599 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3600 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3601 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3602 )
3603 return setError(VBOX_E_INVALID_OBJECT_STATE,
3604 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3605 "to machines that were created with VirtualBox 4.0 or later"),
3606 medium->i_getLocationFull().c_str());
3607 }
3608 }
3609
3610 bool fIndirect = false;
3611 if (!medium.isNull())
3612 fIndirect = medium->i_isReadOnly();
3613 bool associate = true;
3614
3615 do
3616 {
3617 if ( aType == DeviceType_HardDisk
3618 && mMediumAttachments.isBackedUp())
3619 {
3620 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3621
3622 /* check if the medium was attached to the VM before we started
3623 * changing attachments in which case the attachment just needs to
3624 * be restored */
3625 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3626 {
3627 AssertReturn(!fIndirect, E_FAIL);
3628
3629 /* see if it's the same bus/channel/device */
3630 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3631 {
3632 /* the simplest case: restore the whole attachment
3633 * and return, nothing else to do */
3634 mMediumAttachments->push_back(pAttachTemp);
3635
3636 /* Reattach the medium to the VM. */
3637 if (fHotplug || fSilent)
3638 {
3639 mediumLock.release();
3640 treeLock.release();
3641 alock.release();
3642
3643 MediumLockList *pMediumLockList(new MediumLockList());
3644
3645 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3646 medium /* pToLockWrite */,
3647 false /* fMediumLockWriteAll */,
3648 NULL,
3649 *pMediumLockList);
3650 alock.acquire();
3651 if (FAILED(rc))
3652 delete pMediumLockList;
3653 else
3654 {
3655 mData->mSession.mLockedMedia.Unlock();
3656 alock.release();
3657 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3658 mData->mSession.mLockedMedia.Lock();
3659 alock.acquire();
3660 }
3661 alock.release();
3662
3663 if (SUCCEEDED(rc))
3664 {
3665 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3666 /* Remove lock list in case of error. */
3667 if (FAILED(rc))
3668 {
3669 mData->mSession.mLockedMedia.Unlock();
3670 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3671 mData->mSession.mLockedMedia.Lock();
3672 }
3673 }
3674 }
3675
3676 return S_OK;
3677 }
3678
3679 /* bus/channel/device differ; we need a new attachment object,
3680 * but don't try to associate it again */
3681 associate = false;
3682 break;
3683 }
3684 }
3685
3686 /* go further only if the attachment is to be indirect */
3687 if (!fIndirect)
3688 break;
3689
3690 /* perform the so called smart attachment logic for indirect
3691 * attachments. Note that smart attachment is only applicable to base
3692 * hard disks. */
3693
3694 if (medium->i_getParent().isNull())
3695 {
3696 /* first, investigate the backup copy of the current hard disk
3697 * attachments to make it possible to re-attach existing diffs to
3698 * another device slot w/o losing their contents */
3699 if (mMediumAttachments.isBackedUp())
3700 {
3701 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3702
3703 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3704 uint32_t foundLevel = 0;
3705
3706 for (MediumAttachmentList::const_iterator
3707 it = oldAtts.begin();
3708 it != oldAtts.end();
3709 ++it)
3710 {
3711 uint32_t level = 0;
3712 MediumAttachment *pAttach = *it;
3713 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3714 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3715 if (pMedium.isNull())
3716 continue;
3717
3718 if (pMedium->i_getBase(&level) == medium)
3719 {
3720 /* skip the hard disk if its currently attached (we
3721 * cannot attach the same hard disk twice) */
3722 if (i_findAttachment(*mMediumAttachments.data(),
3723 pMedium))
3724 continue;
3725
3726 /* matched device, channel and bus (i.e. attached to the
3727 * same place) will win and immediately stop the search;
3728 * otherwise the attachment that has the youngest
3729 * descendant of medium will be used
3730 */
3731 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3732 {
3733 /* the simplest case: restore the whole attachment
3734 * and return, nothing else to do */
3735 mMediumAttachments->push_back(*it);
3736
3737 /* Reattach the medium to the VM. */
3738 if (fHotplug || fSilent)
3739 {
3740 mediumLock.release();
3741 treeLock.release();
3742 alock.release();
3743
3744 MediumLockList *pMediumLockList(new MediumLockList());
3745
3746 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3747 medium /* pToLockWrite */,
3748 false /* fMediumLockWriteAll */,
3749 NULL,
3750 *pMediumLockList);
3751 alock.acquire();
3752 if (FAILED(rc))
3753 delete pMediumLockList;
3754 else
3755 {
3756 mData->mSession.mLockedMedia.Unlock();
3757 alock.release();
3758 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3759 mData->mSession.mLockedMedia.Lock();
3760 alock.acquire();
3761 }
3762 alock.release();
3763
3764 if (SUCCEEDED(rc))
3765 {
3766 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3767 /* Remove lock list in case of error. */
3768 if (FAILED(rc))
3769 {
3770 mData->mSession.mLockedMedia.Unlock();
3771 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3772 mData->mSession.mLockedMedia.Lock();
3773 }
3774 }
3775 }
3776
3777 return S_OK;
3778 }
3779 else if ( foundIt == oldAtts.end()
3780 || level > foundLevel /* prefer younger */
3781 )
3782 {
3783 foundIt = it;
3784 foundLevel = level;
3785 }
3786 }
3787 }
3788
3789 if (foundIt != oldAtts.end())
3790 {
3791 /* use the previously attached hard disk */
3792 medium = (*foundIt)->i_getMedium();
3793 mediumCaller.attach(medium);
3794 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3795 mediumLock.attach(medium);
3796 /* not implicit, doesn't require association with this VM */
3797 fIndirect = false;
3798 associate = false;
3799 /* go right to the MediumAttachment creation */
3800 break;
3801 }
3802 }
3803
3804 /* must give up the medium lock and medium tree lock as below we
3805 * go over snapshots, which needs a lock with higher lock order. */
3806 mediumLock.release();
3807 treeLock.release();
3808
3809 /* then, search through snapshots for the best diff in the given
3810 * hard disk's chain to base the new diff on */
3811
3812 ComObjPtr<Medium> base;
3813 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3814 while (snap)
3815 {
3816 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3817
3818 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3819
3820 MediumAttachment *pAttachFound = NULL;
3821 uint32_t foundLevel = 0;
3822
3823 for (MediumAttachmentList::const_iterator
3824 it = snapAtts.begin();
3825 it != snapAtts.end();
3826 ++it)
3827 {
3828 MediumAttachment *pAttach = *it;
3829 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3830 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3831 if (pMedium.isNull())
3832 continue;
3833
3834 uint32_t level = 0;
3835 if (pMedium->i_getBase(&level) == medium)
3836 {
3837 /* matched device, channel and bus (i.e. attached to the
3838 * same place) will win and immediately stop the search;
3839 * otherwise the attachment that has the youngest
3840 * descendant of medium will be used
3841 */
3842 if ( pAttach->i_getDevice() == aDevice
3843 && pAttach->i_getPort() == aControllerPort
3844 && pAttach->i_getControllerName() == aName
3845 )
3846 {
3847 pAttachFound = pAttach;
3848 break;
3849 }
3850 else if ( !pAttachFound
3851 || level > foundLevel /* prefer younger */
3852 )
3853 {
3854 pAttachFound = pAttach;
3855 foundLevel = level;
3856 }
3857 }
3858 }
3859
3860 if (pAttachFound)
3861 {
3862 base = pAttachFound->i_getMedium();
3863 break;
3864 }
3865
3866 snap = snap->i_getParent();
3867 }
3868
3869 /* re-lock medium tree and the medium, as we need it below */
3870 treeLock.acquire();
3871 mediumLock.acquire();
3872
3873 /* found a suitable diff, use it as a base */
3874 if (!base.isNull())
3875 {
3876 medium = base;
3877 mediumCaller.attach(medium);
3878 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3879 mediumLock.attach(medium);
3880 }
3881 }
3882
3883 Utf8Str strFullSnapshotFolder;
3884 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3885
3886 ComObjPtr<Medium> diff;
3887 diff.createObject();
3888 // store this diff in the same registry as the parent
3889 Guid uuidRegistryParent;
3890 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3891 {
3892 // parent image has no registry: this can happen if we're attaching a new immutable
3893 // image that has not yet been attached (medium then points to the base and we're
3894 // creating the diff image for the immutable, and the parent is not yet registered);
3895 // put the parent in the machine registry then
3896 mediumLock.release();
3897 treeLock.release();
3898 alock.release();
3899 i_addMediumToRegistry(medium);
3900 alock.acquire();
3901 treeLock.acquire();
3902 mediumLock.acquire();
3903 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3904 }
3905 rc = diff->init(mParent,
3906 medium->i_getPreferredDiffFormat(),
3907 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3908 uuidRegistryParent,
3909 DeviceType_HardDisk);
3910 if (FAILED(rc)) return rc;
3911
3912 /* Apply the normal locking logic to the entire chain. */
3913 MediumLockList *pMediumLockList(new MediumLockList());
3914 mediumLock.release();
3915 treeLock.release();
3916 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3917 diff /* pToLockWrite */,
3918 false /* fMediumLockWriteAll */,
3919 medium,
3920 *pMediumLockList);
3921 treeLock.acquire();
3922 mediumLock.acquire();
3923 if (SUCCEEDED(rc))
3924 {
3925 mediumLock.release();
3926 treeLock.release();
3927 rc = pMediumLockList->Lock();
3928 treeLock.acquire();
3929 mediumLock.acquire();
3930 if (FAILED(rc))
3931 setError(rc,
3932 tr("Could not lock medium when creating diff '%s'"),
3933 diff->i_getLocationFull().c_str());
3934 else
3935 {
3936 /* will release the lock before the potentially lengthy
3937 * operation, so protect with the special state */
3938 MachineState_T oldState = mData->mMachineState;
3939 i_setMachineState(MachineState_SettingUp);
3940
3941 mediumLock.release();
3942 treeLock.release();
3943 alock.release();
3944
3945 rc = medium->i_createDiffStorage(diff,
3946 medium->i_getPreferredDiffVariant(),
3947 pMediumLockList,
3948 NULL /* aProgress */,
3949 true /* aWait */,
3950 false /* aNotify */);
3951
3952 alock.acquire();
3953 treeLock.acquire();
3954 mediumLock.acquire();
3955
3956 i_setMachineState(oldState);
3957 }
3958 }
3959
3960 /* Unlock the media and free the associated memory. */
3961 delete pMediumLockList;
3962
3963 if (FAILED(rc)) return rc;
3964
3965 /* use the created diff for the actual attachment */
3966 medium = diff;
3967 mediumCaller.attach(medium);
3968 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3969 mediumLock.attach(medium);
3970 }
3971 while (0);
3972
3973 ComObjPtr<MediumAttachment> attachment;
3974 attachment.createObject();
3975 rc = attachment->init(this,
3976 medium,
3977 aName,
3978 aControllerPort,
3979 aDevice,
3980 aType,
3981 fIndirect,
3982 false /* fPassthrough */,
3983 false /* fTempEject */,
3984 false /* fNonRotational */,
3985 false /* fDiscard */,
3986 fHotplug /* fHotPluggable */,
3987 Utf8Str::Empty);
3988 if (FAILED(rc)) return rc;
3989
3990 if (associate && !medium.isNull())
3991 {
3992 // as the last step, associate the medium to the VM
3993 rc = medium->i_addBackReference(mData->mUuid);
3994 // here we can fail because of Deleting, or being in process of creating a Diff
3995 if (FAILED(rc)) return rc;
3996
3997 mediumLock.release();
3998 treeLock.release();
3999 alock.release();
4000 i_addMediumToRegistry(medium);
4001 alock.acquire();
4002 treeLock.acquire();
4003 mediumLock.acquire();
4004 }
4005
4006 /* success: finally remember the attachment */
4007 i_setModified(IsModified_Storage);
4008 mMediumAttachments.backup();
4009 mMediumAttachments->push_back(attachment);
4010
4011 mediumLock.release();
4012 treeLock.release();
4013 alock.release();
4014
4015 if (fHotplug || fSilent)
4016 {
4017 if (!medium.isNull())
4018 {
4019 MediumLockList *pMediumLockList(new MediumLockList());
4020
4021 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4022 medium /* pToLockWrite */,
4023 false /* fMediumLockWriteAll */,
4024 NULL,
4025 *pMediumLockList);
4026 alock.acquire();
4027 if (FAILED(rc))
4028 delete pMediumLockList;
4029 else
4030 {
4031 mData->mSession.mLockedMedia.Unlock();
4032 alock.release();
4033 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4034 mData->mSession.mLockedMedia.Lock();
4035 alock.acquire();
4036 }
4037 alock.release();
4038 }
4039
4040 if (SUCCEEDED(rc))
4041 {
4042 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4043 /* Remove lock list in case of error. */
4044 if (FAILED(rc))
4045 {
4046 mData->mSession.mLockedMedia.Unlock();
4047 mData->mSession.mLockedMedia.Remove(attachment);
4048 mData->mSession.mLockedMedia.Lock();
4049 }
4050 }
4051 }
4052
4053 /* Save modified registries, but skip this machine as it's the caller's
4054 * job to save its settings like all other settings changes. */
4055 mParent->i_unmarkRegistryModified(i_getId());
4056 mParent->i_saveModifiedRegistries();
4057
4058 if (SUCCEEDED(rc))
4059 {
4060 if (fIndirect && medium != aM)
4061 mParent->i_onMediumConfigChanged(medium);
4062 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4063 }
4064
4065 return rc;
4066}
4067
4068HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4069 LONG aDevice)
4070{
4071 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4072 aName.c_str(), aControllerPort, aDevice));
4073
4074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4075
4076 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4077 if (FAILED(rc)) return rc;
4078
4079 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4080
4081 /* Check for an existing controller. */
4082 ComObjPtr<StorageController> ctl;
4083 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4084 if (FAILED(rc)) return rc;
4085
4086 StorageControllerType_T ctrlType;
4087 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4088 if (FAILED(rc))
4089 return setError(E_FAIL,
4090 tr("Could not get type of controller '%s'"),
4091 aName.c_str());
4092
4093 bool fSilent = false;
4094 Utf8Str strReconfig;
4095
4096 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4097 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4098 if ( mData->mMachineState == MachineState_Paused
4099 && strReconfig == "1")
4100 fSilent = true;
4101
4102 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4103 bool fHotplug = false;
4104 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4105 fHotplug = true;
4106
4107 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4108 return setError(VBOX_E_INVALID_VM_STATE,
4109 tr("Controller '%s' does not support hotplugging"),
4110 aName.c_str());
4111
4112 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4113 aName,
4114 aControllerPort,
4115 aDevice);
4116 if (!pAttach)
4117 return setError(VBOX_E_OBJECT_NOT_FOUND,
4118 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4119 aDevice, aControllerPort, aName.c_str());
4120
4121 if (fHotplug && !pAttach->i_getHotPluggable())
4122 return setError(VBOX_E_NOT_SUPPORTED,
4123 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4124 aDevice, aControllerPort, aName.c_str());
4125
4126 /*
4127 * The VM has to detach the device before we delete any implicit diffs.
4128 * If this fails we can roll back without loosing data.
4129 */
4130 if (fHotplug || fSilent)
4131 {
4132 alock.release();
4133 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4134 alock.acquire();
4135 }
4136 if (FAILED(rc)) return rc;
4137
4138 /* If we are here everything went well and we can delete the implicit now. */
4139 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4140
4141 alock.release();
4142
4143 /* Save modified registries, but skip this machine as it's the caller's
4144 * job to save its settings like all other settings changes. */
4145 mParent->i_unmarkRegistryModified(i_getId());
4146 mParent->i_saveModifiedRegistries();
4147
4148 if (SUCCEEDED(rc))
4149 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4150
4151 return rc;
4152}
4153
4154HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4155 LONG aDevice, BOOL aPassthrough)
4156{
4157 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4158 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4159
4160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4161
4162 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4163 if (FAILED(rc)) return rc;
4164
4165 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4166
4167 /* Check for an existing controller. */
4168 ComObjPtr<StorageController> ctl;
4169 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4170 if (FAILED(rc)) return rc;
4171
4172 StorageControllerType_T ctrlType;
4173 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4174 if (FAILED(rc))
4175 return setError(E_FAIL,
4176 tr("Could not get type of controller '%s'"),
4177 aName.c_str());
4178
4179 bool fSilent = false;
4180 Utf8Str strReconfig;
4181
4182 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4183 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4184 if ( mData->mMachineState == MachineState_Paused
4185 && strReconfig == "1")
4186 fSilent = true;
4187
4188 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4189 bool fHotplug = false;
4190 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4191 fHotplug = true;
4192
4193 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4194 return setError(VBOX_E_INVALID_VM_STATE,
4195 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4196 aName.c_str());
4197
4198 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4199 aName,
4200 aControllerPort,
4201 aDevice);
4202 if (!pAttach)
4203 return setError(VBOX_E_OBJECT_NOT_FOUND,
4204 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4205 aDevice, aControllerPort, aName.c_str());
4206
4207
4208 i_setModified(IsModified_Storage);
4209 mMediumAttachments.backup();
4210
4211 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4212
4213 if (pAttach->i_getType() != DeviceType_DVD)
4214 return setError(E_INVALIDARG,
4215 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4216 aDevice, aControllerPort, aName.c_str());
4217
4218 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4219
4220 pAttach->i_updatePassthrough(!!aPassthrough);
4221
4222 attLock.release();
4223 alock.release();
4224 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4225 if (SUCCEEDED(rc) && fValueChanged)
4226 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4227
4228 return rc;
4229}
4230
4231HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4232 LONG aDevice, BOOL aTemporaryEject)
4233{
4234
4235 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4236 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4237
4238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4239
4240 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4241 if (FAILED(rc)) return rc;
4242
4243 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4244 aName,
4245 aControllerPort,
4246 aDevice);
4247 if (!pAttach)
4248 return setError(VBOX_E_OBJECT_NOT_FOUND,
4249 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4250 aDevice, aControllerPort, aName.c_str());
4251
4252
4253 i_setModified(IsModified_Storage);
4254 mMediumAttachments.backup();
4255
4256 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4257
4258 if (pAttach->i_getType() != DeviceType_DVD)
4259 return setError(E_INVALIDARG,
4260 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4261 aDevice, aControllerPort, aName.c_str());
4262 pAttach->i_updateTempEject(!!aTemporaryEject);
4263
4264 return S_OK;
4265}
4266
4267HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4268 LONG aDevice, BOOL aNonRotational)
4269{
4270
4271 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4272 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4273
4274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4275
4276 HRESULT rc = i_checkStateDependency(MutableStateDep);
4277 if (FAILED(rc)) return rc;
4278
4279 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4280
4281 if (Global::IsOnlineOrTransient(mData->mMachineState))
4282 return setError(VBOX_E_INVALID_VM_STATE,
4283 tr("Invalid machine state: %s"),
4284 Global::stringifyMachineState(mData->mMachineState));
4285
4286 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4287 aName,
4288 aControllerPort,
4289 aDevice);
4290 if (!pAttach)
4291 return setError(VBOX_E_OBJECT_NOT_FOUND,
4292 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4293 aDevice, aControllerPort, aName.c_str());
4294
4295
4296 i_setModified(IsModified_Storage);
4297 mMediumAttachments.backup();
4298
4299 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4300
4301 if (pAttach->i_getType() != DeviceType_HardDisk)
4302 return setError(E_INVALIDARG,
4303 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"),
4304 aDevice, aControllerPort, aName.c_str());
4305 pAttach->i_updateNonRotational(!!aNonRotational);
4306
4307 return S_OK;
4308}
4309
4310HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4311 LONG aDevice, BOOL aDiscard)
4312{
4313
4314 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4315 aName.c_str(), aControllerPort, aDevice, aDiscard));
4316
4317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4318
4319 HRESULT rc = i_checkStateDependency(MutableStateDep);
4320 if (FAILED(rc)) return rc;
4321
4322 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4323
4324 if (Global::IsOnlineOrTransient(mData->mMachineState))
4325 return setError(VBOX_E_INVALID_VM_STATE,
4326 tr("Invalid machine state: %s"),
4327 Global::stringifyMachineState(mData->mMachineState));
4328
4329 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4330 aName,
4331 aControllerPort,
4332 aDevice);
4333 if (!pAttach)
4334 return setError(VBOX_E_OBJECT_NOT_FOUND,
4335 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4336 aDevice, aControllerPort, aName.c_str());
4337
4338
4339 i_setModified(IsModified_Storage);
4340 mMediumAttachments.backup();
4341
4342 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4343
4344 if (pAttach->i_getType() != DeviceType_HardDisk)
4345 return setError(E_INVALIDARG,
4346 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"),
4347 aDevice, aControllerPort, aName.c_str());
4348 pAttach->i_updateDiscard(!!aDiscard);
4349
4350 return S_OK;
4351}
4352
4353HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4354 LONG aDevice, BOOL aHotPluggable)
4355{
4356 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4357 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4358
4359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4360
4361 HRESULT rc = i_checkStateDependency(MutableStateDep);
4362 if (FAILED(rc)) return rc;
4363
4364 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4365
4366 if (Global::IsOnlineOrTransient(mData->mMachineState))
4367 return setError(VBOX_E_INVALID_VM_STATE,
4368 tr("Invalid machine state: %s"),
4369 Global::stringifyMachineState(mData->mMachineState));
4370
4371 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4372 aName,
4373 aControllerPort,
4374 aDevice);
4375 if (!pAttach)
4376 return setError(VBOX_E_OBJECT_NOT_FOUND,
4377 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4378 aDevice, aControllerPort, aName.c_str());
4379
4380 /* Check for an existing controller. */
4381 ComObjPtr<StorageController> ctl;
4382 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4383 if (FAILED(rc)) return rc;
4384
4385 StorageControllerType_T ctrlType;
4386 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4387 if (FAILED(rc))
4388 return setError(E_FAIL,
4389 tr("Could not get type of controller '%s'"),
4390 aName.c_str());
4391
4392 if (!i_isControllerHotplugCapable(ctrlType))
4393 return setError(VBOX_E_NOT_SUPPORTED,
4394 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4395 aName.c_str());
4396
4397 i_setModified(IsModified_Storage);
4398 mMediumAttachments.backup();
4399
4400 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4401
4402 if (pAttach->i_getType() == DeviceType_Floppy)
4403 return setError(E_INVALIDARG,
4404 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"),
4405 aDevice, aControllerPort, aName.c_str());
4406 pAttach->i_updateHotPluggable(!!aHotPluggable);
4407
4408 return S_OK;
4409}
4410
4411HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4412 LONG aDevice)
4413{
4414 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4415 aName.c_str(), aControllerPort, aDevice));
4416
4417 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4418}
4419
4420HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4421 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4422{
4423 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4424 aName.c_str(), aControllerPort, aDevice));
4425
4426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4427
4428 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4429 if (FAILED(rc)) return rc;
4430
4431 if (Global::IsOnlineOrTransient(mData->mMachineState))
4432 return setError(VBOX_E_INVALID_VM_STATE,
4433 tr("Invalid machine state: %s"),
4434 Global::stringifyMachineState(mData->mMachineState));
4435
4436 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4437 aName,
4438 aControllerPort,
4439 aDevice);
4440 if (!pAttach)
4441 return setError(VBOX_E_OBJECT_NOT_FOUND,
4442 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4443 aDevice, aControllerPort, aName.c_str());
4444
4445
4446 i_setModified(IsModified_Storage);
4447 mMediumAttachments.backup();
4448
4449 IBandwidthGroup *iB = aBandwidthGroup;
4450 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4451 if (aBandwidthGroup && group.isNull())
4452 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4453
4454 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4455
4456 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4457 if (strBandwidthGroupOld.isNotEmpty())
4458 {
4459 /* Get the bandwidth group object and release it - this must not fail. */
4460 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4461 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4462 Assert(SUCCEEDED(rc));
4463
4464 pBandwidthGroupOld->i_release();
4465 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4466 }
4467
4468 if (!group.isNull())
4469 {
4470 group->i_reference();
4471 pAttach->i_updateBandwidthGroup(group->i_getName());
4472 }
4473
4474 return S_OK;
4475}
4476
4477HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4478 LONG aControllerPort,
4479 LONG aDevice,
4480 DeviceType_T aType)
4481{
4482 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4483 aName.c_str(), aControllerPort, aDevice, aType));
4484
4485 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4486}
4487
4488
4489HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4490 LONG aControllerPort,
4491 LONG aDevice,
4492 BOOL aForce)
4493{
4494 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4495 aName.c_str(), aControllerPort, aForce));
4496
4497 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4498}
4499
4500HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4501 LONG aControllerPort,
4502 LONG aDevice,
4503 const ComPtr<IMedium> &aMedium,
4504 BOOL aForce)
4505{
4506 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4507 aName.c_str(), aControllerPort, aDevice, aForce));
4508
4509 // request the host lock first, since might be calling Host methods for getting host drives;
4510 // next, protect the media tree all the while we're in here, as well as our member variables
4511 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4512 this->lockHandle(),
4513 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4514
4515 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4516 aName,
4517 aControllerPort,
4518 aDevice);
4519 if (pAttach.isNull())
4520 return setError(VBOX_E_OBJECT_NOT_FOUND,
4521 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4522 aDevice, aControllerPort, aName.c_str());
4523
4524 /* Remember previously mounted medium. The medium before taking the
4525 * backup is not necessarily the same thing. */
4526 ComObjPtr<Medium> oldmedium;
4527 oldmedium = pAttach->i_getMedium();
4528
4529 IMedium *iM = aMedium;
4530 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4531 if (aMedium && pMedium.isNull())
4532 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4533
4534 AutoCaller mediumCaller(pMedium);
4535 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4536
4537 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4538 if (pMedium)
4539 {
4540 DeviceType_T mediumType = pAttach->i_getType();
4541 switch (mediumType)
4542 {
4543 case DeviceType_DVD:
4544 case DeviceType_Floppy:
4545 break;
4546
4547 default:
4548 return setError(VBOX_E_INVALID_OBJECT_STATE,
4549 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4550 aControllerPort,
4551 aDevice,
4552 aName.c_str());
4553 }
4554 }
4555
4556 i_setModified(IsModified_Storage);
4557 mMediumAttachments.backup();
4558
4559 {
4560 // The backup operation makes the pAttach reference point to the
4561 // old settings. Re-get the correct reference.
4562 pAttach = i_findAttachment(*mMediumAttachments.data(),
4563 aName,
4564 aControllerPort,
4565 aDevice);
4566 if (!oldmedium.isNull())
4567 oldmedium->i_removeBackReference(mData->mUuid);
4568 if (!pMedium.isNull())
4569 {
4570 pMedium->i_addBackReference(mData->mUuid);
4571
4572 mediumLock.release();
4573 multiLock.release();
4574 i_addMediumToRegistry(pMedium);
4575 multiLock.acquire();
4576 mediumLock.acquire();
4577 }
4578
4579 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4580 pAttach->i_updateMedium(pMedium);
4581 }
4582
4583 i_setModified(IsModified_Storage);
4584
4585 mediumLock.release();
4586 multiLock.release();
4587 HRESULT rc = i_onMediumChange(pAttach, aForce);
4588 multiLock.acquire();
4589 mediumLock.acquire();
4590
4591 /* On error roll back this change only. */
4592 if (FAILED(rc))
4593 {
4594 if (!pMedium.isNull())
4595 pMedium->i_removeBackReference(mData->mUuid);
4596 pAttach = i_findAttachment(*mMediumAttachments.data(),
4597 aName,
4598 aControllerPort,
4599 aDevice);
4600 /* If the attachment is gone in the meantime, bail out. */
4601 if (pAttach.isNull())
4602 return rc;
4603 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4604 if (!oldmedium.isNull())
4605 oldmedium->i_addBackReference(mData->mUuid);
4606 pAttach->i_updateMedium(oldmedium);
4607 }
4608
4609 mediumLock.release();
4610 multiLock.release();
4611
4612 /* Save modified registries, but skip this machine as it's the caller's
4613 * job to save its settings like all other settings changes. */
4614 mParent->i_unmarkRegistryModified(i_getId());
4615 mParent->i_saveModifiedRegistries();
4616
4617 return rc;
4618}
4619HRESULT Machine::getMedium(const com::Utf8Str &aName,
4620 LONG aControllerPort,
4621 LONG aDevice,
4622 ComPtr<IMedium> &aMedium)
4623{
4624 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4625 aName.c_str(), aControllerPort, aDevice));
4626
4627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4628
4629 aMedium = NULL;
4630
4631 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4632 aName,
4633 aControllerPort,
4634 aDevice);
4635 if (pAttach.isNull())
4636 return setError(VBOX_E_OBJECT_NOT_FOUND,
4637 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4638 aDevice, aControllerPort, aName.c_str());
4639
4640 aMedium = pAttach->i_getMedium();
4641
4642 return S_OK;
4643}
4644
4645HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4646{
4647 if (aSlot < RT_ELEMENTS(mSerialPorts))
4648 {
4649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4650 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4651 return S_OK;
4652 }
4653 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4654}
4655
4656HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4657{
4658 if (aSlot < RT_ELEMENTS(mParallelPorts))
4659 {
4660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4661 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4662 return S_OK;
4663 }
4664 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4665}
4666
4667
4668HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4669{
4670 /* Do not assert if slot is out of range, just return the advertised
4671 status. testdriver/vbox.py triggers this in logVmInfo. */
4672 if (aSlot >= mNetworkAdapters.size())
4673 return setError(E_INVALIDARG,
4674 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4675 aSlot, mNetworkAdapters.size());
4676
4677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4678
4679 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4680
4681 return S_OK;
4682}
4683
4684HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4685{
4686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4687
4688 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4689 size_t i = 0;
4690 for (settings::StringsMap::const_iterator
4691 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4692 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4693 ++it, ++i)
4694 aKeys[i] = it->first;
4695
4696 return S_OK;
4697}
4698
4699 /**
4700 * @note Locks this object for reading.
4701 */
4702HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4703 com::Utf8Str &aValue)
4704{
4705 /* start with nothing found */
4706 aValue = "";
4707
4708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4709
4710 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4711 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4712 // found:
4713 aValue = it->second; // source is a Utf8Str
4714
4715 /* return the result to caller (may be empty) */
4716 return S_OK;
4717}
4718
4719 /**
4720 * @note Locks mParent for writing + this object for writing.
4721 */
4722HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4723{
4724 /* Because control characters in aKey have caused problems in the settings
4725 * they are rejected unless the key should be deleted. */
4726 if (!aValue.isEmpty())
4727 {
4728 for (size_t i = 0; i < aKey.length(); ++i)
4729 {
4730 char ch = aKey[i];
4731 if (RTLocCIsCntrl(ch))
4732 return E_INVALIDARG;
4733 }
4734 }
4735
4736 Utf8Str strOldValue; // empty
4737
4738 // locking note: we only hold the read lock briefly to look up the old value,
4739 // then release it and call the onExtraCanChange callbacks. There is a small
4740 // chance of a race insofar as the callback might be called twice if two callers
4741 // change the same key at the same time, but that's a much better solution
4742 // than the deadlock we had here before. The actual changing of the extradata
4743 // is then performed under the write lock and race-free.
4744
4745 // look up the old value first; if nothing has changed then we need not do anything
4746 {
4747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4748
4749 // For snapshots don't even think about allowing changes, extradata
4750 // is global for a machine, so there is nothing snapshot specific.
4751 if (i_isSnapshotMachine())
4752 return setError(VBOX_E_INVALID_VM_STATE,
4753 tr("Cannot set extradata for a snapshot"));
4754
4755 // check if the right IMachine instance is used
4756 if (mData->mRegistered && !i_isSessionMachine())
4757 return setError(VBOX_E_INVALID_VM_STATE,
4758 tr("Cannot set extradata for an immutable machine"));
4759
4760 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4761 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4762 strOldValue = it->second;
4763 }
4764
4765 bool fChanged;
4766 if ((fChanged = (strOldValue != aValue)))
4767 {
4768 // ask for permission from all listeners outside the locks;
4769 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4770 // lock to copy the list of callbacks to invoke
4771 Bstr bstrError;
4772 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4773 {
4774 const char *sep = bstrError.isEmpty() ? "" : ": ";
4775 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4776 return setError(E_ACCESSDENIED,
4777 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4778 aKey.c_str(),
4779 aValue.c_str(),
4780 sep,
4781 bstrError.raw());
4782 }
4783
4784 // data is changing and change not vetoed: then write it out under the lock
4785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4786
4787 if (aValue.isEmpty())
4788 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4789 else
4790 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4791 // creates a new key if needed
4792
4793 bool fNeedsGlobalSaveSettings = false;
4794 // This saving of settings is tricky: there is no "old state" for the
4795 // extradata items at all (unlike all other settings), so the old/new
4796 // settings comparison would give a wrong result!
4797 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4798
4799 if (fNeedsGlobalSaveSettings)
4800 {
4801 // save the global settings; for that we should hold only the VirtualBox lock
4802 alock.release();
4803 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4804 mParent->i_saveSettings();
4805 }
4806 }
4807
4808 // fire notification outside the lock
4809 if (fChanged)
4810 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4811
4812 return S_OK;
4813}
4814
4815HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4816{
4817 aProgress = NULL;
4818 NOREF(aSettingsFilePath);
4819 ReturnComNotImplemented();
4820}
4821
4822HRESULT Machine::saveSettings()
4823{
4824 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4825
4826 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4827 if (FAILED(rc)) return rc;
4828
4829 /* the settings file path may never be null */
4830 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4831
4832 /* save all VM data excluding snapshots */
4833 bool fNeedsGlobalSaveSettings = false;
4834 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4835 mlock.release();
4836
4837 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4838 {
4839 // save the global settings; for that we should hold only the VirtualBox lock
4840 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4841 rc = mParent->i_saveSettings();
4842 }
4843
4844 return rc;
4845}
4846
4847
4848HRESULT Machine::discardSettings()
4849{
4850 /*
4851 * We need to take the machine list lock here as well as the machine one
4852 * or we'll get into trouble should any media stuff require rolling back.
4853 *
4854 * Details:
4855 *
4856 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4857 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4858 * 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]
4859 * 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
4860 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4861 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4862 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4863 * 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
4864 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4865 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4866 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4867 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4868 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4869 * 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]
4870 * 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] (*)
4871 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4872 * 0:005> k
4873 * # Child-SP RetAddr Call Site
4874 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4875 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4876 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4877 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4878 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4879 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4880 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4881 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4882 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4883 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4884 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4885 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4886 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4887 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4888 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4889 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4890 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4891 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4892 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4893 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4894 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4895 *
4896 */
4897 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4899
4900 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4901 if (FAILED(rc)) return rc;
4902
4903 /*
4904 * during this rollback, the session will be notified if data has
4905 * been actually changed
4906 */
4907 i_rollback(true /* aNotify */);
4908
4909 return S_OK;
4910}
4911
4912/** @note Locks objects! */
4913HRESULT Machine::unregister(AutoCaller &autoCaller,
4914 CleanupMode_T aCleanupMode,
4915 std::vector<ComPtr<IMedium> > &aMedia)
4916{
4917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4918
4919 Guid id(i_getId());
4920
4921 if (mData->mSession.mState != SessionState_Unlocked)
4922 return setError(VBOX_E_INVALID_OBJECT_STATE,
4923 tr("Cannot unregister the machine '%s' while it is locked"),
4924 mUserData->s.strName.c_str());
4925
4926 // wait for state dependents to drop to zero
4927 i_ensureNoStateDependencies();
4928
4929 if (!mData->mAccessible)
4930 {
4931 // inaccessible machines can only be unregistered; uninitialize ourselves
4932 // here because currently there may be no unregistered that are inaccessible
4933 // (this state combination is not supported). Note releasing the caller and
4934 // leaving the lock before calling uninit()
4935 alock.release();
4936 autoCaller.release();
4937
4938 uninit();
4939
4940 mParent->i_unregisterMachine(this, id);
4941 // calls VirtualBox::i_saveSettings()
4942
4943 return S_OK;
4944 }
4945
4946 HRESULT rc = S_OK;
4947 mData->llFilesToDelete.clear();
4948
4949 if (!mSSData->strStateFilePath.isEmpty())
4950 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4951
4952 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4953 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4954 mData->llFilesToDelete.push_back(strNVRAMFile);
4955
4956 // This list collects the medium objects from all medium attachments
4957 // which we will detach from the machine and its snapshots, in a specific
4958 // order which allows for closing all media without getting "media in use"
4959 // errors, simply by going through the list from the front to the back:
4960 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4961 // and must be closed before the parent media from the snapshots, or closing the parents
4962 // will fail because they still have children);
4963 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4964 // the root ("first") snapshot of the machine.
4965 MediaList llMedia;
4966
4967 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4968 && mMediumAttachments->size()
4969 )
4970 {
4971 // we have media attachments: detach them all and add the Medium objects to our list
4972 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4973 }
4974
4975 if (mData->mFirstSnapshot)
4976 {
4977 // add the media from the medium attachments of the snapshots to llMedia
4978 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4979 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4980 // into the children first
4981
4982 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4983 MachineState_T oldState = mData->mMachineState;
4984 mData->mMachineState = MachineState_DeletingSnapshot;
4985
4986 // make a copy of the first snapshot reference so the refcount does not
4987 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4988 // (would hang due to the AutoCaller voodoo)
4989 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4990
4991 // GO!
4992 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4993
4994 mData->mMachineState = oldState;
4995 }
4996
4997 if (FAILED(rc))
4998 {
4999 i_rollbackMedia();
5000 return rc;
5001 }
5002
5003 // commit all the media changes made above
5004 i_commitMedia();
5005
5006 mData->mRegistered = false;
5007
5008 // machine lock no longer needed
5009 alock.release();
5010
5011 /* Make sure that the settings of the current VM are not saved, because
5012 * they are rather crippled at this point to meet the cleanup expectations
5013 * and there's no point destroying the VM config on disk just because. */
5014 mParent->i_unmarkRegistryModified(id);
5015
5016 // return media to caller
5017 aMedia.resize(llMedia.size());
5018 size_t i = 0;
5019 for (MediaList::const_iterator
5020 it = llMedia.begin();
5021 it != llMedia.end();
5022 ++it, ++i)
5023 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5024
5025 mParent->i_unregisterMachine(this, id);
5026 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5027
5028 return S_OK;
5029}
5030
5031/**
5032 * Task record for deleting a machine config.
5033 */
5034class Machine::DeleteConfigTask
5035 : public Machine::Task
5036{
5037public:
5038 DeleteConfigTask(Machine *m,
5039 Progress *p,
5040 const Utf8Str &t,
5041 const RTCList<ComPtr<IMedium> > &llMediums,
5042 const StringsList &llFilesToDelete)
5043 : Task(m, p, t),
5044 m_llMediums(llMediums),
5045 m_llFilesToDelete(llFilesToDelete)
5046 {}
5047
5048private:
5049 void handler()
5050 {
5051 try
5052 {
5053 m_pMachine->i_deleteConfigHandler(*this);
5054 }
5055 catch (...)
5056 {
5057 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5058 }
5059 }
5060
5061 RTCList<ComPtr<IMedium> > m_llMediums;
5062 StringsList m_llFilesToDelete;
5063
5064 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5065};
5066
5067/**
5068 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5069 * SessionMachine::taskHandler().
5070 *
5071 * @note Locks this object for writing.
5072 *
5073 * @param task
5074 * @return
5075 */
5076void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5077{
5078 LogFlowThisFuncEnter();
5079
5080 AutoCaller autoCaller(this);
5081 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5082 if (FAILED(autoCaller.rc()))
5083 {
5084 /* we might have been uninitialized because the session was accidentally
5085 * closed by the client, so don't assert */
5086 HRESULT rc = setError(E_FAIL,
5087 tr("The session has been accidentally closed"));
5088 task.m_pProgress->i_notifyComplete(rc);
5089 LogFlowThisFuncLeave();
5090 return;
5091 }
5092
5093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5094
5095 HRESULT rc = S_OK;
5096
5097 try
5098 {
5099 ULONG uLogHistoryCount = 3;
5100 ComPtr<ISystemProperties> systemProperties;
5101 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5102 if (FAILED(rc)) throw rc;
5103
5104 if (!systemProperties.isNull())
5105 {
5106 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5107 if (FAILED(rc)) throw rc;
5108 }
5109
5110 MachineState_T oldState = mData->mMachineState;
5111 i_setMachineState(MachineState_SettingUp);
5112 alock.release();
5113 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5114 {
5115 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5116 {
5117 AutoCaller mac(pMedium);
5118 if (FAILED(mac.rc())) throw mac.rc();
5119 Utf8Str strLocation = pMedium->i_getLocationFull();
5120 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5121 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5122 if (FAILED(rc)) throw rc;
5123 }
5124 if (pMedium->i_isMediumFormatFile())
5125 {
5126 ComPtr<IProgress> pProgress2;
5127 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5128 if (FAILED(rc)) throw rc;
5129 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5130 if (FAILED(rc)) throw rc;
5131 }
5132
5133 /* Close the medium, deliberately without checking the return
5134 * code, and without leaving any trace in the error info, as
5135 * a failure here is a very minor issue, which shouldn't happen
5136 * as above we even managed to delete the medium. */
5137 {
5138 ErrorInfoKeeper eik;
5139 pMedium->Close();
5140 }
5141 }
5142 i_setMachineState(oldState);
5143 alock.acquire();
5144
5145 // delete the files pushed on the task list by Machine::Delete()
5146 // (this includes saved states of the machine and snapshots and
5147 // medium storage files from the IMedium list passed in, and the
5148 // machine XML file)
5149 for (StringsList::const_iterator
5150 it = task.m_llFilesToDelete.begin();
5151 it != task.m_llFilesToDelete.end();
5152 ++it)
5153 {
5154 const Utf8Str &strFile = *it;
5155 LogFunc(("Deleting file %s\n", strFile.c_str()));
5156 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5157 if (FAILED(rc)) throw rc;
5158
5159 int vrc = RTFileDelete(strFile.c_str());
5160 if (RT_FAILURE(vrc))
5161 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5162 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5163 }
5164
5165 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5166 if (FAILED(rc)) throw rc;
5167
5168 /* delete the settings only when the file actually exists */
5169 if (mData->pMachineConfigFile->fileExists())
5170 {
5171 /* Delete any backup or uncommitted XML files. Ignore failures.
5172 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5173 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5174 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5175 RTFileDelete(otherXml.c_str());
5176 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5177 RTFileDelete(otherXml.c_str());
5178
5179 /* delete the Logs folder, nothing important should be left
5180 * there (we don't check for errors because the user might have
5181 * some private files there that we don't want to delete) */
5182 Utf8Str logFolder;
5183 getLogFolder(logFolder);
5184 Assert(logFolder.length());
5185 if (RTDirExists(logFolder.c_str()))
5186 {
5187 /* Delete all VBox.log[.N] files from the Logs folder
5188 * (this must be in sync with the rotation logic in
5189 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5190 * files that may have been created by the GUI. */
5191 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5192 RTFileDelete(log.c_str());
5193 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5194 RTFileDelete(log.c_str());
5195 for (ULONG i = uLogHistoryCount; i > 0; i--)
5196 {
5197 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5198 RTFileDelete(log.c_str());
5199 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5200 RTFileDelete(log.c_str());
5201 }
5202 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5203 RTFileDelete(log.c_str());
5204#if defined(RT_OS_WINDOWS)
5205 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5206 RTFileDelete(log.c_str());
5207 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5208 RTFileDelete(log.c_str());
5209#endif
5210
5211 RTDirRemove(logFolder.c_str());
5212 }
5213
5214 /* delete the Snapshots folder, nothing important should be left
5215 * there (we don't check for errors because the user might have
5216 * some private files there that we don't want to delete) */
5217 Utf8Str strFullSnapshotFolder;
5218 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5219 Assert(!strFullSnapshotFolder.isEmpty());
5220 if (RTDirExists(strFullSnapshotFolder.c_str()))
5221 RTDirRemove(strFullSnapshotFolder.c_str());
5222
5223 // delete the directory that contains the settings file, but only
5224 // if it matches the VM name
5225 Utf8Str settingsDir;
5226 if (i_isInOwnDir(&settingsDir))
5227 RTDirRemove(settingsDir.c_str());
5228 }
5229
5230 alock.release();
5231
5232 mParent->i_saveModifiedRegistries();
5233 }
5234 catch (HRESULT aRC) { rc = aRC; }
5235
5236 task.m_pProgress->i_notifyComplete(rc);
5237
5238 LogFlowThisFuncLeave();
5239}
5240
5241HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5242{
5243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5244
5245 HRESULT rc = i_checkStateDependency(MutableStateDep);
5246 if (FAILED(rc)) return rc;
5247
5248 if (mData->mRegistered)
5249 return setError(VBOX_E_INVALID_VM_STATE,
5250 tr("Cannot delete settings of a registered machine"));
5251
5252 // collect files to delete
5253 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5254 // machine config file
5255 if (mData->pMachineConfigFile->fileExists())
5256 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5257 // backup of machine config file
5258 Utf8Str strTmp(mData->m_strConfigFileFull);
5259 strTmp.append("-prev");
5260 if (RTFileExists(strTmp.c_str()))
5261 llFilesToDelete.push_back(strTmp);
5262
5263 RTCList<ComPtr<IMedium> > llMediums;
5264 for (size_t i = 0; i < aMedia.size(); ++i)
5265 {
5266 IMedium *pIMedium(aMedia[i]);
5267 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5268 if (pMedium.isNull())
5269 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5270 SafeArray<BSTR> ids;
5271 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5272 if (FAILED(rc)) return rc;
5273 /* At this point the medium should not have any back references
5274 * anymore. If it has it is attached to another VM and *must* not
5275 * deleted. */
5276 if (ids.size() < 1)
5277 llMediums.append(pMedium);
5278 }
5279
5280 ComObjPtr<Progress> pProgress;
5281 pProgress.createObject();
5282 rc = pProgress->init(i_getVirtualBox(),
5283 static_cast<IMachine*>(this) /* aInitiator */,
5284 tr("Deleting files"),
5285 true /* fCancellable */,
5286 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5287 tr("Collecting file inventory"));
5288 if (FAILED(rc))
5289 return rc;
5290
5291 /* create and start the task on a separate thread (note that it will not
5292 * start working until we release alock) */
5293 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5294 rc = pTask->createThread();
5295 pTask = NULL;
5296 if (FAILED(rc))
5297 return rc;
5298
5299 pProgress.queryInterfaceTo(aProgress.asOutParam());
5300
5301 LogFlowFuncLeave();
5302
5303 return S_OK;
5304}
5305
5306HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5307{
5308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5309
5310 ComObjPtr<Snapshot> pSnapshot;
5311 HRESULT rc;
5312
5313 if (aNameOrId.isEmpty())
5314 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5315 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5316 else
5317 {
5318 Guid uuid(aNameOrId);
5319 if (uuid.isValid())
5320 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5321 else
5322 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5323 }
5324 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5325
5326 return rc;
5327}
5328
5329HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5330 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5331{
5332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5333
5334 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5335 if (FAILED(rc)) return rc;
5336
5337 ComObjPtr<SharedFolder> sharedFolder;
5338 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5339 if (SUCCEEDED(rc))
5340 return setError(VBOX_E_OBJECT_IN_USE,
5341 tr("Shared folder named '%s' already exists"),
5342 aName.c_str());
5343
5344 sharedFolder.createObject();
5345 rc = sharedFolder->init(i_getMachine(),
5346 aName,
5347 aHostPath,
5348 !!aWritable,
5349 !!aAutomount,
5350 aAutoMountPoint,
5351 true /* fFailOnError */);
5352 if (FAILED(rc)) return rc;
5353
5354 i_setModified(IsModified_SharedFolders);
5355 mHWData.backup();
5356 mHWData->mSharedFolders.push_back(sharedFolder);
5357
5358 /* inform the direct session if any */
5359 alock.release();
5360 i_onSharedFolderChange();
5361
5362 return S_OK;
5363}
5364
5365HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5366{
5367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5368
5369 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5370 if (FAILED(rc)) return rc;
5371
5372 ComObjPtr<SharedFolder> sharedFolder;
5373 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5374 if (FAILED(rc)) return rc;
5375
5376 i_setModified(IsModified_SharedFolders);
5377 mHWData.backup();
5378 mHWData->mSharedFolders.remove(sharedFolder);
5379
5380 /* inform the direct session if any */
5381 alock.release();
5382 i_onSharedFolderChange();
5383
5384 return S_OK;
5385}
5386
5387HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5388{
5389 /* start with No */
5390 *aCanShow = FALSE;
5391
5392 ComPtr<IInternalSessionControl> directControl;
5393 {
5394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5395
5396 if (mData->mSession.mState != SessionState_Locked)
5397 return setError(VBOX_E_INVALID_VM_STATE,
5398 tr("Machine is not locked for session (session state: %s)"),
5399 Global::stringifySessionState(mData->mSession.mState));
5400
5401 if (mData->mSession.mLockType == LockType_VM)
5402 directControl = mData->mSession.mDirectControl;
5403 }
5404
5405 /* ignore calls made after #OnSessionEnd() is called */
5406 if (!directControl)
5407 return S_OK;
5408
5409 LONG64 dummy;
5410 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5411}
5412
5413HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5414{
5415 ComPtr<IInternalSessionControl> directControl;
5416 {
5417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5418
5419 if (mData->mSession.mState != SessionState_Locked)
5420 return setError(E_FAIL,
5421 tr("Machine is not locked for session (session state: %s)"),
5422 Global::stringifySessionState(mData->mSession.mState));
5423
5424 if (mData->mSession.mLockType == LockType_VM)
5425 directControl = mData->mSession.mDirectControl;
5426 }
5427
5428 /* ignore calls made after #OnSessionEnd() is called */
5429 if (!directControl)
5430 return S_OK;
5431
5432 BOOL dummy;
5433 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5434}
5435
5436#ifdef VBOX_WITH_GUEST_PROPS
5437/**
5438 * Look up a guest property in VBoxSVC's internal structures.
5439 */
5440HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5441 com::Utf8Str &aValue,
5442 LONG64 *aTimestamp,
5443 com::Utf8Str &aFlags) const
5444{
5445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5446
5447 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5448 if (it != mHWData->mGuestProperties.end())
5449 {
5450 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5451 aValue = it->second.strValue;
5452 *aTimestamp = it->second.mTimestamp;
5453 GuestPropWriteFlags(it->second.mFlags, szFlags);
5454 aFlags = Utf8Str(szFlags);
5455 }
5456
5457 return S_OK;
5458}
5459
5460/**
5461 * Query the VM that a guest property belongs to for the property.
5462 * @returns E_ACCESSDENIED if the VM process is not available or not
5463 * currently handling queries and the lookup should then be done in
5464 * VBoxSVC.
5465 */
5466HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5467 com::Utf8Str &aValue,
5468 LONG64 *aTimestamp,
5469 com::Utf8Str &aFlags) const
5470{
5471 HRESULT rc = S_OK;
5472 Bstr bstrValue;
5473 Bstr bstrFlags;
5474
5475 ComPtr<IInternalSessionControl> directControl;
5476 {
5477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5478 if (mData->mSession.mLockType == LockType_VM)
5479 directControl = mData->mSession.mDirectControl;
5480 }
5481
5482 /* ignore calls made after #OnSessionEnd() is called */
5483 if (!directControl)
5484 rc = E_ACCESSDENIED;
5485 else
5486 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5487 0 /* accessMode */,
5488 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5489
5490 aValue = bstrValue;
5491 aFlags = bstrFlags;
5492
5493 return rc;
5494}
5495#endif // VBOX_WITH_GUEST_PROPS
5496
5497HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5498 com::Utf8Str &aValue,
5499 LONG64 *aTimestamp,
5500 com::Utf8Str &aFlags)
5501{
5502#ifndef VBOX_WITH_GUEST_PROPS
5503 ReturnComNotImplemented();
5504#else // VBOX_WITH_GUEST_PROPS
5505
5506 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5507
5508 if (rc == E_ACCESSDENIED)
5509 /* The VM is not running or the service is not (yet) accessible */
5510 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5511 return rc;
5512#endif // VBOX_WITH_GUEST_PROPS
5513}
5514
5515HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5516{
5517 LONG64 dummyTimestamp;
5518 com::Utf8Str dummyFlags;
5519 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5520 return rc;
5521
5522}
5523HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5524{
5525 com::Utf8Str dummyFlags;
5526 com::Utf8Str dummyValue;
5527 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5528 return rc;
5529}
5530
5531#ifdef VBOX_WITH_GUEST_PROPS
5532/**
5533 * Set a guest property in VBoxSVC's internal structures.
5534 */
5535HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5536 const com::Utf8Str &aFlags, bool fDelete)
5537{
5538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5539 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5540 if (FAILED(rc)) return rc;
5541
5542 try
5543 {
5544 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5545 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5546 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5547
5548 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5549 if (it == mHWData->mGuestProperties.end())
5550 {
5551 if (!fDelete)
5552 {
5553 i_setModified(IsModified_MachineData);
5554 mHWData.backupEx();
5555
5556 RTTIMESPEC time;
5557 HWData::GuestProperty prop;
5558 prop.strValue = Bstr(aValue).raw();
5559 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5560 prop.mFlags = fFlags;
5561 mHWData->mGuestProperties[aName] = prop;
5562 }
5563 }
5564 else
5565 {
5566 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5567 {
5568 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5569 }
5570 else
5571 {
5572 i_setModified(IsModified_MachineData);
5573 mHWData.backupEx();
5574
5575 /* The backupEx() operation invalidates our iterator,
5576 * so get a new one. */
5577 it = mHWData->mGuestProperties.find(aName);
5578 Assert(it != mHWData->mGuestProperties.end());
5579
5580 if (!fDelete)
5581 {
5582 RTTIMESPEC time;
5583 it->second.strValue = aValue;
5584 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5585 it->second.mFlags = fFlags;
5586 }
5587 else
5588 mHWData->mGuestProperties.erase(it);
5589 }
5590 }
5591
5592 if (SUCCEEDED(rc))
5593 {
5594 alock.release();
5595
5596 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5597 }
5598 }
5599 catch (std::bad_alloc &)
5600 {
5601 rc = E_OUTOFMEMORY;
5602 }
5603
5604 return rc;
5605}
5606
5607/**
5608 * Set a property on the VM that that property belongs to.
5609 * @returns E_ACCESSDENIED if the VM process is not available or not
5610 * currently handling queries and the setting should then be done in
5611 * VBoxSVC.
5612 */
5613HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5614 const com::Utf8Str &aFlags, bool fDelete)
5615{
5616 HRESULT rc;
5617
5618 try
5619 {
5620 ComPtr<IInternalSessionControl> directControl;
5621 {
5622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5623 if (mData->mSession.mLockType == LockType_VM)
5624 directControl = mData->mSession.mDirectControl;
5625 }
5626
5627 Bstr dummy1; /* will not be changed (setter) */
5628 Bstr dummy2; /* will not be changed (setter) */
5629 LONG64 dummy64;
5630 if (!directControl)
5631 rc = E_ACCESSDENIED;
5632 else
5633 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5634 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5635 fDelete ? 2 : 1 /* accessMode */,
5636 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5637 }
5638 catch (std::bad_alloc &)
5639 {
5640 rc = E_OUTOFMEMORY;
5641 }
5642
5643 return rc;
5644}
5645#endif // VBOX_WITH_GUEST_PROPS
5646
5647HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5648 const com::Utf8Str &aFlags)
5649{
5650#ifndef VBOX_WITH_GUEST_PROPS
5651 ReturnComNotImplemented();
5652#else // VBOX_WITH_GUEST_PROPS
5653 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5654 if (rc == E_ACCESSDENIED)
5655 /* The VM is not running or the service is not (yet) accessible */
5656 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5657 return rc;
5658#endif // VBOX_WITH_GUEST_PROPS
5659}
5660
5661HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5662{
5663 return setGuestProperty(aProperty, aValue, "");
5664}
5665
5666HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5667{
5668#ifndef VBOX_WITH_GUEST_PROPS
5669 ReturnComNotImplemented();
5670#else // VBOX_WITH_GUEST_PROPS
5671 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5672 if (rc == E_ACCESSDENIED)
5673 /* The VM is not running or the service is not (yet) accessible */
5674 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5675 return rc;
5676#endif // VBOX_WITH_GUEST_PROPS
5677}
5678
5679#ifdef VBOX_WITH_GUEST_PROPS
5680/**
5681 * Enumerate the guest properties in VBoxSVC's internal structures.
5682 */
5683HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5684 std::vector<com::Utf8Str> &aNames,
5685 std::vector<com::Utf8Str> &aValues,
5686 std::vector<LONG64> &aTimestamps,
5687 std::vector<com::Utf8Str> &aFlags)
5688{
5689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5690 Utf8Str strPatterns(aPatterns);
5691
5692 /*
5693 * Look for matching patterns and build up a list.
5694 */
5695 HWData::GuestPropertyMap propMap;
5696 for (HWData::GuestPropertyMap::const_iterator
5697 it = mHWData->mGuestProperties.begin();
5698 it != mHWData->mGuestProperties.end();
5699 ++it)
5700 {
5701 if ( strPatterns.isEmpty()
5702 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5703 RTSTR_MAX,
5704 it->first.c_str(),
5705 RTSTR_MAX,
5706 NULL)
5707 )
5708 propMap.insert(*it);
5709 }
5710
5711 alock.release();
5712
5713 /*
5714 * And build up the arrays for returning the property information.
5715 */
5716 size_t cEntries = propMap.size();
5717
5718 aNames.resize(cEntries);
5719 aValues.resize(cEntries);
5720 aTimestamps.resize(cEntries);
5721 aFlags.resize(cEntries);
5722
5723 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5724 size_t i = 0;
5725 for (HWData::GuestPropertyMap::const_iterator
5726 it = propMap.begin();
5727 it != propMap.end();
5728 ++it, ++i)
5729 {
5730 aNames[i] = it->first;
5731 aValues[i] = it->second.strValue;
5732 aTimestamps[i] = it->second.mTimestamp;
5733 GuestPropWriteFlags(it->second.mFlags, szFlags);
5734 aFlags[i] = Utf8Str(szFlags);
5735 }
5736
5737 return S_OK;
5738}
5739
5740/**
5741 * Enumerate the properties managed by a VM.
5742 * @returns E_ACCESSDENIED if the VM process is not available or not
5743 * currently handling queries and the setting should then be done in
5744 * VBoxSVC.
5745 */
5746HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5747 std::vector<com::Utf8Str> &aNames,
5748 std::vector<com::Utf8Str> &aValues,
5749 std::vector<LONG64> &aTimestamps,
5750 std::vector<com::Utf8Str> &aFlags)
5751{
5752 HRESULT rc;
5753 ComPtr<IInternalSessionControl> directControl;
5754 {
5755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5756 if (mData->mSession.mLockType == LockType_VM)
5757 directControl = mData->mSession.mDirectControl;
5758 }
5759
5760 com::SafeArray<BSTR> bNames;
5761 com::SafeArray<BSTR> bValues;
5762 com::SafeArray<LONG64> bTimestamps;
5763 com::SafeArray<BSTR> bFlags;
5764
5765 if (!directControl)
5766 rc = E_ACCESSDENIED;
5767 else
5768 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5769 ComSafeArrayAsOutParam(bNames),
5770 ComSafeArrayAsOutParam(bValues),
5771 ComSafeArrayAsOutParam(bTimestamps),
5772 ComSafeArrayAsOutParam(bFlags));
5773 size_t i;
5774 aNames.resize(bNames.size());
5775 for (i = 0; i < bNames.size(); ++i)
5776 aNames[i] = Utf8Str(bNames[i]);
5777 aValues.resize(bValues.size());
5778 for (i = 0; i < bValues.size(); ++i)
5779 aValues[i] = Utf8Str(bValues[i]);
5780 aTimestamps.resize(bTimestamps.size());
5781 for (i = 0; i < bTimestamps.size(); ++i)
5782 aTimestamps[i] = bTimestamps[i];
5783 aFlags.resize(bFlags.size());
5784 for (i = 0; i < bFlags.size(); ++i)
5785 aFlags[i] = Utf8Str(bFlags[i]);
5786
5787 return rc;
5788}
5789#endif // VBOX_WITH_GUEST_PROPS
5790HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5791 std::vector<com::Utf8Str> &aNames,
5792 std::vector<com::Utf8Str> &aValues,
5793 std::vector<LONG64> &aTimestamps,
5794 std::vector<com::Utf8Str> &aFlags)
5795{
5796#ifndef VBOX_WITH_GUEST_PROPS
5797 ReturnComNotImplemented();
5798#else // VBOX_WITH_GUEST_PROPS
5799
5800 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5801
5802 if (rc == E_ACCESSDENIED)
5803 /* The VM is not running or the service is not (yet) accessible */
5804 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5805 return rc;
5806#endif // VBOX_WITH_GUEST_PROPS
5807}
5808
5809HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5810 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5811{
5812 MediumAttachmentList atts;
5813
5814 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5815 if (FAILED(rc)) return rc;
5816
5817 aMediumAttachments.resize(atts.size());
5818 size_t i = 0;
5819 for (MediumAttachmentList::const_iterator
5820 it = atts.begin();
5821 it != atts.end();
5822 ++it, ++i)
5823 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5824
5825 return S_OK;
5826}
5827
5828HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5829 LONG aControllerPort,
5830 LONG aDevice,
5831 ComPtr<IMediumAttachment> &aAttachment)
5832{
5833 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5834 aName.c_str(), aControllerPort, aDevice));
5835
5836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5837
5838 aAttachment = NULL;
5839
5840 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5841 aName,
5842 aControllerPort,
5843 aDevice);
5844 if (pAttach.isNull())
5845 return setError(VBOX_E_OBJECT_NOT_FOUND,
5846 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5847 aDevice, aControllerPort, aName.c_str());
5848
5849 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5850
5851 return S_OK;
5852}
5853
5854
5855HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5856 StorageBus_T aConnectionType,
5857 ComPtr<IStorageController> &aController)
5858{
5859 if ( (aConnectionType <= StorageBus_Null)
5860 || (aConnectionType > StorageBus_VirtioSCSI))
5861 return setError(E_INVALIDARG,
5862 tr("Invalid connection type: %d"),
5863 aConnectionType);
5864
5865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5866
5867 HRESULT rc = i_checkStateDependency(MutableStateDep);
5868 if (FAILED(rc)) return rc;
5869
5870 /* try to find one with the name first. */
5871 ComObjPtr<StorageController> ctrl;
5872
5873 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5874 if (SUCCEEDED(rc))
5875 return setError(VBOX_E_OBJECT_IN_USE,
5876 tr("Storage controller named '%s' already exists"),
5877 aName.c_str());
5878
5879 ctrl.createObject();
5880
5881 /* get a new instance number for the storage controller */
5882 ULONG ulInstance = 0;
5883 bool fBootable = true;
5884 for (StorageControllerList::const_iterator
5885 it = mStorageControllers->begin();
5886 it != mStorageControllers->end();
5887 ++it)
5888 {
5889 if ((*it)->i_getStorageBus() == aConnectionType)
5890 {
5891 ULONG ulCurInst = (*it)->i_getInstance();
5892
5893 if (ulCurInst >= ulInstance)
5894 ulInstance = ulCurInst + 1;
5895
5896 /* Only one controller of each type can be marked as bootable. */
5897 if ((*it)->i_getBootable())
5898 fBootable = false;
5899 }
5900 }
5901
5902 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5903 if (FAILED(rc)) return rc;
5904
5905 i_setModified(IsModified_Storage);
5906 mStorageControllers.backup();
5907 mStorageControllers->push_back(ctrl);
5908
5909 ctrl.queryInterfaceTo(aController.asOutParam());
5910
5911 /* inform the direct session if any */
5912 alock.release();
5913 i_onStorageControllerChange(i_getId(), aName);
5914
5915 return S_OK;
5916}
5917
5918HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5919 ComPtr<IStorageController> &aStorageController)
5920{
5921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5922
5923 ComObjPtr<StorageController> ctrl;
5924
5925 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5926 if (SUCCEEDED(rc))
5927 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5928
5929 return rc;
5930}
5931
5932HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5933 ULONG aInstance,
5934 ComPtr<IStorageController> &aStorageController)
5935{
5936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5937
5938 for (StorageControllerList::const_iterator
5939 it = mStorageControllers->begin();
5940 it != mStorageControllers->end();
5941 ++it)
5942 {
5943 if ( (*it)->i_getStorageBus() == aConnectionType
5944 && (*it)->i_getInstance() == aInstance)
5945 {
5946 (*it).queryInterfaceTo(aStorageController.asOutParam());
5947 return S_OK;
5948 }
5949 }
5950
5951 return setError(VBOX_E_OBJECT_NOT_FOUND,
5952 tr("Could not find a storage controller with instance number '%lu'"),
5953 aInstance);
5954}
5955
5956HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5957{
5958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5959
5960 HRESULT rc = i_checkStateDependency(MutableStateDep);
5961 if (FAILED(rc)) return rc;
5962
5963 ComObjPtr<StorageController> ctrl;
5964
5965 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5966 if (SUCCEEDED(rc))
5967 {
5968 /* Ensure that only one controller of each type is marked as bootable. */
5969 if (aBootable == TRUE)
5970 {
5971 for (StorageControllerList::const_iterator
5972 it = mStorageControllers->begin();
5973 it != mStorageControllers->end();
5974 ++it)
5975 {
5976 ComObjPtr<StorageController> aCtrl = (*it);
5977
5978 if ( (aCtrl->i_getName() != aName)
5979 && aCtrl->i_getBootable() == TRUE
5980 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5981 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5982 {
5983 aCtrl->i_setBootable(FALSE);
5984 break;
5985 }
5986 }
5987 }
5988
5989 if (SUCCEEDED(rc))
5990 {
5991 ctrl->i_setBootable(aBootable);
5992 i_setModified(IsModified_Storage);
5993 }
5994 }
5995
5996 if (SUCCEEDED(rc))
5997 {
5998 /* inform the direct session if any */
5999 alock.release();
6000 i_onStorageControllerChange(i_getId(), aName);
6001 }
6002
6003 return rc;
6004}
6005
6006HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6007{
6008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6009
6010 HRESULT rc = i_checkStateDependency(MutableStateDep);
6011 if (FAILED(rc)) return rc;
6012
6013 ComObjPtr<StorageController> ctrl;
6014 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6015 if (FAILED(rc)) return rc;
6016
6017 MediumAttachmentList llDetachedAttachments;
6018 {
6019 /* find all attached devices to the appropriate storage controller and detach them all */
6020 // make a temporary list because detachDevice invalidates iterators into
6021 // mMediumAttachments
6022 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6023
6024 for (MediumAttachmentList::const_iterator
6025 it = llAttachments2.begin();
6026 it != llAttachments2.end();
6027 ++it)
6028 {
6029 MediumAttachment *pAttachTemp = *it;
6030
6031 AutoCaller localAutoCaller(pAttachTemp);
6032 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6033
6034 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6035
6036 if (pAttachTemp->i_getControllerName() == aName)
6037 {
6038 llDetachedAttachments.push_back(pAttachTemp);
6039 rc = i_detachDevice(pAttachTemp, alock, NULL);
6040 if (FAILED(rc)) return rc;
6041 }
6042 }
6043 }
6044
6045 /* send event about detached devices before removing parent controller */
6046 for (MediumAttachmentList::const_iterator
6047 it = llDetachedAttachments.begin();
6048 it != llDetachedAttachments.end();
6049 ++it)
6050 {
6051 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6052 }
6053
6054 /* We can remove it now. */
6055 i_setModified(IsModified_Storage);
6056 mStorageControllers.backup();
6057
6058 ctrl->i_unshare();
6059
6060 mStorageControllers->remove(ctrl);
6061
6062 /* inform the direct session if any */
6063 alock.release();
6064 i_onStorageControllerChange(i_getId(), aName);
6065
6066 return S_OK;
6067}
6068
6069HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6070 ComPtr<IUSBController> &aController)
6071{
6072 if ( (aType <= USBControllerType_Null)
6073 || (aType >= USBControllerType_Last))
6074 return setError(E_INVALIDARG,
6075 tr("Invalid USB controller type: %d"),
6076 aType);
6077
6078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6079
6080 HRESULT rc = i_checkStateDependency(MutableStateDep);
6081 if (FAILED(rc)) return rc;
6082
6083 /* try to find one with the same type first. */
6084 ComObjPtr<USBController> ctrl;
6085
6086 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6087 if (SUCCEEDED(rc))
6088 return setError(VBOX_E_OBJECT_IN_USE,
6089 tr("USB controller named '%s' already exists"),
6090 aName.c_str());
6091
6092 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6093 ULONG maxInstances;
6094 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6095 if (FAILED(rc))
6096 return rc;
6097
6098 ULONG cInstances = i_getUSBControllerCountByType(aType);
6099 if (cInstances >= maxInstances)
6100 return setError(E_INVALIDARG,
6101 tr("Too many USB controllers of this type"));
6102
6103 ctrl.createObject();
6104
6105 rc = ctrl->init(this, aName, aType);
6106 if (FAILED(rc)) return rc;
6107
6108 i_setModified(IsModified_USB);
6109 mUSBControllers.backup();
6110 mUSBControllers->push_back(ctrl);
6111
6112 ctrl.queryInterfaceTo(aController.asOutParam());
6113
6114 /* inform the direct session if any */
6115 alock.release();
6116 i_onUSBControllerChange();
6117
6118 return S_OK;
6119}
6120
6121HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6122{
6123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6124
6125 ComObjPtr<USBController> ctrl;
6126
6127 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6128 if (SUCCEEDED(rc))
6129 ctrl.queryInterfaceTo(aController.asOutParam());
6130
6131 return rc;
6132}
6133
6134HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6135 ULONG *aControllers)
6136{
6137 if ( (aType <= USBControllerType_Null)
6138 || (aType >= USBControllerType_Last))
6139 return setError(E_INVALIDARG,
6140 tr("Invalid USB controller type: %d"),
6141 aType);
6142
6143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6144
6145 ComObjPtr<USBController> ctrl;
6146
6147 *aControllers = i_getUSBControllerCountByType(aType);
6148
6149 return S_OK;
6150}
6151
6152HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6153{
6154
6155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6156
6157 HRESULT rc = i_checkStateDependency(MutableStateDep);
6158 if (FAILED(rc)) return rc;
6159
6160 ComObjPtr<USBController> ctrl;
6161 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6162 if (FAILED(rc)) return rc;
6163
6164 i_setModified(IsModified_USB);
6165 mUSBControllers.backup();
6166
6167 ctrl->i_unshare();
6168
6169 mUSBControllers->remove(ctrl);
6170
6171 /* inform the direct session if any */
6172 alock.release();
6173 i_onUSBControllerChange();
6174
6175 return S_OK;
6176}
6177
6178HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6179 ULONG *aOriginX,
6180 ULONG *aOriginY,
6181 ULONG *aWidth,
6182 ULONG *aHeight,
6183 BOOL *aEnabled)
6184{
6185 uint32_t u32OriginX= 0;
6186 uint32_t u32OriginY= 0;
6187 uint32_t u32Width = 0;
6188 uint32_t u32Height = 0;
6189 uint16_t u16Flags = 0;
6190
6191 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6192 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6193 if (RT_FAILURE(vrc))
6194 {
6195#ifdef RT_OS_WINDOWS
6196 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6197 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6198 * So just assign fEnable to TRUE again.
6199 * The right fix would be to change GUI API wrappers to make sure that parameters
6200 * are changed only if API succeeds.
6201 */
6202 *aEnabled = TRUE;
6203#endif
6204 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6205 tr("Saved guest size is not available (%Rrc)"),
6206 vrc);
6207 }
6208
6209 *aOriginX = u32OriginX;
6210 *aOriginY = u32OriginY;
6211 *aWidth = u32Width;
6212 *aHeight = u32Height;
6213 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6214
6215 return S_OK;
6216}
6217
6218HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6219 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6220{
6221 if (aScreenId != 0)
6222 return E_NOTIMPL;
6223
6224 if ( aBitmapFormat != BitmapFormat_BGR0
6225 && aBitmapFormat != BitmapFormat_BGRA
6226 && aBitmapFormat != BitmapFormat_RGBA
6227 && aBitmapFormat != BitmapFormat_PNG)
6228 return setError(E_NOTIMPL,
6229 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6230
6231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6232
6233 uint8_t *pu8Data = NULL;
6234 uint32_t cbData = 0;
6235 uint32_t u32Width = 0;
6236 uint32_t u32Height = 0;
6237
6238 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6239
6240 if (RT_FAILURE(vrc))
6241 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6242 tr("Saved thumbnail data is not available (%Rrc)"),
6243 vrc);
6244
6245 HRESULT hr = S_OK;
6246
6247 *aWidth = u32Width;
6248 *aHeight = u32Height;
6249
6250 if (cbData > 0)
6251 {
6252 /* Convert pixels to the format expected by the API caller. */
6253 if (aBitmapFormat == BitmapFormat_BGR0)
6254 {
6255 /* [0] B, [1] G, [2] R, [3] 0. */
6256 aData.resize(cbData);
6257 memcpy(&aData.front(), pu8Data, cbData);
6258 }
6259 else if (aBitmapFormat == BitmapFormat_BGRA)
6260 {
6261 /* [0] B, [1] G, [2] R, [3] A. */
6262 aData.resize(cbData);
6263 for (uint32_t i = 0; i < cbData; i += 4)
6264 {
6265 aData[i] = pu8Data[i];
6266 aData[i + 1] = pu8Data[i + 1];
6267 aData[i + 2] = pu8Data[i + 2];
6268 aData[i + 3] = 0xff;
6269 }
6270 }
6271 else if (aBitmapFormat == BitmapFormat_RGBA)
6272 {
6273 /* [0] R, [1] G, [2] B, [3] A. */
6274 aData.resize(cbData);
6275 for (uint32_t i = 0; i < cbData; i += 4)
6276 {
6277 aData[i] = pu8Data[i + 2];
6278 aData[i + 1] = pu8Data[i + 1];
6279 aData[i + 2] = pu8Data[i];
6280 aData[i + 3] = 0xff;
6281 }
6282 }
6283 else if (aBitmapFormat == BitmapFormat_PNG)
6284 {
6285 uint8_t *pu8PNG = NULL;
6286 uint32_t cbPNG = 0;
6287 uint32_t cxPNG = 0;
6288 uint32_t cyPNG = 0;
6289
6290 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6291
6292 if (RT_SUCCESS(vrc))
6293 {
6294 aData.resize(cbPNG);
6295 if (cbPNG)
6296 memcpy(&aData.front(), pu8PNG, cbPNG);
6297 }
6298 else
6299 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6300 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6301 vrc);
6302
6303 RTMemFree(pu8PNG);
6304 }
6305 }
6306
6307 freeSavedDisplayScreenshot(pu8Data);
6308
6309 return hr;
6310}
6311
6312HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6313 ULONG *aWidth,
6314 ULONG *aHeight,
6315 std::vector<BitmapFormat_T> &aBitmapFormats)
6316{
6317 if (aScreenId != 0)
6318 return E_NOTIMPL;
6319
6320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6321
6322 uint8_t *pu8Data = NULL;
6323 uint32_t cbData = 0;
6324 uint32_t u32Width = 0;
6325 uint32_t u32Height = 0;
6326
6327 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6328
6329 if (RT_FAILURE(vrc))
6330 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6331 tr("Saved screenshot data is not available (%Rrc)"),
6332 vrc);
6333
6334 *aWidth = u32Width;
6335 *aHeight = u32Height;
6336 aBitmapFormats.resize(1);
6337 aBitmapFormats[0] = BitmapFormat_PNG;
6338
6339 freeSavedDisplayScreenshot(pu8Data);
6340
6341 return S_OK;
6342}
6343
6344HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6345 BitmapFormat_T aBitmapFormat,
6346 ULONG *aWidth,
6347 ULONG *aHeight,
6348 std::vector<BYTE> &aData)
6349{
6350 if (aScreenId != 0)
6351 return E_NOTIMPL;
6352
6353 if (aBitmapFormat != BitmapFormat_PNG)
6354 return E_NOTIMPL;
6355
6356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6357
6358 uint8_t *pu8Data = NULL;
6359 uint32_t cbData = 0;
6360 uint32_t u32Width = 0;
6361 uint32_t u32Height = 0;
6362
6363 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6364
6365 if (RT_FAILURE(vrc))
6366 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6367 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6368 vrc);
6369
6370 *aWidth = u32Width;
6371 *aHeight = u32Height;
6372
6373 aData.resize(cbData);
6374 if (cbData)
6375 memcpy(&aData.front(), pu8Data, cbData);
6376
6377 freeSavedDisplayScreenshot(pu8Data);
6378
6379 return S_OK;
6380}
6381
6382HRESULT Machine::hotPlugCPU(ULONG aCpu)
6383{
6384 HRESULT rc = S_OK;
6385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6386
6387 if (!mHWData->mCPUHotPlugEnabled)
6388 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6389
6390 if (aCpu >= mHWData->mCPUCount)
6391 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6392
6393 if (mHWData->mCPUAttached[aCpu])
6394 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6395
6396 alock.release();
6397 rc = i_onCPUChange(aCpu, false);
6398 alock.acquire();
6399 if (FAILED(rc)) return rc;
6400
6401 i_setModified(IsModified_MachineData);
6402 mHWData.backup();
6403 mHWData->mCPUAttached[aCpu] = true;
6404
6405 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6406 if (Global::IsOnline(mData->mMachineState))
6407 i_saveSettings(NULL);
6408
6409 return S_OK;
6410}
6411
6412HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6413{
6414 HRESULT rc = S_OK;
6415
6416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6417
6418 if (!mHWData->mCPUHotPlugEnabled)
6419 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6420
6421 if (aCpu >= SchemaDefs::MaxCPUCount)
6422 return setError(E_INVALIDARG,
6423 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6424 SchemaDefs::MaxCPUCount);
6425
6426 if (!mHWData->mCPUAttached[aCpu])
6427 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6428
6429 /* CPU 0 can't be detached */
6430 if (aCpu == 0)
6431 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6432
6433 alock.release();
6434 rc = i_onCPUChange(aCpu, true);
6435 alock.acquire();
6436 if (FAILED(rc)) return rc;
6437
6438 i_setModified(IsModified_MachineData);
6439 mHWData.backup();
6440 mHWData->mCPUAttached[aCpu] = false;
6441
6442 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6443 if (Global::IsOnline(mData->mMachineState))
6444 i_saveSettings(NULL);
6445
6446 return S_OK;
6447}
6448
6449HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6450{
6451 *aAttached = false;
6452
6453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6454
6455 /* If hotplug is enabled the CPU is always enabled. */
6456 if (!mHWData->mCPUHotPlugEnabled)
6457 {
6458 if (aCpu < mHWData->mCPUCount)
6459 *aAttached = true;
6460 }
6461 else
6462 {
6463 if (aCpu < SchemaDefs::MaxCPUCount)
6464 *aAttached = mHWData->mCPUAttached[aCpu];
6465 }
6466
6467 return S_OK;
6468}
6469
6470HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6471{
6472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6473
6474 Utf8Str log = i_getLogFilename(aIdx);
6475 if (!RTFileExists(log.c_str()))
6476 log.setNull();
6477 aFilename = log;
6478
6479 return S_OK;
6480}
6481
6482HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6483{
6484 if (aSize < 0)
6485 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6486
6487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6488
6489 HRESULT rc = S_OK;
6490 Utf8Str log = i_getLogFilename(aIdx);
6491
6492 /* do not unnecessarily hold the lock while doing something which does
6493 * not need the lock and potentially takes a long time. */
6494 alock.release();
6495
6496 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6497 * keeps the SOAP reply size under 1M for the webservice (we're using
6498 * base64 encoded strings for binary data for years now, avoiding the
6499 * expansion of each byte array element to approx. 25 bytes of XML. */
6500 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6501 aData.resize(cbData);
6502
6503 RTFILE LogFile;
6504 int vrc = RTFileOpen(&LogFile, log.c_str(),
6505 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6506 if (RT_SUCCESS(vrc))
6507 {
6508 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6509 if (RT_SUCCESS(vrc))
6510 aData.resize(cbData);
6511 else
6512 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6513 tr("Could not read log file '%s' (%Rrc)"),
6514 log.c_str(), vrc);
6515 RTFileClose(LogFile);
6516 }
6517 else
6518 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6519 tr("Could not open log file '%s' (%Rrc)"),
6520 log.c_str(), vrc);
6521
6522 if (FAILED(rc))
6523 aData.resize(0);
6524
6525 return rc;
6526}
6527
6528
6529/**
6530 * Currently this method doesn't attach device to the running VM,
6531 * just makes sure it's plugged on next VM start.
6532 */
6533HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6534{
6535 // lock scope
6536 {
6537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 HRESULT rc = i_checkStateDependency(MutableStateDep);
6540 if (FAILED(rc)) return rc;
6541
6542 ChipsetType_T aChipset = ChipsetType_PIIX3;
6543 COMGETTER(ChipsetType)(&aChipset);
6544
6545 if (aChipset != ChipsetType_ICH9)
6546 {
6547 return setError(E_INVALIDARG,
6548 tr("Host PCI attachment only supported with ICH9 chipset"));
6549 }
6550
6551 // check if device with this host PCI address already attached
6552 for (HWData::PCIDeviceAssignmentList::const_iterator
6553 it = mHWData->mPCIDeviceAssignments.begin();
6554 it != mHWData->mPCIDeviceAssignments.end();
6555 ++it)
6556 {
6557 LONG iHostAddress = -1;
6558 ComPtr<PCIDeviceAttachment> pAttach;
6559 pAttach = *it;
6560 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6561 if (iHostAddress == aHostAddress)
6562 return setError(E_INVALIDARG,
6563 tr("Device with host PCI address already attached to this VM"));
6564 }
6565
6566 ComObjPtr<PCIDeviceAttachment> pda;
6567 char name[32];
6568
6569 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6570 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6571 pda.createObject();
6572 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6573 i_setModified(IsModified_MachineData);
6574 mHWData.backup();
6575 mHWData->mPCIDeviceAssignments.push_back(pda);
6576 }
6577
6578 return S_OK;
6579}
6580
6581/**
6582 * Currently this method doesn't detach device from the running VM,
6583 * just makes sure it's not plugged on next VM start.
6584 */
6585HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6586{
6587 ComObjPtr<PCIDeviceAttachment> pAttach;
6588 bool fRemoved = false;
6589 HRESULT rc;
6590
6591 // lock scope
6592 {
6593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6594
6595 rc = i_checkStateDependency(MutableStateDep);
6596 if (FAILED(rc)) return rc;
6597
6598 for (HWData::PCIDeviceAssignmentList::const_iterator
6599 it = mHWData->mPCIDeviceAssignments.begin();
6600 it != mHWData->mPCIDeviceAssignments.end();
6601 ++it)
6602 {
6603 LONG iHostAddress = -1;
6604 pAttach = *it;
6605 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6606 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6607 {
6608 i_setModified(IsModified_MachineData);
6609 mHWData.backup();
6610 mHWData->mPCIDeviceAssignments.remove(pAttach);
6611 fRemoved = true;
6612 break;
6613 }
6614 }
6615 }
6616
6617
6618 /* Fire event outside of the lock */
6619 if (fRemoved)
6620 {
6621 Assert(!pAttach.isNull());
6622 ComPtr<IEventSource> es;
6623 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6624 Assert(SUCCEEDED(rc));
6625 Bstr mid;
6626 rc = this->COMGETTER(Id)(mid.asOutParam());
6627 Assert(SUCCEEDED(rc));
6628 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6629 }
6630
6631 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6632 tr("No host PCI device %08x attached"),
6633 aHostAddress
6634 );
6635}
6636
6637HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6638{
6639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6640
6641 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6642 size_t i = 0;
6643 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6644 it = mHWData->mPCIDeviceAssignments.begin();
6645 it != mHWData->mPCIDeviceAssignments.end();
6646 ++it, ++i)
6647 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6648
6649 return S_OK;
6650}
6651
6652HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6653{
6654 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6655
6656 return S_OK;
6657}
6658
6659HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6660{
6661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6662
6663 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6664
6665 return S_OK;
6666}
6667
6668HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6669{
6670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6671 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6672 if (SUCCEEDED(hrc))
6673 {
6674 hrc = mHWData.backupEx();
6675 if (SUCCEEDED(hrc))
6676 {
6677 i_setModified(IsModified_MachineData);
6678 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6679 }
6680 }
6681 return hrc;
6682}
6683
6684HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6685{
6686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6687 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6688 return S_OK;
6689}
6690
6691HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6692{
6693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6694 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6695 if (SUCCEEDED(hrc))
6696 {
6697 hrc = mHWData.backupEx();
6698 if (SUCCEEDED(hrc))
6699 {
6700 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6701 if (SUCCEEDED(hrc))
6702 i_setModified(IsModified_MachineData);
6703 }
6704 }
6705 return hrc;
6706}
6707
6708HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6709{
6710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6711
6712 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6713
6714 return S_OK;
6715}
6716
6717HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6718{
6719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6720 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6721 if (SUCCEEDED(hrc))
6722 {
6723 hrc = mHWData.backupEx();
6724 if (SUCCEEDED(hrc))
6725 {
6726 i_setModified(IsModified_MachineData);
6727 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6728 }
6729 }
6730 return hrc;
6731}
6732
6733HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6734{
6735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6736
6737 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6738
6739 return S_OK;
6740}
6741
6742HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6743{
6744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6745
6746 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6747 if ( SUCCEEDED(hrc)
6748 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6749 {
6750 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6751 int vrc;
6752
6753 if (aAutostartEnabled)
6754 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6755 else
6756 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6757
6758 if (RT_SUCCESS(vrc))
6759 {
6760 hrc = mHWData.backupEx();
6761 if (SUCCEEDED(hrc))
6762 {
6763 i_setModified(IsModified_MachineData);
6764 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6765 }
6766 }
6767 else if (vrc == VERR_NOT_SUPPORTED)
6768 hrc = setError(VBOX_E_NOT_SUPPORTED,
6769 tr("The VM autostart feature is not supported on this platform"));
6770 else if (vrc == VERR_PATH_NOT_FOUND)
6771 hrc = setError(E_FAIL,
6772 tr("The path to the autostart database is not set"));
6773 else
6774 hrc = setError(E_UNEXPECTED,
6775 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6776 aAutostartEnabled ? "Adding" : "Removing",
6777 mUserData->s.strName.c_str(), vrc);
6778 }
6779 return hrc;
6780}
6781
6782HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6783{
6784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6785
6786 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6787
6788 return S_OK;
6789}
6790
6791HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6792{
6793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6794 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6795 if (SUCCEEDED(hrc))
6796 {
6797 hrc = mHWData.backupEx();
6798 if (SUCCEEDED(hrc))
6799 {
6800 i_setModified(IsModified_MachineData);
6801 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6802 }
6803 }
6804 return hrc;
6805}
6806
6807HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6808{
6809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6810
6811 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6812
6813 return S_OK;
6814}
6815
6816HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6817{
6818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6819 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6820 if ( SUCCEEDED(hrc)
6821 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6822 {
6823 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6824 int vrc;
6825
6826 if (aAutostopType != AutostopType_Disabled)
6827 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6828 else
6829 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6830
6831 if (RT_SUCCESS(vrc))
6832 {
6833 hrc = mHWData.backupEx();
6834 if (SUCCEEDED(hrc))
6835 {
6836 i_setModified(IsModified_MachineData);
6837 mHWData->mAutostart.enmAutostopType = aAutostopType;
6838 }
6839 }
6840 else if (vrc == VERR_NOT_SUPPORTED)
6841 hrc = setError(VBOX_E_NOT_SUPPORTED,
6842 tr("The VM autostop feature is not supported on this platform"));
6843 else if (vrc == VERR_PATH_NOT_FOUND)
6844 hrc = setError(E_FAIL,
6845 tr("The path to the autostart database is not set"));
6846 else
6847 hrc = setError(E_UNEXPECTED,
6848 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6849 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6850 mUserData->s.strName.c_str(), vrc);
6851 }
6852 return hrc;
6853}
6854
6855HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6856{
6857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6858
6859 aDefaultFrontend = mHWData->mDefaultFrontend;
6860
6861 return S_OK;
6862}
6863
6864HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6865{
6866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6867 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6868 if (SUCCEEDED(hrc))
6869 {
6870 hrc = mHWData.backupEx();
6871 if (SUCCEEDED(hrc))
6872 {
6873 i_setModified(IsModified_MachineData);
6874 mHWData->mDefaultFrontend = aDefaultFrontend;
6875 }
6876 }
6877 return hrc;
6878}
6879
6880HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6881{
6882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6883 size_t cbIcon = mUserData->s.ovIcon.size();
6884 aIcon.resize(cbIcon);
6885 if (cbIcon)
6886 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6887 return S_OK;
6888}
6889
6890HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6891{
6892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6893 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6894 if (SUCCEEDED(hrc))
6895 {
6896 i_setModified(IsModified_MachineData);
6897 mUserData.backup();
6898 size_t cbIcon = aIcon.size();
6899 mUserData->s.ovIcon.resize(cbIcon);
6900 if (cbIcon)
6901 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6902 }
6903 return hrc;
6904}
6905
6906HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6907{
6908#ifdef VBOX_WITH_USB
6909 *aUSBProxyAvailable = true;
6910#else
6911 *aUSBProxyAvailable = false;
6912#endif
6913 return S_OK;
6914}
6915
6916HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6917{
6918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6919
6920 *aVMProcessPriority = mUserData->s.enmVMPriority;
6921
6922 return S_OK;
6923}
6924
6925HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6926{
6927 RT_NOREF(aVMProcessPriority);
6928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6929 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6930 if (SUCCEEDED(hrc))
6931 {
6932 hrc = mUserData.backupEx();
6933 if (SUCCEEDED(hrc))
6934 {
6935 i_setModified(IsModified_MachineData);
6936 mUserData->s.enmVMPriority = aVMProcessPriority;
6937 }
6938 }
6939 alock.release();
6940 if (SUCCEEDED(hrc))
6941 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6942 return hrc;
6943}
6944
6945HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6946 ComPtr<IProgress> &aProgress)
6947{
6948 ComObjPtr<Progress> pP;
6949 Progress *ppP = pP;
6950 IProgress *iP = static_cast<IProgress *>(ppP);
6951 IProgress **pProgress = &iP;
6952
6953 IMachine *pTarget = aTarget;
6954
6955 /* Convert the options. */
6956 RTCList<CloneOptions_T> optList;
6957 if (aOptions.size())
6958 for (size_t i = 0; i < aOptions.size(); ++i)
6959 optList.append(aOptions[i]);
6960
6961 if (optList.contains(CloneOptions_Link))
6962 {
6963 if (!i_isSnapshotMachine())
6964 return setError(E_INVALIDARG,
6965 tr("Linked clone can only be created from a snapshot"));
6966 if (aMode != CloneMode_MachineState)
6967 return setError(E_INVALIDARG,
6968 tr("Linked clone can only be created for a single machine state"));
6969 }
6970 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6971
6972 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6973
6974 HRESULT rc = pWorker->start(pProgress);
6975
6976 pP = static_cast<Progress *>(*pProgress);
6977 pP.queryInterfaceTo(aProgress.asOutParam());
6978
6979 return rc;
6980
6981}
6982
6983HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6984 const com::Utf8Str &aType,
6985 ComPtr<IProgress> &aProgress)
6986{
6987 LogFlowThisFuncEnter();
6988
6989 ComObjPtr<Progress> ptrProgress;
6990 HRESULT hrc = ptrProgress.createObject();
6991 if (SUCCEEDED(hrc))
6992 {
6993 com::Utf8Str strDefaultPath;
6994 if (aTargetPath.isEmpty())
6995 i_calculateFullPath(".", strDefaultPath);
6996
6997 /* Initialize our worker task */
6998 MachineMoveVM *pTask = NULL;
6999 try
7000 {
7001 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7002 }
7003 catch (std::bad_alloc &)
7004 {
7005 return E_OUTOFMEMORY;
7006 }
7007
7008 hrc = pTask->init();//no exceptions are thrown
7009
7010 if (SUCCEEDED(hrc))
7011 {
7012 hrc = pTask->createThread();
7013 pTask = NULL; /* Consumed by createThread(). */
7014 if (SUCCEEDED(hrc))
7015 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7016 else
7017 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7018 }
7019 else
7020 delete pTask;
7021 }
7022
7023 LogFlowThisFuncLeave();
7024 return hrc;
7025
7026}
7027
7028HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7029{
7030 NOREF(aProgress);
7031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7032
7033 // This check should always fail.
7034 HRESULT rc = i_checkStateDependency(MutableStateDep);
7035 if (FAILED(rc)) return rc;
7036
7037 AssertFailedReturn(E_NOTIMPL);
7038}
7039
7040HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7041{
7042 NOREF(aSavedStateFile);
7043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7044
7045 // This check should always fail.
7046 HRESULT rc = i_checkStateDependency(MutableStateDep);
7047 if (FAILED(rc)) return rc;
7048
7049 AssertFailedReturn(E_NOTIMPL);
7050}
7051
7052HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7053{
7054 NOREF(aFRemoveFile);
7055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7056
7057 // This check should always fail.
7058 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7059 if (FAILED(rc)) return rc;
7060
7061 AssertFailedReturn(E_NOTIMPL);
7062}
7063
7064// public methods for internal purposes
7065/////////////////////////////////////////////////////////////////////////////
7066
7067/**
7068 * Adds the given IsModified_* flag to the dirty flags of the machine.
7069 * This must be called either during i_loadSettings or under the machine write lock.
7070 * @param fl Flag
7071 * @param fAllowStateModification If state modifications are allowed.
7072 */
7073void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7074{
7075 mData->flModifications |= fl;
7076 if (fAllowStateModification && i_isStateModificationAllowed())
7077 mData->mCurrentStateModified = true;
7078}
7079
7080/**
7081 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7082 * care of the write locking.
7083 *
7084 * @param fModification The flag to add.
7085 * @param fAllowStateModification If state modifications are allowed.
7086 */
7087void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7088{
7089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7090 i_setModified(fModification, fAllowStateModification);
7091}
7092
7093/**
7094 * Saves the registry entry of this machine to the given configuration node.
7095 *
7096 * @param data Machine registry data.
7097 *
7098 * @note locks this object for reading.
7099 */
7100HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7101{
7102 AutoLimitedCaller autoCaller(this);
7103 AssertComRCReturnRC(autoCaller.rc());
7104
7105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7106
7107 data.uuid = mData->mUuid;
7108 data.strSettingsFile = mData->m_strConfigFile;
7109
7110 return S_OK;
7111}
7112
7113/**
7114 * Calculates the absolute path of the given path taking the directory of the
7115 * machine settings file as the current directory.
7116 *
7117 * @param strPath Path to calculate the absolute path for.
7118 * @param aResult Where to put the result (used only on success, can be the
7119 * same Utf8Str instance as passed in @a aPath).
7120 * @return IPRT result.
7121 *
7122 * @note Locks this object for reading.
7123 */
7124int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7125{
7126 AutoCaller autoCaller(this);
7127 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7128
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7132
7133 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7134
7135 strSettingsDir.stripFilename();
7136 char szFolder[RTPATH_MAX];
7137 size_t cbFolder = sizeof(szFolder);
7138 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7139 if (RT_SUCCESS(vrc))
7140 aResult = szFolder;
7141
7142 return vrc;
7143}
7144
7145/**
7146 * Copies strSource to strTarget, making it relative to the machine folder
7147 * if it is a subdirectory thereof, or simply copying it otherwise.
7148 *
7149 * @param strSource Path to evaluate and copy.
7150 * @param strTarget Buffer to receive target path.
7151 *
7152 * @note Locks this object for reading.
7153 */
7154void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7155 Utf8Str &strTarget)
7156{
7157 AutoCaller autoCaller(this);
7158 AssertComRCReturn(autoCaller.rc(), (void)0);
7159
7160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7161
7162 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7163 // use strTarget as a temporary buffer to hold the machine settings dir
7164 strTarget = mData->m_strConfigFileFull;
7165 strTarget.stripFilename();
7166 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7167 {
7168 // is relative: then append what's left
7169 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7170 // for empty paths (only possible for subdirs) use "." to avoid
7171 // triggering default settings for not present config attributes.
7172 if (strTarget.isEmpty())
7173 strTarget = ".";
7174 }
7175 else
7176 // is not relative: then overwrite
7177 strTarget = strSource;
7178}
7179
7180/**
7181 * Returns the full path to the machine's log folder in the
7182 * \a aLogFolder argument.
7183 */
7184void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7185{
7186 AutoCaller autoCaller(this);
7187 AssertComRCReturnVoid(autoCaller.rc());
7188
7189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7190
7191 char szTmp[RTPATH_MAX];
7192 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7193 if (RT_SUCCESS(vrc))
7194 {
7195 if (szTmp[0] && !mUserData.isNull())
7196 {
7197 char szTmp2[RTPATH_MAX];
7198 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7199 if (RT_SUCCESS(vrc))
7200 aLogFolder.printf("%s%c%s",
7201 szTmp2,
7202 RTPATH_DELIMITER,
7203 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7204 }
7205 else
7206 vrc = VERR_PATH_IS_RELATIVE;
7207 }
7208
7209 if (RT_FAILURE(vrc))
7210 {
7211 // fallback if VBOX_USER_LOGHOME is not set or invalid
7212 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7213 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7214 aLogFolder.append(RTPATH_DELIMITER);
7215 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7216 }
7217}
7218
7219/**
7220 * Returns the full path to the machine's log file for an given index.
7221 */
7222Utf8Str Machine::i_getLogFilename(ULONG idx)
7223{
7224 Utf8Str logFolder;
7225 getLogFolder(logFolder);
7226 Assert(logFolder.length());
7227
7228 Utf8Str log;
7229 if (idx == 0)
7230 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7231#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7232 else if (idx == 1)
7233 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7234 else
7235 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7236#else
7237 else
7238 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7239#endif
7240 return log;
7241}
7242
7243/**
7244 * Returns the full path to the machine's hardened log file.
7245 */
7246Utf8Str Machine::i_getHardeningLogFilename(void)
7247{
7248 Utf8Str strFilename;
7249 getLogFolder(strFilename);
7250 Assert(strFilename.length());
7251 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7252 return strFilename;
7253}
7254
7255/**
7256 * Returns the default NVRAM filename based on the location of the VM config.
7257 * Note that this is a relative path.
7258 */
7259Utf8Str Machine::i_getDefaultNVRAMFilename()
7260{
7261 AutoCaller autoCaller(this);
7262 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7263
7264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7265
7266 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7267 || i_isSnapshotMachine())
7268 return Utf8Str::Empty;
7269
7270 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7271 strNVRAMFilePath.stripPath();
7272 strNVRAMFilePath.stripSuffix();
7273 strNVRAMFilePath += ".nvram";
7274
7275 return strNVRAMFilePath;
7276}
7277
7278/**
7279 * Returns the NVRAM filename for a new snapshot. This intentionally works
7280 * similarly to the saved state file naming. Note that this is usually
7281 * a relative path, unless the snapshot folder is absolute.
7282 */
7283Utf8Str Machine::i_getSnapshotNVRAMFilename()
7284{
7285 AutoCaller autoCaller(this);
7286 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7287
7288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7289
7290 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7291 return Utf8Str::Empty;
7292
7293 RTTIMESPEC ts;
7294 RTTimeNow(&ts);
7295 RTTIME time;
7296 RTTimeExplode(&time, &ts);
7297
7298 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7299 strNVRAMFilePath += RTPATH_DELIMITER;
7300 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7301 time.i32Year, time.u8Month, time.u8MonthDay,
7302 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7303
7304 return strNVRAMFilePath;
7305}
7306
7307/**
7308 * Composes a unique saved state filename based on the current system time. The filename is
7309 * granular to the second so this will work so long as no more than one snapshot is taken on
7310 * a machine per second.
7311 *
7312 * Before version 4.1, we used this formula for saved state files:
7313 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7314 * which no longer works because saved state files can now be shared between the saved state of the
7315 * "saved" machine and an online snapshot, and the following would cause problems:
7316 * 1) save machine
7317 * 2) create online snapshot from that machine state --> reusing saved state file
7318 * 3) save machine again --> filename would be reused, breaking the online snapshot
7319 *
7320 * So instead we now use a timestamp.
7321 *
7322 * @param strStateFilePath
7323 */
7324
7325void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7326{
7327 AutoCaller autoCaller(this);
7328 AssertComRCReturnVoid(autoCaller.rc());
7329
7330 {
7331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7332 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7333 }
7334
7335 RTTIMESPEC ts;
7336 RTTimeNow(&ts);
7337 RTTIME time;
7338 RTTimeExplode(&time, &ts);
7339
7340 strStateFilePath += RTPATH_DELIMITER;
7341 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7342 time.i32Year, time.u8Month, time.u8MonthDay,
7343 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7344}
7345
7346/**
7347 * Returns whether at least one USB controller is present for the VM.
7348 */
7349bool Machine::i_isUSBControllerPresent()
7350{
7351 AutoCaller autoCaller(this);
7352 AssertComRCReturn(autoCaller.rc(), false);
7353
7354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7355
7356 return (mUSBControllers->size() > 0);
7357}
7358
7359
7360/**
7361 * @note Locks this object for writing, calls the client process
7362 * (inside the lock).
7363 */
7364HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7365 const Utf8Str &strFrontend,
7366 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7367 ProgressProxy *aProgress)
7368{
7369 LogFlowThisFuncEnter();
7370
7371 AssertReturn(aControl, E_FAIL);
7372 AssertReturn(aProgress, E_FAIL);
7373 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7374
7375 AutoCaller autoCaller(this);
7376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7377
7378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7379
7380 if (!mData->mRegistered)
7381 return setError(E_UNEXPECTED,
7382 tr("The machine '%s' is not registered"),
7383 mUserData->s.strName.c_str());
7384
7385 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7386
7387 /* The process started when launching a VM with separate UI/VM processes is always
7388 * the UI process, i.e. needs special handling as it won't claim the session. */
7389 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7390
7391 if (fSeparate)
7392 {
7393 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7394 return setError(VBOX_E_INVALID_OBJECT_STATE,
7395 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7396 mUserData->s.strName.c_str());
7397 }
7398 else
7399 {
7400 if ( mData->mSession.mState == SessionState_Locked
7401 || mData->mSession.mState == SessionState_Spawning
7402 || mData->mSession.mState == SessionState_Unlocking)
7403 return setError(VBOX_E_INVALID_OBJECT_STATE,
7404 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7405 mUserData->s.strName.c_str());
7406
7407 /* may not be busy */
7408 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7409 }
7410
7411 /* Hardening logging */
7412#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7413 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7414 {
7415 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7416 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7417 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7418 {
7419 Utf8Str strStartupLogDir = strHardeningLogFile;
7420 strStartupLogDir.stripFilename();
7421 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7422 file without stripping the file. */
7423 }
7424 strSupHardeningLogArg.append(strHardeningLogFile);
7425
7426 /* Remove legacy log filename to avoid confusion. */
7427 Utf8Str strOldStartupLogFile;
7428 getLogFolder(strOldStartupLogFile);
7429 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7430 RTFileDelete(strOldStartupLogFile.c_str());
7431 }
7432#else
7433 Utf8Str strSupHardeningLogArg;
7434#endif
7435
7436 Utf8Str strAppOverride;
7437#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7438 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7439#endif
7440
7441 bool fUseVBoxSDS = false;
7442 Utf8Str strCanonicalName;
7443 if (false)
7444 { }
7445#ifdef VBOX_WITH_QTGUI
7446 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7447 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7448 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7449 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7450 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7451 {
7452 strCanonicalName = "GUI/Qt";
7453 fUseVBoxSDS = true;
7454 }
7455#endif
7456#ifdef VBOX_WITH_VBOXSDL
7457 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7458 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7459 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7460 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7461 {
7462 strCanonicalName = "GUI/SDL";
7463 fUseVBoxSDS = true;
7464 }
7465#endif
7466#ifdef VBOX_WITH_HEADLESS
7467 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7468 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7469 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7470 {
7471 strCanonicalName = "headless";
7472 }
7473#endif
7474 else
7475 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7476
7477 Utf8Str idStr = mData->mUuid.toString();
7478 Utf8Str const &strMachineName = mUserData->s.strName;
7479 RTPROCESS pid = NIL_RTPROCESS;
7480
7481#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7482 RT_NOREF(fUseVBoxSDS);
7483#else
7484 DWORD idCallerSession = ~(DWORD)0;
7485 if (fUseVBoxSDS)
7486 {
7487 /*
7488 * The VBoxSDS should be used for process launching the VM with
7489 * GUI only if the caller and the VBoxSDS are in different Windows
7490 * sessions and the caller in the interactive one.
7491 */
7492 fUseVBoxSDS = false;
7493
7494 /* Get windows session of the current process. The process token used
7495 due to several reasons:
7496 1. The token is absent for the current thread except someone set it
7497 for us.
7498 2. Needs to get the id of the session where the process is started.
7499 We only need to do this once, though. */
7500 static DWORD s_idCurrentSession = ~(DWORD)0;
7501 DWORD idCurrentSession = s_idCurrentSession;
7502 if (idCurrentSession == ~(DWORD)0)
7503 {
7504 HANDLE hCurrentProcessToken = NULL;
7505 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7506 {
7507 DWORD cbIgn = 0;
7508 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7509 s_idCurrentSession = idCurrentSession;
7510 else
7511 {
7512 idCurrentSession = ~(DWORD)0;
7513 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7514 }
7515 CloseHandle(hCurrentProcessToken);
7516 }
7517 else
7518 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7519 }
7520
7521 /* get the caller's session */
7522 HRESULT hrc = CoImpersonateClient();
7523 if (SUCCEEDED(hrc))
7524 {
7525 HANDLE hCallerThreadToken;
7526 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7527 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7528 &hCallerThreadToken))
7529 {
7530 SetLastError(NO_ERROR);
7531 DWORD cbIgn = 0;
7532 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7533 {
7534 /* Only need to use SDS if the session ID differs: */
7535 if (idCurrentSession != idCallerSession)
7536 {
7537 fUseVBoxSDS = false;
7538
7539 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7540 DWORD cbTokenGroups = 0;
7541 PTOKEN_GROUPS pTokenGroups = NULL;
7542 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7543 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7544 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7545 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7546 {
7547 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7548 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7549 PSID pInteractiveSid = NULL;
7550 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7551 {
7552 /* Iterate over the groups looking for the interactive SID: */
7553 fUseVBoxSDS = false;
7554 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7555 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7556 {
7557 fUseVBoxSDS = true;
7558 break;
7559 }
7560 FreeSid(pInteractiveSid);
7561 }
7562 }
7563 else
7564 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7565 RTMemTmpFree(pTokenGroups);
7566 }
7567 }
7568 else
7569 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7570 CloseHandle(hCallerThreadToken);
7571 }
7572 else
7573 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7574 CoRevertToSelf();
7575 }
7576 else
7577 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7578 }
7579 if (fUseVBoxSDS)
7580 {
7581 /* connect to VBoxSDS */
7582 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7583 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7584 if (FAILED(rc))
7585 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7586 strMachineName.c_str());
7587
7588 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7589 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7590 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7591 service to access the files. */
7592 rc = CoSetProxyBlanket(pVBoxSDS,
7593 RPC_C_AUTHN_DEFAULT,
7594 RPC_C_AUTHZ_DEFAULT,
7595 COLE_DEFAULT_PRINCIPAL,
7596 RPC_C_AUTHN_LEVEL_DEFAULT,
7597 RPC_C_IMP_LEVEL_IMPERSONATE,
7598 NULL,
7599 EOAC_DEFAULT);
7600 if (FAILED(rc))
7601 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7602
7603 size_t const cEnvVars = aEnvironmentChanges.size();
7604 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7605 for (size_t i = 0; i < cEnvVars; i++)
7606 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7607
7608 ULONG uPid = 0;
7609 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7610 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7611 idCallerSession, &uPid);
7612 if (FAILED(rc))
7613 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7614 pid = (RTPROCESS)uPid;
7615 }
7616 else
7617#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7618 {
7619 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7620 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7621 if (RT_FAILURE(vrc))
7622 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7623 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7624 }
7625
7626 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7627 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7628
7629 if (!fSeparate)
7630 {
7631 /*
7632 * Note that we don't release the lock here before calling the client,
7633 * because it doesn't need to call us back if called with a NULL argument.
7634 * Releasing the lock here is dangerous because we didn't prepare the
7635 * launch data yet, but the client we've just started may happen to be
7636 * too fast and call LockMachine() that will fail (because of PID, etc.),
7637 * so that the Machine will never get out of the Spawning session state.
7638 */
7639
7640 /* inform the session that it will be a remote one */
7641 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7642#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7643 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7644#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7645 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7646#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7647 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7648
7649 if (FAILED(rc))
7650 {
7651 /* restore the session state */
7652 mData->mSession.mState = SessionState_Unlocked;
7653 alock.release();
7654 mParent->i_addProcessToReap(pid);
7655 /* The failure may occur w/o any error info (from RPC), so provide one */
7656 return setError(VBOX_E_VM_ERROR,
7657 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7658 }
7659
7660 /* attach launch data to the machine */
7661 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7662 mData->mSession.mRemoteControls.push_back(aControl);
7663 mData->mSession.mProgress = aProgress;
7664 mData->mSession.mPID = pid;
7665 mData->mSession.mState = SessionState_Spawning;
7666 Assert(strCanonicalName.isNotEmpty());
7667 mData->mSession.mName = strCanonicalName;
7668 }
7669 else
7670 {
7671 /* For separate UI process we declare the launch as completed instantly, as the
7672 * actual headless VM start may or may not come. No point in remembering anything
7673 * yet, as what matters for us is when the headless VM gets started. */
7674 aProgress->i_notifyComplete(S_OK);
7675 }
7676
7677 alock.release();
7678 mParent->i_addProcessToReap(pid);
7679
7680 LogFlowThisFuncLeave();
7681 return S_OK;
7682}
7683
7684/**
7685 * Returns @c true if the given session machine instance has an open direct
7686 * session (and optionally also for direct sessions which are closing) and
7687 * returns the session control machine instance if so.
7688 *
7689 * Note that when the method returns @c false, the arguments remain unchanged.
7690 *
7691 * @param aMachine Session machine object.
7692 * @param aControl Direct session control object (optional).
7693 * @param aRequireVM If true then only allow VM sessions.
7694 * @param aAllowClosing If true then additionally a session which is currently
7695 * being closed will also be allowed.
7696 *
7697 * @note locks this object for reading.
7698 */
7699bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7700 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7701 bool aRequireVM /*= false*/,
7702 bool aAllowClosing /*= false*/)
7703{
7704 AutoLimitedCaller autoCaller(this);
7705 AssertComRCReturn(autoCaller.rc(), false);
7706
7707 /* just return false for inaccessible machines */
7708 if (getObjectState().getState() != ObjectState::Ready)
7709 return false;
7710
7711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7712
7713 if ( ( mData->mSession.mState == SessionState_Locked
7714 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7715 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7716 )
7717 {
7718 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7719
7720 aMachine = mData->mSession.mMachine;
7721
7722 if (aControl != NULL)
7723 *aControl = mData->mSession.mDirectControl;
7724
7725 return true;
7726 }
7727
7728 return false;
7729}
7730
7731/**
7732 * Returns @c true if the given machine has an spawning direct session.
7733 *
7734 * @note locks this object for reading.
7735 */
7736bool Machine::i_isSessionSpawning()
7737{
7738 AutoLimitedCaller autoCaller(this);
7739 AssertComRCReturn(autoCaller.rc(), false);
7740
7741 /* just return false for inaccessible machines */
7742 if (getObjectState().getState() != ObjectState::Ready)
7743 return false;
7744
7745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7746
7747 if (mData->mSession.mState == SessionState_Spawning)
7748 return true;
7749
7750 return false;
7751}
7752
7753/**
7754 * Called from the client watcher thread to check for unexpected client process
7755 * death during Session_Spawning state (e.g. before it successfully opened a
7756 * direct session).
7757 *
7758 * On Win32 and on OS/2, this method is called only when we've got the
7759 * direct client's process termination notification, so it always returns @c
7760 * true.
7761 *
7762 * On other platforms, this method returns @c true if the client process is
7763 * terminated and @c false if it's still alive.
7764 *
7765 * @note Locks this object for writing.
7766 */
7767bool Machine::i_checkForSpawnFailure()
7768{
7769 AutoCaller autoCaller(this);
7770 if (!autoCaller.isOk())
7771 {
7772 /* nothing to do */
7773 LogFlowThisFunc(("Already uninitialized!\n"));
7774 return true;
7775 }
7776
7777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7778
7779 if (mData->mSession.mState != SessionState_Spawning)
7780 {
7781 /* nothing to do */
7782 LogFlowThisFunc(("Not spawning any more!\n"));
7783 return true;
7784 }
7785
7786 HRESULT rc = S_OK;
7787
7788 /* PID not yet initialized, skip check. */
7789 if (mData->mSession.mPID == NIL_RTPROCESS)
7790 return false;
7791
7792 RTPROCSTATUS status;
7793 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7794
7795 if (vrc != VERR_PROCESS_RUNNING)
7796 {
7797 Utf8Str strExtraInfo;
7798
7799#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7800 /* If the startup logfile exists and is of non-zero length, tell the
7801 user to look there for more details to encourage them to attach it
7802 when reporting startup issues. */
7803 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7804 uint64_t cbStartupLogFile = 0;
7805 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7806 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7807 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7808#endif
7809
7810 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7811 rc = setError(E_FAIL,
7812 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7813 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7814 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7815 rc = setError(E_FAIL,
7816 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7817 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7818 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7819 rc = setError(E_FAIL,
7820 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7821 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7822 else
7823 rc = setErrorBoth(E_FAIL, vrc,
7824 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7825 i_getName().c_str(), vrc, strExtraInfo.c_str());
7826 }
7827
7828 if (FAILED(rc))
7829 {
7830 /* Close the remote session, remove the remote control from the list
7831 * and reset session state to Closed (@note keep the code in sync with
7832 * the relevant part in LockMachine()). */
7833
7834 Assert(mData->mSession.mRemoteControls.size() == 1);
7835 if (mData->mSession.mRemoteControls.size() == 1)
7836 {
7837 ErrorInfoKeeper eik;
7838 mData->mSession.mRemoteControls.front()->Uninitialize();
7839 }
7840
7841 mData->mSession.mRemoteControls.clear();
7842 mData->mSession.mState = SessionState_Unlocked;
7843
7844 /* finalize the progress after setting the state */
7845 if (!mData->mSession.mProgress.isNull())
7846 {
7847 mData->mSession.mProgress->notifyComplete(rc);
7848 mData->mSession.mProgress.setNull();
7849 }
7850
7851 mData->mSession.mPID = NIL_RTPROCESS;
7852
7853 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7854 return true;
7855 }
7856
7857 return false;
7858}
7859
7860/**
7861 * Checks whether the machine can be registered. If so, commits and saves
7862 * all settings.
7863 *
7864 * @note Must be called from mParent's write lock. Locks this object and
7865 * children for writing.
7866 */
7867HRESULT Machine::i_prepareRegister()
7868{
7869 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7870
7871 AutoLimitedCaller autoCaller(this);
7872 AssertComRCReturnRC(autoCaller.rc());
7873
7874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7875
7876 /* wait for state dependents to drop to zero */
7877 i_ensureNoStateDependencies();
7878
7879 if (!mData->mAccessible)
7880 return setError(VBOX_E_INVALID_OBJECT_STATE,
7881 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7882 mUserData->s.strName.c_str(),
7883 mData->mUuid.toString().c_str());
7884
7885 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7886
7887 if (mData->mRegistered)
7888 return setError(VBOX_E_INVALID_OBJECT_STATE,
7889 tr("The machine '%s' with UUID {%s} is already registered"),
7890 mUserData->s.strName.c_str(),
7891 mData->mUuid.toString().c_str());
7892
7893 HRESULT rc = S_OK;
7894
7895 // Ensure the settings are saved. If we are going to be registered and
7896 // no config file exists yet, create it by calling i_saveSettings() too.
7897 if ( (mData->flModifications)
7898 || (!mData->pMachineConfigFile->fileExists())
7899 )
7900 {
7901 rc = i_saveSettings(NULL);
7902 // no need to check whether VirtualBox.xml needs saving too since
7903 // we can't have a machine XML file rename pending
7904 if (FAILED(rc)) return rc;
7905 }
7906
7907 /* more config checking goes here */
7908
7909 if (SUCCEEDED(rc))
7910 {
7911 /* we may have had implicit modifications we want to fix on success */
7912 i_commit();
7913
7914 mData->mRegistered = true;
7915 }
7916 else
7917 {
7918 /* we may have had implicit modifications we want to cancel on failure*/
7919 i_rollback(false /* aNotify */);
7920 }
7921
7922 return rc;
7923}
7924
7925/**
7926 * Increases the number of objects dependent on the machine state or on the
7927 * registered state. Guarantees that these two states will not change at least
7928 * until #i_releaseStateDependency() is called.
7929 *
7930 * Depending on the @a aDepType value, additional state checks may be made.
7931 * These checks will set extended error info on failure. See
7932 * #i_checkStateDependency() for more info.
7933 *
7934 * If this method returns a failure, the dependency is not added and the caller
7935 * is not allowed to rely on any particular machine state or registration state
7936 * value and may return the failed result code to the upper level.
7937 *
7938 * @param aDepType Dependency type to add.
7939 * @param aState Current machine state (NULL if not interested).
7940 * @param aRegistered Current registered state (NULL if not interested).
7941 *
7942 * @note Locks this object for writing.
7943 */
7944HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7945 MachineState_T *aState /* = NULL */,
7946 BOOL *aRegistered /* = NULL */)
7947{
7948 AutoCaller autoCaller(this);
7949 AssertComRCReturnRC(autoCaller.rc());
7950
7951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7952
7953 HRESULT rc = i_checkStateDependency(aDepType);
7954 if (FAILED(rc)) return rc;
7955
7956 {
7957 if (mData->mMachineStateChangePending != 0)
7958 {
7959 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7960 * drop to zero so don't add more. It may make sense to wait a bit
7961 * and retry before reporting an error (since the pending state
7962 * transition should be really quick) but let's just assert for
7963 * now to see if it ever happens on practice. */
7964
7965 AssertFailed();
7966
7967 return setError(E_ACCESSDENIED,
7968 tr("Machine state change is in progress. Please retry the operation later."));
7969 }
7970
7971 ++mData->mMachineStateDeps;
7972 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7973 }
7974
7975 if (aState)
7976 *aState = mData->mMachineState;
7977 if (aRegistered)
7978 *aRegistered = mData->mRegistered;
7979
7980 return S_OK;
7981}
7982
7983/**
7984 * Decreases the number of objects dependent on the machine state.
7985 * Must always complete the #i_addStateDependency() call after the state
7986 * dependency is no more necessary.
7987 */
7988void Machine::i_releaseStateDependency()
7989{
7990 AutoCaller autoCaller(this);
7991 AssertComRCReturnVoid(autoCaller.rc());
7992
7993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7994
7995 /* releaseStateDependency() w/o addStateDependency()? */
7996 AssertReturnVoid(mData->mMachineStateDeps != 0);
7997 -- mData->mMachineStateDeps;
7998
7999 if (mData->mMachineStateDeps == 0)
8000 {
8001 /* inform i_ensureNoStateDependencies() that there are no more deps */
8002 if (mData->mMachineStateChangePending != 0)
8003 {
8004 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8005 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8006 }
8007 }
8008}
8009
8010Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8011{
8012 /* start with nothing found */
8013 Utf8Str strResult("");
8014
8015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8016
8017 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8018 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8019 // found:
8020 strResult = it->second; // source is a Utf8Str
8021
8022 return strResult;
8023}
8024
8025// protected methods
8026/////////////////////////////////////////////////////////////////////////////
8027
8028/**
8029 * Performs machine state checks based on the @a aDepType value. If a check
8030 * fails, this method will set extended error info, otherwise it will return
8031 * S_OK. It is supposed, that on failure, the caller will immediately return
8032 * the return value of this method to the upper level.
8033 *
8034 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8035 *
8036 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8037 * current state of this machine object allows to change settings of the
8038 * machine (i.e. the machine is not registered, or registered but not running
8039 * and not saved). It is useful to call this method from Machine setters
8040 * before performing any change.
8041 *
8042 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8043 * as for MutableStateDep except that if the machine is saved, S_OK is also
8044 * returned. This is useful in setters which allow changing machine
8045 * properties when it is in the saved state.
8046 *
8047 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8048 * if the current state of this machine object allows to change runtime
8049 * changeable settings of the machine (i.e. the machine is not registered, or
8050 * registered but either running or not running and not saved). It is useful
8051 * to call this method from Machine setters before performing any changes to
8052 * runtime changeable settings.
8053 *
8054 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8055 * the same as for MutableOrRunningStateDep except that if the machine is
8056 * saved, S_OK is also returned. This is useful in setters which allow
8057 * changing runtime and saved state changeable machine properties.
8058 *
8059 * @param aDepType Dependency type to check.
8060 *
8061 * @note Non Machine based classes should use #i_addStateDependency() and
8062 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8063 * template.
8064 *
8065 * @note This method must be called from under this object's read or write
8066 * lock.
8067 */
8068HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8069{
8070 switch (aDepType)
8071 {
8072 case AnyStateDep:
8073 {
8074 break;
8075 }
8076 case MutableStateDep:
8077 {
8078 if ( mData->mRegistered
8079 && ( !i_isSessionMachine()
8080 || ( mData->mMachineState != MachineState_Aborted
8081 && mData->mMachineState != MachineState_Teleported
8082 && mData->mMachineState != MachineState_PoweredOff
8083 )
8084 )
8085 )
8086 return setError(VBOX_E_INVALID_VM_STATE,
8087 tr("The machine is not mutable (state is %s)"),
8088 Global::stringifyMachineState(mData->mMachineState));
8089 break;
8090 }
8091 case MutableOrSavedStateDep:
8092 {
8093 if ( mData->mRegistered
8094 && ( !i_isSessionMachine()
8095 || ( mData->mMachineState != MachineState_Aborted
8096 && mData->mMachineState != MachineState_Teleported
8097 && mData->mMachineState != MachineState_Saved
8098 && mData->mMachineState != MachineState_AbortedSaved
8099 && mData->mMachineState != MachineState_PoweredOff
8100 )
8101 )
8102 )
8103 return setError(VBOX_E_INVALID_VM_STATE,
8104 tr("The machine is not mutable or saved (state is %s)"),
8105 Global::stringifyMachineState(mData->mMachineState));
8106 break;
8107 }
8108 case MutableOrRunningStateDep:
8109 {
8110 if ( mData->mRegistered
8111 && ( !i_isSessionMachine()
8112 || ( mData->mMachineState != MachineState_Aborted
8113 && mData->mMachineState != MachineState_Teleported
8114 && mData->mMachineState != MachineState_PoweredOff
8115 && !Global::IsOnline(mData->mMachineState)
8116 )
8117 )
8118 )
8119 return setError(VBOX_E_INVALID_VM_STATE,
8120 tr("The machine is not mutable or running (state is %s)"),
8121 Global::stringifyMachineState(mData->mMachineState));
8122 break;
8123 }
8124 case MutableOrSavedOrRunningStateDep:
8125 {
8126 if ( mData->mRegistered
8127 && ( !i_isSessionMachine()
8128 || ( mData->mMachineState != MachineState_Aborted
8129 && mData->mMachineState != MachineState_Teleported
8130 && mData->mMachineState != MachineState_Saved
8131 && mData->mMachineState != MachineState_AbortedSaved
8132 && mData->mMachineState != MachineState_PoweredOff
8133 && !Global::IsOnline(mData->mMachineState)
8134 )
8135 )
8136 )
8137 return setError(VBOX_E_INVALID_VM_STATE,
8138 tr("The machine is not mutable, saved or running (state is %s)"),
8139 Global::stringifyMachineState(mData->mMachineState));
8140 break;
8141 }
8142 }
8143
8144 return S_OK;
8145}
8146
8147/**
8148 * Helper to initialize all associated child objects and allocate data
8149 * structures.
8150 *
8151 * This method must be called as a part of the object's initialization procedure
8152 * (usually done in the #init() method).
8153 *
8154 * @note Must be called only from #init() or from #i_registeredInit().
8155 */
8156HRESULT Machine::initDataAndChildObjects()
8157{
8158 AutoCaller autoCaller(this);
8159 AssertComRCReturnRC(autoCaller.rc());
8160 AssertReturn( getObjectState().getState() == ObjectState::InInit
8161 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8162
8163 AssertReturn(!mData->mAccessible, E_FAIL);
8164
8165 /* allocate data structures */
8166 mSSData.allocate();
8167 mUserData.allocate();
8168 mHWData.allocate();
8169 mMediumAttachments.allocate();
8170 mStorageControllers.allocate();
8171 mUSBControllers.allocate();
8172
8173 /* initialize mOSTypeId */
8174 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8175
8176/** @todo r=bird: init() methods never fails, right? Why don't we make them
8177 * return void then! */
8178
8179 /* create associated BIOS settings object */
8180 unconst(mBIOSSettings).createObject();
8181 mBIOSSettings->init(this);
8182
8183 /* create associated trusted platform module object */
8184 unconst(mTrustedPlatformModule).createObject();
8185 mTrustedPlatformModule->init(this);
8186
8187 /* create associated NVRAM store object */
8188 unconst(mNvramStore).createObject();
8189 mNvramStore->init(this);
8190
8191 /* create associated record settings object */
8192 unconst(mRecordingSettings).createObject();
8193 mRecordingSettings->init(this);
8194
8195 /* create the graphics adapter object (always present) */
8196 unconst(mGraphicsAdapter).createObject();
8197 mGraphicsAdapter->init(this);
8198
8199 /* create an associated VRDE object (default is disabled) */
8200 unconst(mVRDEServer).createObject();
8201 mVRDEServer->init(this);
8202
8203 /* create associated serial port objects */
8204 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8205 {
8206 unconst(mSerialPorts[slot]).createObject();
8207 mSerialPorts[slot]->init(this, slot);
8208 }
8209
8210 /* create associated parallel port objects */
8211 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8212 {
8213 unconst(mParallelPorts[slot]).createObject();
8214 mParallelPorts[slot]->init(this, slot);
8215 }
8216
8217 /* create the audio adapter object (always present, default is disabled) */
8218 unconst(mAudioAdapter).createObject();
8219 mAudioAdapter->init(this);
8220
8221 /* create the USB device filters object (always present) */
8222 unconst(mUSBDeviceFilters).createObject();
8223 mUSBDeviceFilters->init(this);
8224
8225 /* create associated network adapter objects */
8226 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8227 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8228 {
8229 unconst(mNetworkAdapters[slot]).createObject();
8230 mNetworkAdapters[slot]->init(this, slot);
8231 }
8232
8233 /* create the bandwidth control */
8234 unconst(mBandwidthControl).createObject();
8235 mBandwidthControl->init(this);
8236
8237 return S_OK;
8238}
8239
8240/**
8241 * Helper to uninitialize all associated child objects and to free all data
8242 * structures.
8243 *
8244 * This method must be called as a part of the object's uninitialization
8245 * procedure (usually done in the #uninit() method).
8246 *
8247 * @note Must be called only from #uninit() or from #i_registeredInit().
8248 */
8249void Machine::uninitDataAndChildObjects()
8250{
8251 AutoCaller autoCaller(this);
8252 AssertComRCReturnVoid(autoCaller.rc());
8253 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8254 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8255 || getObjectState().getState() == ObjectState::InUninit
8256 || getObjectState().getState() == ObjectState::Limited);
8257
8258 /* tell all our other child objects we've been uninitialized */
8259 if (mBandwidthControl)
8260 {
8261 mBandwidthControl->uninit();
8262 unconst(mBandwidthControl).setNull();
8263 }
8264
8265 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8266 {
8267 if (mNetworkAdapters[slot])
8268 {
8269 mNetworkAdapters[slot]->uninit();
8270 unconst(mNetworkAdapters[slot]).setNull();
8271 }
8272 }
8273
8274 if (mUSBDeviceFilters)
8275 {
8276 mUSBDeviceFilters->uninit();
8277 unconst(mUSBDeviceFilters).setNull();
8278 }
8279
8280 if (mAudioAdapter)
8281 {
8282 mAudioAdapter->uninit();
8283 unconst(mAudioAdapter).setNull();
8284 }
8285
8286 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8287 {
8288 if (mParallelPorts[slot])
8289 {
8290 mParallelPorts[slot]->uninit();
8291 unconst(mParallelPorts[slot]).setNull();
8292 }
8293 }
8294
8295 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8296 {
8297 if (mSerialPorts[slot])
8298 {
8299 mSerialPorts[slot]->uninit();
8300 unconst(mSerialPorts[slot]).setNull();
8301 }
8302 }
8303
8304 if (mVRDEServer)
8305 {
8306 mVRDEServer->uninit();
8307 unconst(mVRDEServer).setNull();
8308 }
8309
8310 if (mGraphicsAdapter)
8311 {
8312 mGraphicsAdapter->uninit();
8313 unconst(mGraphicsAdapter).setNull();
8314 }
8315
8316 if (mBIOSSettings)
8317 {
8318 mBIOSSettings->uninit();
8319 unconst(mBIOSSettings).setNull();
8320 }
8321
8322 if (mTrustedPlatformModule)
8323 {
8324 mTrustedPlatformModule->uninit();
8325 unconst(mTrustedPlatformModule).setNull();
8326 }
8327
8328 if (mNvramStore)
8329 {
8330 mNvramStore->uninit();
8331 unconst(mNvramStore).setNull();
8332 }
8333
8334 if (mRecordingSettings)
8335 {
8336 mRecordingSettings->uninit();
8337 unconst(mRecordingSettings).setNull();
8338 }
8339
8340 /* Deassociate media (only when a real Machine or a SnapshotMachine
8341 * instance is uninitialized; SessionMachine instances refer to real
8342 * Machine media). This is necessary for a clean re-initialization of
8343 * the VM after successfully re-checking the accessibility state. Note
8344 * that in case of normal Machine or SnapshotMachine uninitialization (as
8345 * a result of unregistering or deleting the snapshot), outdated media
8346 * attachments will already be uninitialized and deleted, so this
8347 * code will not affect them. */
8348 if ( !mMediumAttachments.isNull()
8349 && !i_isSessionMachine()
8350 )
8351 {
8352 for (MediumAttachmentList::const_iterator
8353 it = mMediumAttachments->begin();
8354 it != mMediumAttachments->end();
8355 ++it)
8356 {
8357 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8358 if (pMedium.isNull())
8359 continue;
8360 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8361 AssertComRC(rc);
8362 }
8363 }
8364
8365 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8366 {
8367 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8368 if (mData->mFirstSnapshot)
8369 {
8370 // snapshots tree is protected by machine write lock; strictly
8371 // this isn't necessary here since we're deleting the entire
8372 // machine, but otherwise we assert in Snapshot::uninit()
8373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8374 mData->mFirstSnapshot->uninit();
8375 mData->mFirstSnapshot.setNull();
8376 }
8377
8378 mData->mCurrentSnapshot.setNull();
8379 }
8380
8381 /* free data structures (the essential mData structure is not freed here
8382 * since it may be still in use) */
8383 mMediumAttachments.free();
8384 mStorageControllers.free();
8385 mUSBControllers.free();
8386 mHWData.free();
8387 mUserData.free();
8388 mSSData.free();
8389}
8390
8391/**
8392 * Returns a pointer to the Machine object for this machine that acts like a
8393 * parent for complex machine data objects such as shared folders, etc.
8394 *
8395 * For primary Machine objects and for SnapshotMachine objects, returns this
8396 * object's pointer itself. For SessionMachine objects, returns the peer
8397 * (primary) machine pointer.
8398 */
8399Machine *Machine::i_getMachine()
8400{
8401 if (i_isSessionMachine())
8402 return (Machine*)mPeer;
8403 return this;
8404}
8405
8406/**
8407 * Makes sure that there are no machine state dependents. If necessary, waits
8408 * for the number of dependents to drop to zero.
8409 *
8410 * Make sure this method is called from under this object's write lock to
8411 * guarantee that no new dependents may be added when this method returns
8412 * control to the caller.
8413 *
8414 * @note Locks this object for writing. The lock will be released while waiting
8415 * (if necessary).
8416 *
8417 * @warning To be used only in methods that change the machine state!
8418 */
8419void Machine::i_ensureNoStateDependencies()
8420{
8421 AssertReturnVoid(isWriteLockOnCurrentThread());
8422
8423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8424
8425 /* Wait for all state dependents if necessary */
8426 if (mData->mMachineStateDeps != 0)
8427 {
8428 /* lazy semaphore creation */
8429 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8430 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8431
8432 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8433 mData->mMachineStateDeps));
8434
8435 ++mData->mMachineStateChangePending;
8436
8437 /* reset the semaphore before waiting, the last dependent will signal
8438 * it */
8439 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8440
8441 alock.release();
8442
8443 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8444
8445 alock.acquire();
8446
8447 -- mData->mMachineStateChangePending;
8448 }
8449}
8450
8451/**
8452 * Changes the machine state and informs callbacks.
8453 *
8454 * This method is not intended to fail so it either returns S_OK or asserts (and
8455 * returns a failure).
8456 *
8457 * @note Locks this object for writing.
8458 */
8459HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8460{
8461 LogFlowThisFuncEnter();
8462 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8463 Assert(aMachineState != MachineState_Null);
8464
8465 AutoCaller autoCaller(this);
8466 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8467
8468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8469
8470 /* wait for state dependents to drop to zero */
8471 i_ensureNoStateDependencies();
8472
8473 MachineState_T const enmOldState = mData->mMachineState;
8474 if (enmOldState != aMachineState)
8475 {
8476 mData->mMachineState = aMachineState;
8477 RTTimeNow(&mData->mLastStateChange);
8478
8479#ifdef VBOX_WITH_DTRACE_R3_MAIN
8480 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8481#endif
8482 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8483 }
8484
8485 LogFlowThisFuncLeave();
8486 return S_OK;
8487}
8488
8489/**
8490 * Searches for a shared folder with the given logical name
8491 * in the collection of shared folders.
8492 *
8493 * @param aName logical name of the shared folder
8494 * @param aSharedFolder where to return the found object
8495 * @param aSetError whether to set the error info if the folder is
8496 * not found
8497 * @return
8498 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8499 *
8500 * @note
8501 * must be called from under the object's lock!
8502 */
8503HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8504 ComObjPtr<SharedFolder> &aSharedFolder,
8505 bool aSetError /* = false */)
8506{
8507 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8508 for (HWData::SharedFolderList::const_iterator
8509 it = mHWData->mSharedFolders.begin();
8510 it != mHWData->mSharedFolders.end();
8511 ++it)
8512 {
8513 SharedFolder *pSF = *it;
8514 AutoCaller autoCaller(pSF);
8515 if (pSF->i_getName() == aName)
8516 {
8517 aSharedFolder = pSF;
8518 rc = S_OK;
8519 break;
8520 }
8521 }
8522
8523 if (aSetError && FAILED(rc))
8524 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8525
8526 return rc;
8527}
8528
8529/**
8530 * Initializes all machine instance data from the given settings structures
8531 * from XML. The exception is the machine UUID which needs special handling
8532 * depending on the caller's use case, so the caller needs to set that herself.
8533 *
8534 * This gets called in several contexts during machine initialization:
8535 *
8536 * -- When machine XML exists on disk already and needs to be loaded into memory,
8537 * for example, from #i_registeredInit() to load all registered machines on
8538 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8539 * attached to the machine should be part of some media registry already.
8540 *
8541 * -- During OVF import, when a machine config has been constructed from an
8542 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8543 * ensure that the media listed as attachments in the config (which have
8544 * been imported from the OVF) receive the correct registry ID.
8545 *
8546 * -- During VM cloning.
8547 *
8548 * @param config Machine settings from XML.
8549 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8550 * for each attached medium in the config.
8551 * @return
8552 */
8553HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8554 const Guid *puuidRegistry)
8555{
8556 // copy name, description, OS type, teleporter, UTC etc.
8557 mUserData->s = config.machineUserData;
8558
8559 // look up the object by Id to check it is valid
8560 ComObjPtr<GuestOSType> pGuestOSType;
8561 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8562 if (!pGuestOSType.isNull())
8563 mUserData->s.strOsType = pGuestOSType->i_id();
8564
8565 // stateFile (optional)
8566 if (config.strStateFile.isEmpty())
8567 mSSData->strStateFilePath.setNull();
8568 else
8569 {
8570 Utf8Str stateFilePathFull(config.strStateFile);
8571 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8572 if (RT_FAILURE(vrc))
8573 return setErrorBoth(E_FAIL, vrc,
8574 tr("Invalid saved state file path '%s' (%Rrc)"),
8575 config.strStateFile.c_str(),
8576 vrc);
8577 mSSData->strStateFilePath = stateFilePathFull;
8578 }
8579
8580 // snapshot folder needs special processing so set it again
8581 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8582 if (FAILED(rc)) return rc;
8583
8584 /* Copy the extra data items (config may or may not be the same as
8585 * mData->pMachineConfigFile) if necessary. When loading the XML files
8586 * from disk they are the same, but not for OVF import. */
8587 if (mData->pMachineConfigFile != &config)
8588 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8589
8590 /* currentStateModified (optional, default is true) */
8591 mData->mCurrentStateModified = config.fCurrentStateModified;
8592
8593 mData->mLastStateChange = config.timeLastStateChange;
8594
8595 /*
8596 * note: all mUserData members must be assigned prior this point because
8597 * we need to commit changes in order to let mUserData be shared by all
8598 * snapshot machine instances.
8599 */
8600 mUserData.commitCopy();
8601
8602 // machine registry, if present (must be loaded before snapshots)
8603 if (config.canHaveOwnMediaRegistry())
8604 {
8605 // determine machine folder
8606 Utf8Str strMachineFolder = i_getSettingsFileFull();
8607 strMachineFolder.stripFilename();
8608 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8609 config.mediaRegistry,
8610 strMachineFolder);
8611 if (FAILED(rc)) return rc;
8612 }
8613
8614 /* Snapshot node (optional) */
8615 size_t cRootSnapshots;
8616 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8617 {
8618 // there must be only one root snapshot
8619 Assert(cRootSnapshots == 1);
8620
8621 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8622
8623 rc = i_loadSnapshot(snap,
8624 config.uuidCurrentSnapshot,
8625 NULL); // no parent == first snapshot
8626 if (FAILED(rc)) return rc;
8627 }
8628
8629 // hardware data
8630 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8631 if (FAILED(rc)) return rc;
8632
8633 /*
8634 * NOTE: the assignment below must be the last thing to do,
8635 * otherwise it will be not possible to change the settings
8636 * somewhere in the code above because all setters will be
8637 * blocked by i_checkStateDependency(MutableStateDep).
8638 */
8639
8640 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8641 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8642 {
8643 /* no need to use i_setMachineState() during init() */
8644 mData->mMachineState = MachineState_AbortedSaved;
8645 }
8646 else if (config.fAborted)
8647 {
8648 mSSData->strStateFilePath.setNull();
8649
8650 /* no need to use i_setMachineState() during init() */
8651 mData->mMachineState = MachineState_Aborted;
8652 }
8653 else if (!mSSData->strStateFilePath.isEmpty())
8654 {
8655 /* no need to use i_setMachineState() during init() */
8656 mData->mMachineState = MachineState_Saved;
8657 }
8658
8659 // after loading settings, we are no longer different from the XML on disk
8660 mData->flModifications = 0;
8661
8662 return S_OK;
8663}
8664
8665/**
8666 * Recursively loads all snapshots starting from the given.
8667 *
8668 * @param data snapshot settings.
8669 * @param aCurSnapshotId Current snapshot ID from the settings file.
8670 * @param aParentSnapshot Parent snapshot.
8671 */
8672HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8673 const Guid &aCurSnapshotId,
8674 Snapshot *aParentSnapshot)
8675{
8676 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8677 AssertReturn(!i_isSessionMachine(), E_FAIL);
8678
8679 HRESULT rc = S_OK;
8680
8681 Utf8Str strStateFile;
8682 if (!data.strStateFile.isEmpty())
8683 {
8684 /* optional */
8685 strStateFile = data.strStateFile;
8686 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8687 if (RT_FAILURE(vrc))
8688 return setErrorBoth(E_FAIL, vrc,
8689 tr("Invalid saved state file path '%s' (%Rrc)"),
8690 strStateFile.c_str(),
8691 vrc);
8692 }
8693
8694 /* create a snapshot machine object */
8695 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8696 pSnapshotMachine.createObject();
8697 rc = pSnapshotMachine->initFromSettings(this,
8698 data.hardware,
8699 &data.debugging,
8700 &data.autostart,
8701 data.uuid.ref(),
8702 strStateFile);
8703 if (FAILED(rc)) return rc;
8704
8705 /* create a snapshot object */
8706 ComObjPtr<Snapshot> pSnapshot;
8707 pSnapshot.createObject();
8708 /* initialize the snapshot */
8709 rc = pSnapshot->init(mParent, // VirtualBox object
8710 data.uuid,
8711 data.strName,
8712 data.strDescription,
8713 data.timestamp,
8714 pSnapshotMachine,
8715 aParentSnapshot);
8716 if (FAILED(rc)) return rc;
8717
8718 /* memorize the first snapshot if necessary */
8719 if (!mData->mFirstSnapshot)
8720 mData->mFirstSnapshot = pSnapshot;
8721
8722 /* memorize the current snapshot when appropriate */
8723 if ( !mData->mCurrentSnapshot
8724 && pSnapshot->i_getId() == aCurSnapshotId
8725 )
8726 mData->mCurrentSnapshot = pSnapshot;
8727
8728 // now create the children
8729 for (settings::SnapshotsList::const_iterator
8730 it = data.llChildSnapshots.begin();
8731 it != data.llChildSnapshots.end();
8732 ++it)
8733 {
8734 const settings::Snapshot &childData = *it;
8735 // recurse
8736 rc = i_loadSnapshot(childData,
8737 aCurSnapshotId,
8738 pSnapshot); // parent = the one we created above
8739 if (FAILED(rc)) return rc;
8740 }
8741
8742 return rc;
8743}
8744
8745/**
8746 * Loads settings into mHWData.
8747 *
8748 * @param puuidRegistry Registry ID.
8749 * @param puuidSnapshot Snapshot ID
8750 * @param data Reference to the hardware settings.
8751 * @param pDbg Pointer to the debugging settings.
8752 * @param pAutostart Pointer to the autostart settings.
8753 */
8754HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8755 const Guid *puuidSnapshot,
8756 const settings::Hardware &data,
8757 const settings::Debugging *pDbg,
8758 const settings::Autostart *pAutostart)
8759{
8760 AssertReturn(!i_isSessionMachine(), E_FAIL);
8761
8762 HRESULT rc = S_OK;
8763
8764 try
8765 {
8766 ComObjPtr<GuestOSType> pGuestOSType;
8767 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8768
8769 /* The hardware version attribute (optional). */
8770 mHWData->mHWVersion = data.strVersion;
8771 mHWData->mHardwareUUID = data.uuid;
8772
8773 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8774 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8775 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8776 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8777 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8778 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8779 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8780 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8781 mHWData->mPAEEnabled = data.fPAE;
8782 mHWData->mLongMode = data.enmLongMode;
8783 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8784 mHWData->mAPIC = data.fAPIC;
8785 mHWData->mX2APIC = data.fX2APIC;
8786 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8787 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8788 mHWData->mSpecCtrl = data.fSpecCtrl;
8789 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8790 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8791 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8792 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8793 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8794 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8795 mHWData->mCPUCount = data.cCPUs;
8796 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8797 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8798 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8799 mHWData->mCpuProfile = data.strCpuProfile;
8800
8801 // cpu
8802 if (mHWData->mCPUHotPlugEnabled)
8803 {
8804 for (settings::CpuList::const_iterator
8805 it = data.llCpus.begin();
8806 it != data.llCpus.end();
8807 ++it)
8808 {
8809 const settings::Cpu &cpu = *it;
8810
8811 mHWData->mCPUAttached[cpu.ulId] = true;
8812 }
8813 }
8814
8815 // cpuid leafs
8816 for (settings::CpuIdLeafsList::const_iterator
8817 it = data.llCpuIdLeafs.begin();
8818 it != data.llCpuIdLeafs.end();
8819 ++it)
8820 {
8821 const settings::CpuIdLeaf &rLeaf= *it;
8822 if ( rLeaf.idx < UINT32_C(0x20)
8823 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8824 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8825 mHWData->mCpuIdLeafList.push_back(rLeaf);
8826 /* else: just ignore */
8827 }
8828
8829 mHWData->mMemorySize = data.ulMemorySizeMB;
8830 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8831
8832 // boot order
8833 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8834 {
8835 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8836 if (it == data.mapBootOrder.end())
8837 mHWData->mBootOrder[i] = DeviceType_Null;
8838 else
8839 mHWData->mBootOrder[i] = it->second;
8840 }
8841
8842 mHWData->mFirmwareType = data.firmwareType;
8843 mHWData->mPointingHIDType = data.pointingHIDType;
8844 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8845 mHWData->mChipsetType = data.chipsetType;
8846 mHWData->mIommuType = data.iommuType;
8847 mHWData->mParavirtProvider = data.paravirtProvider;
8848 mHWData->mParavirtDebug = data.strParavirtDebug;
8849 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8850 mHWData->mHPETEnabled = data.fHPETEnabled;
8851
8852 /* GraphicsAdapter */
8853 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8854 if (FAILED(rc)) return rc;
8855
8856 /* VRDEServer */
8857 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8858 if (FAILED(rc)) return rc;
8859
8860 /* BIOS */
8861 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8862 if (FAILED(rc)) return rc;
8863
8864 /* Trusted Platform Module */
8865 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8866 if (FAILED(rc)) return rc;
8867
8868 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8869 if (FAILED(rc)) return rc;
8870
8871 /* Recording settings */
8872 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8873 if (FAILED(rc)) return rc;
8874
8875 // Bandwidth control (must come before network adapters)
8876 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8877 if (FAILED(rc)) return rc;
8878
8879 /* USB controllers */
8880 for (settings::USBControllerList::const_iterator
8881 it = data.usbSettings.llUSBControllers.begin();
8882 it != data.usbSettings.llUSBControllers.end();
8883 ++it)
8884 {
8885 const settings::USBController &settingsCtrl = *it;
8886 ComObjPtr<USBController> newCtrl;
8887
8888 newCtrl.createObject();
8889 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8890 mUSBControllers->push_back(newCtrl);
8891 }
8892
8893 /* USB device filters */
8894 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8895 if (FAILED(rc)) return rc;
8896
8897 // network adapters (establish array size first and apply defaults, to
8898 // ensure reading the same settings as we saved, since the list skips
8899 // adapters having defaults)
8900 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8901 size_t oldCount = mNetworkAdapters.size();
8902 if (newCount > oldCount)
8903 {
8904 mNetworkAdapters.resize(newCount);
8905 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8906 {
8907 unconst(mNetworkAdapters[slot]).createObject();
8908 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8909 }
8910 }
8911 else if (newCount < oldCount)
8912 mNetworkAdapters.resize(newCount);
8913 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8914 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8915 for (settings::NetworkAdaptersList::const_iterator
8916 it = data.llNetworkAdapters.begin();
8917 it != data.llNetworkAdapters.end();
8918 ++it)
8919 {
8920 const settings::NetworkAdapter &nic = *it;
8921
8922 /* slot uniqueness is guaranteed by XML Schema */
8923 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8924 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8925 if (FAILED(rc)) return rc;
8926 }
8927
8928 // serial ports (establish defaults first, to ensure reading the same
8929 // settings as we saved, since the list skips ports having defaults)
8930 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8931 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8932 for (settings::SerialPortsList::const_iterator
8933 it = data.llSerialPorts.begin();
8934 it != data.llSerialPorts.end();
8935 ++it)
8936 {
8937 const settings::SerialPort &s = *it;
8938
8939 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8940 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8941 if (FAILED(rc)) return rc;
8942 }
8943
8944 // parallel ports (establish defaults first, to ensure reading the same
8945 // settings as we saved, since the list skips ports having defaults)
8946 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8947 mParallelPorts[i]->i_applyDefaults();
8948 for (settings::ParallelPortsList::const_iterator
8949 it = data.llParallelPorts.begin();
8950 it != data.llParallelPorts.end();
8951 ++it)
8952 {
8953 const settings::ParallelPort &p = *it;
8954
8955 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8956 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8957 if (FAILED(rc)) return rc;
8958 }
8959
8960 /* AudioAdapter */
8961 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8962 if (FAILED(rc)) return rc;
8963
8964 /* storage controllers */
8965 rc = i_loadStorageControllers(data.storage,
8966 puuidRegistry,
8967 puuidSnapshot);
8968 if (FAILED(rc)) return rc;
8969
8970 /* Shared folders */
8971 for (settings::SharedFoldersList::const_iterator
8972 it = data.llSharedFolders.begin();
8973 it != data.llSharedFolders.end();
8974 ++it)
8975 {
8976 const settings::SharedFolder &sf = *it;
8977
8978 ComObjPtr<SharedFolder> sharedFolder;
8979 /* Check for double entries. Not allowed! */
8980 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8981 if (SUCCEEDED(rc))
8982 return setError(VBOX_E_OBJECT_IN_USE,
8983 tr("Shared folder named '%s' already exists"),
8984 sf.strName.c_str());
8985
8986 /* Create the new shared folder. Don't break on error. This will be
8987 * reported when the machine starts. */
8988 sharedFolder.createObject();
8989 rc = sharedFolder->init(i_getMachine(),
8990 sf.strName,
8991 sf.strHostPath,
8992 RT_BOOL(sf.fWritable),
8993 RT_BOOL(sf.fAutoMount),
8994 sf.strAutoMountPoint,
8995 false /* fFailOnError */);
8996 if (FAILED(rc)) return rc;
8997 mHWData->mSharedFolders.push_back(sharedFolder);
8998 }
8999
9000 // Clipboard
9001 mHWData->mClipboardMode = data.clipboardMode;
9002 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9003
9004 // drag'n'drop
9005 mHWData->mDnDMode = data.dndMode;
9006
9007 // guest settings
9008 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9009
9010 // IO settings
9011 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9012 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9013
9014 // Host PCI devices
9015 for (settings::HostPCIDeviceAttachmentList::const_iterator
9016 it = data.pciAttachments.begin();
9017 it != data.pciAttachments.end();
9018 ++it)
9019 {
9020 const settings::HostPCIDeviceAttachment &hpda = *it;
9021 ComObjPtr<PCIDeviceAttachment> pda;
9022
9023 pda.createObject();
9024 pda->i_loadSettings(this, hpda);
9025 mHWData->mPCIDeviceAssignments.push_back(pda);
9026 }
9027
9028 /*
9029 * (The following isn't really real hardware, but it lives in HWData
9030 * for reasons of convenience.)
9031 */
9032
9033#ifdef VBOX_WITH_GUEST_PROPS
9034 /* Guest properties (optional) */
9035
9036 /* Only load transient guest properties for configs which have saved
9037 * state, because there shouldn't be any for powered off VMs. The same
9038 * logic applies for snapshots, as offline snapshots shouldn't have
9039 * any such properties. They confuse the code in various places.
9040 * Note: can't rely on the machine state, as it isn't set yet. */
9041 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9042 /* apologies for the hacky unconst() usage, but this needs hacking
9043 * actually inconsistent settings into consistency, otherwise there
9044 * will be some corner cases where the inconsistency survives
9045 * surprisingly long without getting fixed, especially for snapshots
9046 * as there are no config changes. */
9047 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9048 for (settings::GuestPropertiesList::iterator
9049 it = llGuestProperties.begin();
9050 it != llGuestProperties.end();
9051 /*nothing*/)
9052 {
9053 const settings::GuestProperty &prop = *it;
9054 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9055 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9056 if ( fSkipTransientGuestProperties
9057 && ( fFlags & GUEST_PROP_F_TRANSIENT
9058 || fFlags & GUEST_PROP_F_TRANSRESET))
9059 {
9060 it = llGuestProperties.erase(it);
9061 continue;
9062 }
9063 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9064 mHWData->mGuestProperties[prop.strName] = property;
9065 ++it;
9066 }
9067#endif /* VBOX_WITH_GUEST_PROPS defined */
9068
9069 rc = i_loadDebugging(pDbg);
9070 if (FAILED(rc))
9071 return rc;
9072
9073 mHWData->mAutostart = *pAutostart;
9074
9075 /* default frontend */
9076 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9077 }
9078 catch (std::bad_alloc &)
9079 {
9080 return E_OUTOFMEMORY;
9081 }
9082
9083 AssertComRC(rc);
9084 return rc;
9085}
9086
9087/**
9088 * Called from i_loadHardware() to load the debugging settings of the
9089 * machine.
9090 *
9091 * @param pDbg Pointer to the settings.
9092 */
9093HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9094{
9095 mHWData->mDebugging = *pDbg;
9096 /* no more processing currently required, this will probably change. */
9097 return S_OK;
9098}
9099
9100/**
9101 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9102 *
9103 * @param data storage settings.
9104 * @param puuidRegistry media registry ID to set media to or NULL;
9105 * see Machine::i_loadMachineDataFromSettings()
9106 * @param puuidSnapshot snapshot ID
9107 * @return
9108 */
9109HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9110 const Guid *puuidRegistry,
9111 const Guid *puuidSnapshot)
9112{
9113 AssertReturn(!i_isSessionMachine(), E_FAIL);
9114
9115 HRESULT rc = S_OK;
9116
9117 for (settings::StorageControllersList::const_iterator
9118 it = data.llStorageControllers.begin();
9119 it != data.llStorageControllers.end();
9120 ++it)
9121 {
9122 const settings::StorageController &ctlData = *it;
9123
9124 ComObjPtr<StorageController> pCtl;
9125 /* Try to find one with the name first. */
9126 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9127 if (SUCCEEDED(rc))
9128 return setError(VBOX_E_OBJECT_IN_USE,
9129 tr("Storage controller named '%s' already exists"),
9130 ctlData.strName.c_str());
9131
9132 pCtl.createObject();
9133 rc = pCtl->init(this,
9134 ctlData.strName,
9135 ctlData.storageBus,
9136 ctlData.ulInstance,
9137 ctlData.fBootable);
9138 if (FAILED(rc)) return rc;
9139
9140 mStorageControllers->push_back(pCtl);
9141
9142 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9143 if (FAILED(rc)) return rc;
9144
9145 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9146 if (FAILED(rc)) return rc;
9147
9148 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9149 if (FAILED(rc)) return rc;
9150
9151 /* Load the attached devices now. */
9152 rc = i_loadStorageDevices(pCtl,
9153 ctlData,
9154 puuidRegistry,
9155 puuidSnapshot);
9156 if (FAILED(rc)) return rc;
9157 }
9158
9159 return S_OK;
9160}
9161
9162/**
9163 * Called from i_loadStorageControllers for a controller's devices.
9164 *
9165 * @param aStorageController
9166 * @param data
9167 * @param puuidRegistry media registry ID to set media to or NULL; see
9168 * Machine::i_loadMachineDataFromSettings()
9169 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9170 * @return
9171 */
9172HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9173 const settings::StorageController &data,
9174 const Guid *puuidRegistry,
9175 const Guid *puuidSnapshot)
9176{
9177 HRESULT rc = S_OK;
9178
9179 /* paranoia: detect duplicate attachments */
9180 for (settings::AttachedDevicesList::const_iterator
9181 it = data.llAttachedDevices.begin();
9182 it != data.llAttachedDevices.end();
9183 ++it)
9184 {
9185 const settings::AttachedDevice &ad = *it;
9186
9187 for (settings::AttachedDevicesList::const_iterator it2 = it;
9188 it2 != data.llAttachedDevices.end();
9189 ++it2)
9190 {
9191 if (it == it2)
9192 continue;
9193
9194 const settings::AttachedDevice &ad2 = *it2;
9195
9196 if ( ad.lPort == ad2.lPort
9197 && ad.lDevice == ad2.lDevice)
9198 {
9199 return setError(E_FAIL,
9200 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9201 aStorageController->i_getName().c_str(),
9202 ad.lPort,
9203 ad.lDevice,
9204 mUserData->s.strName.c_str());
9205 }
9206 }
9207 }
9208
9209 for (settings::AttachedDevicesList::const_iterator
9210 it = data.llAttachedDevices.begin();
9211 it != data.llAttachedDevices.end();
9212 ++it)
9213 {
9214 const settings::AttachedDevice &dev = *it;
9215 ComObjPtr<Medium> medium;
9216
9217 switch (dev.deviceType)
9218 {
9219 case DeviceType_Floppy:
9220 case DeviceType_DVD:
9221 if (dev.strHostDriveSrc.isNotEmpty())
9222 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9223 false /* fRefresh */, medium);
9224 else
9225 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9226 dev.uuid,
9227 false /* fRefresh */,
9228 false /* aSetError */,
9229 medium);
9230 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9231 // This is not an error. The host drive or UUID might have vanished, so just go
9232 // ahead without this removeable medium attachment
9233 rc = S_OK;
9234 break;
9235
9236 case DeviceType_HardDisk:
9237 {
9238 /* find a hard disk by UUID */
9239 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9240 if (FAILED(rc))
9241 {
9242 if (i_isSnapshotMachine())
9243 {
9244 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9245 // so the user knows that the bad disk is in a snapshot somewhere
9246 com::ErrorInfo info;
9247 return setError(E_FAIL,
9248 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9249 puuidSnapshot->raw(),
9250 info.getText().raw());
9251 }
9252 else
9253 return rc;
9254 }
9255
9256 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9257
9258 if (medium->i_getType() == MediumType_Immutable)
9259 {
9260 if (i_isSnapshotMachine())
9261 return setError(E_FAIL,
9262 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9263 "of the virtual machine '%s' ('%s')"),
9264 medium->i_getLocationFull().c_str(),
9265 dev.uuid.raw(),
9266 puuidSnapshot->raw(),
9267 mUserData->s.strName.c_str(),
9268 mData->m_strConfigFileFull.c_str());
9269
9270 return setError(E_FAIL,
9271 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9272 medium->i_getLocationFull().c_str(),
9273 dev.uuid.raw(),
9274 mUserData->s.strName.c_str(),
9275 mData->m_strConfigFileFull.c_str());
9276 }
9277
9278 if (medium->i_getType() == MediumType_MultiAttach)
9279 {
9280 if (i_isSnapshotMachine())
9281 return setError(E_FAIL,
9282 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9283 "of the virtual machine '%s' ('%s')"),
9284 medium->i_getLocationFull().c_str(),
9285 dev.uuid.raw(),
9286 puuidSnapshot->raw(),
9287 mUserData->s.strName.c_str(),
9288 mData->m_strConfigFileFull.c_str());
9289
9290 return setError(E_FAIL,
9291 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9292 medium->i_getLocationFull().c_str(),
9293 dev.uuid.raw(),
9294 mUserData->s.strName.c_str(),
9295 mData->m_strConfigFileFull.c_str());
9296 }
9297
9298 if ( !i_isSnapshotMachine()
9299 && medium->i_getChildren().size() != 0
9300 )
9301 return setError(E_FAIL,
9302 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9303 "because it has %d differencing child hard disks"),
9304 medium->i_getLocationFull().c_str(),
9305 dev.uuid.raw(),
9306 mUserData->s.strName.c_str(),
9307 mData->m_strConfigFileFull.c_str(),
9308 medium->i_getChildren().size());
9309
9310 if (i_findAttachment(*mMediumAttachments.data(),
9311 medium))
9312 return setError(E_FAIL,
9313 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9314 medium->i_getLocationFull().c_str(),
9315 dev.uuid.raw(),
9316 mUserData->s.strName.c_str(),
9317 mData->m_strConfigFileFull.c_str());
9318
9319 break;
9320 }
9321
9322 default:
9323 return setError(E_FAIL,
9324 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9325 medium->i_getLocationFull().c_str(),
9326 mUserData->s.strName.c_str(),
9327 mData->m_strConfigFileFull.c_str());
9328 }
9329
9330 if (FAILED(rc))
9331 break;
9332
9333 /* Bandwidth groups are loaded at this point. */
9334 ComObjPtr<BandwidthGroup> pBwGroup;
9335
9336 if (!dev.strBwGroup.isEmpty())
9337 {
9338 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9339 if (FAILED(rc))
9340 return setError(E_FAIL,
9341 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9342 medium->i_getLocationFull().c_str(),
9343 dev.strBwGroup.c_str(),
9344 mUserData->s.strName.c_str(),
9345 mData->m_strConfigFileFull.c_str());
9346 pBwGroup->i_reference();
9347 }
9348
9349 const Utf8Str controllerName = aStorageController->i_getName();
9350 ComObjPtr<MediumAttachment> pAttachment;
9351 pAttachment.createObject();
9352 rc = pAttachment->init(this,
9353 medium,
9354 controllerName,
9355 dev.lPort,
9356 dev.lDevice,
9357 dev.deviceType,
9358 false,
9359 dev.fPassThrough,
9360 dev.fTempEject,
9361 dev.fNonRotational,
9362 dev.fDiscard,
9363 dev.fHotPluggable,
9364 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9365 if (FAILED(rc)) break;
9366
9367 /* associate the medium with this machine and snapshot */
9368 if (!medium.isNull())
9369 {
9370 AutoCaller medCaller(medium);
9371 if (FAILED(medCaller.rc())) return medCaller.rc();
9372 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9373
9374 if (i_isSnapshotMachine())
9375 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9376 else
9377 rc = medium->i_addBackReference(mData->mUuid);
9378 /* If the medium->addBackReference fails it sets an appropriate
9379 * error message, so no need to do any guesswork here. */
9380
9381 if (puuidRegistry)
9382 // caller wants registry ID to be set on all attached media (OVF import case)
9383 medium->i_addRegistry(*puuidRegistry);
9384 }
9385
9386 if (FAILED(rc))
9387 break;
9388
9389 /* back up mMediumAttachments to let registeredInit() properly rollback
9390 * on failure (= limited accessibility) */
9391 i_setModified(IsModified_Storage);
9392 mMediumAttachments.backup();
9393 mMediumAttachments->push_back(pAttachment);
9394 }
9395
9396 return rc;
9397}
9398
9399/**
9400 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9401 *
9402 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9403 * @param aSnapshot where to return the found snapshot
9404 * @param aSetError true to set extended error info on failure
9405 */
9406HRESULT Machine::i_findSnapshotById(const Guid &aId,
9407 ComObjPtr<Snapshot> &aSnapshot,
9408 bool aSetError /* = false */)
9409{
9410 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9411
9412 if (!mData->mFirstSnapshot)
9413 {
9414 if (aSetError)
9415 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9416 return E_FAIL;
9417 }
9418
9419 if (aId.isZero())
9420 aSnapshot = mData->mFirstSnapshot;
9421 else
9422 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9423
9424 if (!aSnapshot)
9425 {
9426 if (aSetError)
9427 return setError(E_FAIL,
9428 tr("Could not find a snapshot with UUID {%s}"),
9429 aId.toString().c_str());
9430 return E_FAIL;
9431 }
9432
9433 return S_OK;
9434}
9435
9436/**
9437 * Returns the snapshot with the given name or fails of no such snapshot.
9438 *
9439 * @param strName snapshot name to find
9440 * @param aSnapshot where to return the found snapshot
9441 * @param aSetError true to set extended error info on failure
9442 */
9443HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9444 ComObjPtr<Snapshot> &aSnapshot,
9445 bool aSetError /* = false */)
9446{
9447 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9448
9449 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9450
9451 if (!mData->mFirstSnapshot)
9452 {
9453 if (aSetError)
9454 return setError(VBOX_E_OBJECT_NOT_FOUND,
9455 tr("This machine does not have any snapshots"));
9456 return VBOX_E_OBJECT_NOT_FOUND;
9457 }
9458
9459 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9460
9461 if (!aSnapshot)
9462 {
9463 if (aSetError)
9464 return setError(VBOX_E_OBJECT_NOT_FOUND,
9465 tr("Could not find a snapshot named '%s'"), strName.c_str());
9466 return VBOX_E_OBJECT_NOT_FOUND;
9467 }
9468
9469 return S_OK;
9470}
9471
9472/**
9473 * Returns a storage controller object with the given name.
9474 *
9475 * @param aName storage controller name to find
9476 * @param aStorageController where to return the found storage controller
9477 * @param aSetError true to set extended error info on failure
9478 */
9479HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9480 ComObjPtr<StorageController> &aStorageController,
9481 bool aSetError /* = false */)
9482{
9483 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9484
9485 for (StorageControllerList::const_iterator
9486 it = mStorageControllers->begin();
9487 it != mStorageControllers->end();
9488 ++it)
9489 {
9490 if ((*it)->i_getName() == aName)
9491 {
9492 aStorageController = (*it);
9493 return S_OK;
9494 }
9495 }
9496
9497 if (aSetError)
9498 return setError(VBOX_E_OBJECT_NOT_FOUND,
9499 tr("Could not find a storage controller named '%s'"),
9500 aName.c_str());
9501 return VBOX_E_OBJECT_NOT_FOUND;
9502}
9503
9504/**
9505 * Returns a USB controller object with the given name.
9506 *
9507 * @param aName USB controller name to find
9508 * @param aUSBController where to return the found USB controller
9509 * @param aSetError true to set extended error info on failure
9510 */
9511HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9512 ComObjPtr<USBController> &aUSBController,
9513 bool aSetError /* = false */)
9514{
9515 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9516
9517 for (USBControllerList::const_iterator
9518 it = mUSBControllers->begin();
9519 it != mUSBControllers->end();
9520 ++it)
9521 {
9522 if ((*it)->i_getName() == aName)
9523 {
9524 aUSBController = (*it);
9525 return S_OK;
9526 }
9527 }
9528
9529 if (aSetError)
9530 return setError(VBOX_E_OBJECT_NOT_FOUND,
9531 tr("Could not find a storage controller named '%s'"),
9532 aName.c_str());
9533 return VBOX_E_OBJECT_NOT_FOUND;
9534}
9535
9536/**
9537 * Returns the number of USB controller instance of the given type.
9538 *
9539 * @param enmType USB controller type.
9540 */
9541ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9542{
9543 ULONG cCtrls = 0;
9544
9545 for (USBControllerList::const_iterator
9546 it = mUSBControllers->begin();
9547 it != mUSBControllers->end();
9548 ++it)
9549 {
9550 if ((*it)->i_getControllerType() == enmType)
9551 cCtrls++;
9552 }
9553
9554 return cCtrls;
9555}
9556
9557HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9558 MediumAttachmentList &atts)
9559{
9560 AutoCaller autoCaller(this);
9561 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9562
9563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9564
9565 for (MediumAttachmentList::const_iterator
9566 it = mMediumAttachments->begin();
9567 it != mMediumAttachments->end();
9568 ++it)
9569 {
9570 const ComObjPtr<MediumAttachment> &pAtt = *it;
9571 // should never happen, but deal with NULL pointers in the list.
9572 AssertContinue(!pAtt.isNull());
9573
9574 // getControllerName() needs caller+read lock
9575 AutoCaller autoAttCaller(pAtt);
9576 if (FAILED(autoAttCaller.rc()))
9577 {
9578 atts.clear();
9579 return autoAttCaller.rc();
9580 }
9581 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9582
9583 if (pAtt->i_getControllerName() == aName)
9584 atts.push_back(pAtt);
9585 }
9586
9587 return S_OK;
9588}
9589
9590
9591/**
9592 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9593 * file if the machine name was changed and about creating a new settings file
9594 * if this is a new machine.
9595 *
9596 * @note Must be never called directly but only from #saveSettings().
9597 */
9598HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9599{
9600 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9601
9602 HRESULT rc = S_OK;
9603
9604 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9605
9606 /// @todo need to handle primary group change, too
9607
9608 /* attempt to rename the settings file if machine name is changed */
9609 if ( mUserData->s.fNameSync
9610 && mUserData.isBackedUp()
9611 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9612 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9613 )
9614 {
9615 bool dirRenamed = false;
9616 bool fileRenamed = false;
9617
9618 Utf8Str configFile, newConfigFile;
9619 Utf8Str configFilePrev, newConfigFilePrev;
9620 Utf8Str NVRAMFile, newNVRAMFile;
9621 Utf8Str configDir, newConfigDir;
9622
9623 do
9624 {
9625 int vrc = VINF_SUCCESS;
9626
9627 Utf8Str name = mUserData.backedUpData()->s.strName;
9628 Utf8Str newName = mUserData->s.strName;
9629 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9630 if (group == "/")
9631 group.setNull();
9632 Utf8Str newGroup = mUserData->s.llGroups.front();
9633 if (newGroup == "/")
9634 newGroup.setNull();
9635
9636 configFile = mData->m_strConfigFileFull;
9637
9638 /* first, rename the directory if it matches the group and machine name */
9639 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9640 /** @todo hack, make somehow use of ComposeMachineFilename */
9641 if (mUserData->s.fDirectoryIncludesUUID)
9642 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9643 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9644 /** @todo hack, make somehow use of ComposeMachineFilename */
9645 if (mUserData->s.fDirectoryIncludesUUID)
9646 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9647 configDir = configFile;
9648 configDir.stripFilename();
9649 newConfigDir = configDir;
9650 if ( configDir.length() >= groupPlusName.length()
9651 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9652 groupPlusName.c_str()))
9653 {
9654 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9655 Utf8Str newConfigBaseDir(newConfigDir);
9656 newConfigDir.append(newGroupPlusName);
9657 /* consistency: use \ if appropriate on the platform */
9658 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9659 /* new dir and old dir cannot be equal here because of 'if'
9660 * above and because name != newName */
9661 Assert(configDir != newConfigDir);
9662 if (!fSettingsFileIsNew)
9663 {
9664 /* perform real rename only if the machine is not new */
9665 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9666 if ( vrc == VERR_FILE_NOT_FOUND
9667 || vrc == VERR_PATH_NOT_FOUND)
9668 {
9669 /* create the parent directory, then retry renaming */
9670 Utf8Str parent(newConfigDir);
9671 parent.stripFilename();
9672 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9673 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9674 }
9675 if (RT_FAILURE(vrc))
9676 {
9677 rc = setErrorBoth(E_FAIL, vrc,
9678 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9679 configDir.c_str(),
9680 newConfigDir.c_str(),
9681 vrc);
9682 break;
9683 }
9684 /* delete subdirectories which are no longer needed */
9685 Utf8Str dir(configDir);
9686 dir.stripFilename();
9687 while (dir != newConfigBaseDir && dir != ".")
9688 {
9689 vrc = RTDirRemove(dir.c_str());
9690 if (RT_FAILURE(vrc))
9691 break;
9692 dir.stripFilename();
9693 }
9694 dirRenamed = true;
9695 }
9696 }
9697
9698 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9699
9700 /* then try to rename the settings file itself */
9701 if (newConfigFile != configFile)
9702 {
9703 /* get the path to old settings file in renamed directory */
9704 Assert(mData->m_strConfigFileFull == configFile);
9705 configFile.printf("%s%c%s",
9706 newConfigDir.c_str(),
9707 RTPATH_DELIMITER,
9708 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9709 if (!fSettingsFileIsNew)
9710 {
9711 /* perform real rename only if the machine is not new */
9712 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9713 if (RT_FAILURE(vrc))
9714 {
9715 rc = setErrorBoth(E_FAIL, vrc,
9716 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9717 configFile.c_str(),
9718 newConfigFile.c_str(),
9719 vrc);
9720 break;
9721 }
9722 fileRenamed = true;
9723 configFilePrev = configFile;
9724 configFilePrev += "-prev";
9725 newConfigFilePrev = newConfigFile;
9726 newConfigFilePrev += "-prev";
9727 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9728 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9729 if (NVRAMFile.isNotEmpty())
9730 {
9731 // in the NVRAM file path, replace the old directory with the new directory
9732 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9733 {
9734 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9735 NVRAMFile = newConfigDir + strNVRAMFile;
9736 }
9737 newNVRAMFile = newConfigFile;
9738 newNVRAMFile.stripSuffix();
9739 newNVRAMFile += ".nvram";
9740 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9741 }
9742 }
9743 }
9744
9745 // update m_strConfigFileFull amd mConfigFile
9746 mData->m_strConfigFileFull = newConfigFile;
9747 // compute the relative path too
9748 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9749
9750 // store the old and new so that VirtualBox::i_saveSettings() can update
9751 // the media registry
9752 if ( mData->mRegistered
9753 && (configDir != newConfigDir || configFile != newConfigFile))
9754 {
9755 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9756
9757 if (pfNeedsGlobalSaveSettings)
9758 *pfNeedsGlobalSaveSettings = true;
9759 }
9760
9761 // in the saved state file path, replace the old directory with the new directory
9762 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9763 {
9764 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9765 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9766 }
9767 if (newNVRAMFile.isNotEmpty())
9768 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9769
9770 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9771 if (mData->mFirstSnapshot)
9772 {
9773 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9774 newConfigDir.c_str());
9775 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9776 newConfigDir.c_str());
9777 }
9778 }
9779 while (0);
9780
9781 if (FAILED(rc))
9782 {
9783 /* silently try to rename everything back */
9784 if (fileRenamed)
9785 {
9786 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9787 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9788 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9789 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9790 }
9791 if (dirRenamed)
9792 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9793 }
9794
9795 if (FAILED(rc)) return rc;
9796 }
9797
9798 if (fSettingsFileIsNew)
9799 {
9800 /* create a virgin config file */
9801 int vrc = VINF_SUCCESS;
9802
9803 /* ensure the settings directory exists */
9804 Utf8Str path(mData->m_strConfigFileFull);
9805 path.stripFilename();
9806 if (!RTDirExists(path.c_str()))
9807 {
9808 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9809 if (RT_FAILURE(vrc))
9810 {
9811 return setErrorBoth(E_FAIL, vrc,
9812 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9813 path.c_str(),
9814 vrc);
9815 }
9816 }
9817
9818 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9819 path = Utf8Str(mData->m_strConfigFileFull);
9820 RTFILE f = NIL_RTFILE;
9821 vrc = RTFileOpen(&f, path.c_str(),
9822 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9823 if (RT_FAILURE(vrc))
9824 return setErrorBoth(E_FAIL, vrc,
9825 tr("Could not create the settings file '%s' (%Rrc)"),
9826 path.c_str(),
9827 vrc);
9828 RTFileClose(f);
9829 }
9830
9831 return rc;
9832}
9833
9834/**
9835 * Saves and commits machine data, user data and hardware data.
9836 *
9837 * Note that on failure, the data remains uncommitted.
9838 *
9839 * @a aFlags may combine the following flags:
9840 *
9841 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9842 * Used when saving settings after an operation that makes them 100%
9843 * correspond to the settings from the current snapshot.
9844 * - SaveS_Force: settings will be saved without doing a deep compare of the
9845 * settings structures. This is used when this is called because snapshots
9846 * have changed to avoid the overhead of the deep compare.
9847 *
9848 * @note Must be called from under this object's write lock. Locks children for
9849 * writing.
9850 *
9851 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9852 * initialized to false and that will be set to true by this function if
9853 * the caller must invoke VirtualBox::i_saveSettings() because the global
9854 * settings have changed. This will happen if a machine rename has been
9855 * saved and the global machine and media registries will therefore need
9856 * updating.
9857 * @param aFlags Flags.
9858 */
9859HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9860 int aFlags /*= 0*/)
9861{
9862 LogFlowThisFuncEnter();
9863
9864 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9865
9866 /* make sure child objects are unable to modify the settings while we are
9867 * saving them */
9868 i_ensureNoStateDependencies();
9869
9870 AssertReturn(!i_isSnapshotMachine(),
9871 E_FAIL);
9872
9873 if (!mData->mAccessible)
9874 return setError(VBOX_E_INVALID_VM_STATE,
9875 tr("The machine is not accessible, so cannot save settings"));
9876
9877 HRESULT rc = S_OK;
9878 bool fNeedsWrite = false;
9879
9880 /* First, prepare to save settings. It will care about renaming the
9881 * settings directory and file if the machine name was changed and about
9882 * creating a new settings file if this is a new machine. */
9883 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9884 if (FAILED(rc)) return rc;
9885
9886 // keep a pointer to the current settings structures
9887 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9888 settings::MachineConfigFile *pNewConfig = NULL;
9889
9890 try
9891 {
9892 // make a fresh one to have everyone write stuff into
9893 pNewConfig = new settings::MachineConfigFile(NULL);
9894 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9895
9896 // now go and copy all the settings data from COM to the settings structures
9897 // (this calls i_saveSettings() on all the COM objects in the machine)
9898 i_copyMachineDataToSettings(*pNewConfig);
9899
9900 if (aFlags & SaveS_ResetCurStateModified)
9901 {
9902 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9903 mData->mCurrentStateModified = FALSE;
9904 fNeedsWrite = true; // always, no need to compare
9905 }
9906 else if (aFlags & SaveS_Force)
9907 {
9908 fNeedsWrite = true; // always, no need to compare
9909 }
9910 else
9911 {
9912 if (!mData->mCurrentStateModified)
9913 {
9914 // do a deep compare of the settings that we just saved with the settings
9915 // previously stored in the config file; this invokes MachineConfigFile::operator==
9916 // which does a deep compare of all the settings, which is expensive but less expensive
9917 // than writing out XML in vain
9918 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9919
9920 // could still be modified if any settings changed
9921 mData->mCurrentStateModified = fAnySettingsChanged;
9922
9923 fNeedsWrite = fAnySettingsChanged;
9924 }
9925 else
9926 fNeedsWrite = true;
9927 }
9928
9929 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9930
9931 if (fNeedsWrite)
9932 // now spit it all out!
9933 pNewConfig->write(mData->m_strConfigFileFull);
9934
9935 mData->pMachineConfigFile = pNewConfig;
9936 delete pOldConfig;
9937 i_commit();
9938
9939 // after saving settings, we are no longer different from the XML on disk
9940 mData->flModifications = 0;
9941 }
9942 catch (HRESULT err)
9943 {
9944 // we assume that error info is set by the thrower
9945 rc = err;
9946
9947 // restore old config
9948 delete pNewConfig;
9949 mData->pMachineConfigFile = pOldConfig;
9950 }
9951 catch (...)
9952 {
9953 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9954 }
9955
9956 if (fNeedsWrite)
9957 {
9958 /* Fire the data change event, even on failure (since we've already
9959 * committed all data). This is done only for SessionMachines because
9960 * mutable Machine instances are always not registered (i.e. private
9961 * to the client process that creates them) and thus don't need to
9962 * inform callbacks. */
9963 if (i_isSessionMachine())
9964 mParent->i_onMachineDataChanged(mData->mUuid);
9965 }
9966
9967 LogFlowThisFunc(("rc=%08X\n", rc));
9968 LogFlowThisFuncLeave();
9969 return rc;
9970}
9971
9972/**
9973 * Implementation for saving the machine settings into the given
9974 * settings::MachineConfigFile instance. This copies machine extradata
9975 * from the previous machine config file in the instance data, if any.
9976 *
9977 * This gets called from two locations:
9978 *
9979 * -- Machine::i_saveSettings(), during the regular XML writing;
9980 *
9981 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9982 * exported to OVF and we write the VirtualBox proprietary XML
9983 * into a <vbox:Machine> tag.
9984 *
9985 * This routine fills all the fields in there, including snapshots, *except*
9986 * for the following:
9987 *
9988 * -- fCurrentStateModified. There is some special logic associated with that.
9989 *
9990 * The caller can then call MachineConfigFile::write() or do something else
9991 * with it.
9992 *
9993 * Caller must hold the machine lock!
9994 *
9995 * This throws XML errors and HRESULT, so the caller must have a catch block!
9996 */
9997void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9998{
9999 // deep copy extradata, being extra careful with self assignment (the STL
10000 // map assignment on Mac OS X clang based Xcode isn't checking)
10001 if (&config != mData->pMachineConfigFile)
10002 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10003
10004 config.uuid = mData->mUuid;
10005
10006 // copy name, description, OS type, teleport, UTC etc.
10007 config.machineUserData = mUserData->s;
10008
10009 if ( mData->mMachineState == MachineState_Saved
10010 || mData->mMachineState == MachineState_AbortedSaved
10011 || mData->mMachineState == MachineState_Restoring
10012 // when doing certain snapshot operations we may or may not have
10013 // a saved state in the current state, so keep everything as is
10014 || ( ( mData->mMachineState == MachineState_Snapshotting
10015 || mData->mMachineState == MachineState_DeletingSnapshot
10016 || mData->mMachineState == MachineState_RestoringSnapshot)
10017 && (!mSSData->strStateFilePath.isEmpty())
10018 )
10019 )
10020 {
10021 Assert(!mSSData->strStateFilePath.isEmpty());
10022 /* try to make the file name relative to the settings file dir */
10023 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10024 }
10025 else
10026 {
10027 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10028 config.strStateFile.setNull();
10029 }
10030
10031 if (mData->mCurrentSnapshot)
10032 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10033 else
10034 config.uuidCurrentSnapshot.clear();
10035
10036 config.timeLastStateChange = mData->mLastStateChange;
10037 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10038 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10039
10040 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10041 if (FAILED(rc)) throw rc;
10042
10043 // save machine's media registry if this is VirtualBox 4.0 or later
10044 if (config.canHaveOwnMediaRegistry())
10045 {
10046 // determine machine folder
10047 Utf8Str strMachineFolder = i_getSettingsFileFull();
10048 strMachineFolder.stripFilename();
10049 mParent->i_saveMediaRegistry(config.mediaRegistry,
10050 i_getId(), // only media with registry ID == machine UUID
10051 strMachineFolder);
10052 // this throws HRESULT
10053 }
10054
10055 // save snapshots
10056 rc = i_saveAllSnapshots(config);
10057 if (FAILED(rc)) throw rc;
10058}
10059
10060/**
10061 * Saves all snapshots of the machine into the given machine config file. Called
10062 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10063 * @param config
10064 * @return
10065 */
10066HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10067{
10068 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10069
10070 HRESULT rc = S_OK;
10071
10072 try
10073 {
10074 config.llFirstSnapshot.clear();
10075
10076 if (mData->mFirstSnapshot)
10077 {
10078 // the settings use a list for "the first snapshot"
10079 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10080
10081 // get reference to the snapshot on the list and work on that
10082 // element straight in the list to avoid excessive copying later
10083 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10084 if (FAILED(rc)) throw rc;
10085 }
10086
10087// if (mType == IsSessionMachine)
10088// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10089
10090 }
10091 catch (HRESULT err)
10092 {
10093 /* we assume that error info is set by the thrower */
10094 rc = err;
10095 }
10096 catch (...)
10097 {
10098 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10099 }
10100
10101 return rc;
10102}
10103
10104/**
10105 * Saves the VM hardware configuration. It is assumed that the
10106 * given node is empty.
10107 *
10108 * @param data Reference to the settings object for the hardware config.
10109 * @param pDbg Pointer to the settings object for the debugging config
10110 * which happens to live in mHWData.
10111 * @param pAutostart Pointer to the settings object for the autostart config
10112 * which happens to live in mHWData.
10113 */
10114HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10115 settings::Autostart *pAutostart)
10116{
10117 HRESULT rc = S_OK;
10118
10119 try
10120 {
10121 /* The hardware version attribute (optional).
10122 Automatically upgrade from 1 to current default hardware version
10123 when there is no saved state. (ugly!) */
10124 if ( mHWData->mHWVersion == "1"
10125 && mSSData->strStateFilePath.isEmpty()
10126 )
10127 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10128
10129 data.strVersion = mHWData->mHWVersion;
10130 data.uuid = mHWData->mHardwareUUID;
10131
10132 // CPU
10133 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10134 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10135 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10136 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10137 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10138 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10139 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10140 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10141 data.fPAE = !!mHWData->mPAEEnabled;
10142 data.enmLongMode = mHWData->mLongMode;
10143 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10144 data.fAPIC = !!mHWData->mAPIC;
10145 data.fX2APIC = !!mHWData->mX2APIC;
10146 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10147 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10148 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10149 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10150 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10151 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10152 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10153 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10154 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10155 data.cCPUs = mHWData->mCPUCount;
10156 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10157 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10158 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10159 data.strCpuProfile = mHWData->mCpuProfile;
10160
10161 data.llCpus.clear();
10162 if (data.fCpuHotPlug)
10163 {
10164 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10165 {
10166 if (mHWData->mCPUAttached[idx])
10167 {
10168 settings::Cpu cpu;
10169 cpu.ulId = idx;
10170 data.llCpus.push_back(cpu);
10171 }
10172 }
10173 }
10174
10175 /* Standard and Extended CPUID leafs. */
10176 data.llCpuIdLeafs.clear();
10177 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10178
10179 // memory
10180 data.ulMemorySizeMB = mHWData->mMemorySize;
10181 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10182
10183 // firmware
10184 data.firmwareType = mHWData->mFirmwareType;
10185
10186 // HID
10187 data.pointingHIDType = mHWData->mPointingHIDType;
10188 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10189
10190 // chipset
10191 data.chipsetType = mHWData->mChipsetType;
10192
10193 // iommu
10194 data.iommuType = mHWData->mIommuType;
10195
10196 // paravirt
10197 data.paravirtProvider = mHWData->mParavirtProvider;
10198 data.strParavirtDebug = mHWData->mParavirtDebug;
10199
10200 // emulated USB card reader
10201 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10202
10203 // HPET
10204 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10205
10206 // boot order
10207 data.mapBootOrder.clear();
10208 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10209 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10210
10211 /* VRDEServer settings (optional) */
10212 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10213 if (FAILED(rc)) throw rc;
10214
10215 /* BIOS settings (required) */
10216 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10217 if (FAILED(rc)) throw rc;
10218
10219 /* Trusted Platform Module settings (required) */
10220 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10221 if (FAILED(rc)) throw rc;
10222
10223 /* NVRAM settings (required) */
10224 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10225 if (FAILED(rc)) throw rc;
10226
10227 /* Recording settings (required) */
10228 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10229 if (FAILED(rc)) throw rc;
10230
10231 /* GraphicsAdapter settings (required) */
10232 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10233 if (FAILED(rc)) throw rc;
10234
10235 /* USB Controller (required) */
10236 data.usbSettings.llUSBControllers.clear();
10237 for (USBControllerList::const_iterator
10238 it = mUSBControllers->begin();
10239 it != mUSBControllers->end();
10240 ++it)
10241 {
10242 ComObjPtr<USBController> ctrl = *it;
10243 settings::USBController settingsCtrl;
10244
10245 settingsCtrl.strName = ctrl->i_getName();
10246 settingsCtrl.enmType = ctrl->i_getControllerType();
10247
10248 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10249 }
10250
10251 /* USB device filters (required) */
10252 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10253 if (FAILED(rc)) throw rc;
10254
10255 /* Network adapters (required) */
10256 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10257 data.llNetworkAdapters.clear();
10258 /* Write out only the nominal number of network adapters for this
10259 * chipset type. Since Machine::commit() hasn't been called there
10260 * may be extra NIC settings in the vector. */
10261 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10262 {
10263 settings::NetworkAdapter nic;
10264 nic.ulSlot = (uint32_t)slot;
10265 /* paranoia check... must not be NULL, but must not crash either. */
10266 if (mNetworkAdapters[slot])
10267 {
10268 if (mNetworkAdapters[slot]->i_hasDefaults())
10269 continue;
10270
10271 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10272 if (FAILED(rc)) throw rc;
10273
10274 data.llNetworkAdapters.push_back(nic);
10275 }
10276 }
10277
10278 /* Serial ports */
10279 data.llSerialPorts.clear();
10280 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10281 {
10282 if (mSerialPorts[slot]->i_hasDefaults())
10283 continue;
10284
10285 settings::SerialPort s;
10286 s.ulSlot = slot;
10287 rc = mSerialPorts[slot]->i_saveSettings(s);
10288 if (FAILED(rc)) return rc;
10289
10290 data.llSerialPorts.push_back(s);
10291 }
10292
10293 /* Parallel ports */
10294 data.llParallelPorts.clear();
10295 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10296 {
10297 if (mParallelPorts[slot]->i_hasDefaults())
10298 continue;
10299
10300 settings::ParallelPort p;
10301 p.ulSlot = slot;
10302 rc = mParallelPorts[slot]->i_saveSettings(p);
10303 if (FAILED(rc)) return rc;
10304
10305 data.llParallelPorts.push_back(p);
10306 }
10307
10308 /* Audio adapter */
10309 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10310 if (FAILED(rc)) return rc;
10311
10312 rc = i_saveStorageControllers(data.storage);
10313 if (FAILED(rc)) return rc;
10314
10315 /* Shared folders */
10316 data.llSharedFolders.clear();
10317 for (HWData::SharedFolderList::const_iterator
10318 it = mHWData->mSharedFolders.begin();
10319 it != mHWData->mSharedFolders.end();
10320 ++it)
10321 {
10322 SharedFolder *pSF = *it;
10323 AutoCaller sfCaller(pSF);
10324 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10325 settings::SharedFolder sf;
10326 sf.strName = pSF->i_getName();
10327 sf.strHostPath = pSF->i_getHostPath();
10328 sf.fWritable = !!pSF->i_isWritable();
10329 sf.fAutoMount = !!pSF->i_isAutoMounted();
10330 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10331
10332 data.llSharedFolders.push_back(sf);
10333 }
10334
10335 // clipboard
10336 data.clipboardMode = mHWData->mClipboardMode;
10337 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10338
10339 // drag'n'drop
10340 data.dndMode = mHWData->mDnDMode;
10341
10342 /* Guest */
10343 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10344
10345 // IO settings
10346 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10347 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10348
10349 /* BandwidthControl (required) */
10350 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10351 if (FAILED(rc)) throw rc;
10352
10353 /* Host PCI devices */
10354 data.pciAttachments.clear();
10355 for (HWData::PCIDeviceAssignmentList::const_iterator
10356 it = mHWData->mPCIDeviceAssignments.begin();
10357 it != mHWData->mPCIDeviceAssignments.end();
10358 ++it)
10359 {
10360 ComObjPtr<PCIDeviceAttachment> pda = *it;
10361 settings::HostPCIDeviceAttachment hpda;
10362
10363 rc = pda->i_saveSettings(hpda);
10364 if (FAILED(rc)) throw rc;
10365
10366 data.pciAttachments.push_back(hpda);
10367 }
10368
10369 // guest properties
10370 data.llGuestProperties.clear();
10371#ifdef VBOX_WITH_GUEST_PROPS
10372 for (HWData::GuestPropertyMap::const_iterator
10373 it = mHWData->mGuestProperties.begin();
10374 it != mHWData->mGuestProperties.end();
10375 ++it)
10376 {
10377 HWData::GuestProperty property = it->second;
10378
10379 /* Remove transient guest properties at shutdown unless we
10380 * are saving state. Note that restoring snapshot intentionally
10381 * keeps them, they will be removed if appropriate once the final
10382 * machine state is set (as crashes etc. need to work). */
10383 if ( ( mData->mMachineState == MachineState_PoweredOff
10384 || mData->mMachineState == MachineState_Aborted
10385 || mData->mMachineState == MachineState_Teleported)
10386 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10387 continue;
10388 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10389 prop.strName = it->first;
10390 prop.strValue = property.strValue;
10391 prop.timestamp = (uint64_t)property.mTimestamp;
10392 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10393 GuestPropWriteFlags(property.mFlags, szFlags);
10394 prop.strFlags = szFlags;
10395
10396 data.llGuestProperties.push_back(prop);
10397 }
10398
10399 /* I presume this doesn't require a backup(). */
10400 mData->mGuestPropertiesModified = FALSE;
10401#endif /* VBOX_WITH_GUEST_PROPS defined */
10402
10403 *pDbg = mHWData->mDebugging;
10404 *pAutostart = mHWData->mAutostart;
10405
10406 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10407 }
10408 catch (std::bad_alloc &)
10409 {
10410 return E_OUTOFMEMORY;
10411 }
10412
10413 AssertComRC(rc);
10414 return rc;
10415}
10416
10417/**
10418 * Saves the storage controller configuration.
10419 *
10420 * @param data storage settings.
10421 */
10422HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10423{
10424 data.llStorageControllers.clear();
10425
10426 for (StorageControllerList::const_iterator
10427 it = mStorageControllers->begin();
10428 it != mStorageControllers->end();
10429 ++it)
10430 {
10431 HRESULT rc;
10432 ComObjPtr<StorageController> pCtl = *it;
10433
10434 settings::StorageController ctl;
10435 ctl.strName = pCtl->i_getName();
10436 ctl.controllerType = pCtl->i_getControllerType();
10437 ctl.storageBus = pCtl->i_getStorageBus();
10438 ctl.ulInstance = pCtl->i_getInstance();
10439 ctl.fBootable = pCtl->i_getBootable();
10440
10441 /* Save the port count. */
10442 ULONG portCount;
10443 rc = pCtl->COMGETTER(PortCount)(&portCount);
10444 ComAssertComRCRet(rc, rc);
10445 ctl.ulPortCount = portCount;
10446
10447 /* Save fUseHostIOCache */
10448 BOOL fUseHostIOCache;
10449 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10450 ComAssertComRCRet(rc, rc);
10451 ctl.fUseHostIOCache = !!fUseHostIOCache;
10452
10453 /* save the devices now. */
10454 rc = i_saveStorageDevices(pCtl, ctl);
10455 ComAssertComRCRet(rc, rc);
10456
10457 data.llStorageControllers.push_back(ctl);
10458 }
10459
10460 return S_OK;
10461}
10462
10463/**
10464 * Saves the hard disk configuration.
10465 */
10466HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10467 settings::StorageController &data)
10468{
10469 MediumAttachmentList atts;
10470
10471 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10472 if (FAILED(rc)) return rc;
10473
10474 data.llAttachedDevices.clear();
10475 for (MediumAttachmentList::const_iterator
10476 it = atts.begin();
10477 it != atts.end();
10478 ++it)
10479 {
10480 settings::AttachedDevice dev;
10481 IMediumAttachment *iA = *it;
10482 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10483 Medium *pMedium = pAttach->i_getMedium();
10484
10485 dev.deviceType = pAttach->i_getType();
10486 dev.lPort = pAttach->i_getPort();
10487 dev.lDevice = pAttach->i_getDevice();
10488 dev.fPassThrough = pAttach->i_getPassthrough();
10489 dev.fHotPluggable = pAttach->i_getHotPluggable();
10490 if (pMedium)
10491 {
10492 if (pMedium->i_isHostDrive())
10493 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10494 else
10495 dev.uuid = pMedium->i_getId();
10496 dev.fTempEject = pAttach->i_getTempEject();
10497 dev.fNonRotational = pAttach->i_getNonRotational();
10498 dev.fDiscard = pAttach->i_getDiscard();
10499 }
10500
10501 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10502
10503 data.llAttachedDevices.push_back(dev);
10504 }
10505
10506 return S_OK;
10507}
10508
10509/**
10510 * Saves machine state settings as defined by aFlags
10511 * (SaveSTS_* values).
10512 *
10513 * @param aFlags Combination of SaveSTS_* flags.
10514 *
10515 * @note Locks objects for writing.
10516 */
10517HRESULT Machine::i_saveStateSettings(int aFlags)
10518{
10519 if (aFlags == 0)
10520 return S_OK;
10521
10522 AutoCaller autoCaller(this);
10523 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10524
10525 /* This object's write lock is also necessary to serialize file access
10526 * (prevent concurrent reads and writes) */
10527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10528
10529 HRESULT rc = S_OK;
10530
10531 Assert(mData->pMachineConfigFile);
10532
10533 try
10534 {
10535 if (aFlags & SaveSTS_CurStateModified)
10536 mData->pMachineConfigFile->fCurrentStateModified = true;
10537
10538 if (aFlags & SaveSTS_StateFilePath)
10539 {
10540 if (!mSSData->strStateFilePath.isEmpty())
10541 /* try to make the file name relative to the settings file dir */
10542 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10543 else
10544 mData->pMachineConfigFile->strStateFile.setNull();
10545 }
10546
10547 if (aFlags & SaveSTS_StateTimeStamp)
10548 {
10549 Assert( mData->mMachineState != MachineState_Aborted
10550 || mSSData->strStateFilePath.isEmpty());
10551
10552 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10553
10554 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10555 || mData->mMachineState == MachineState_AbortedSaved);
10556/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10557 }
10558
10559 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10560 }
10561 catch (...)
10562 {
10563 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10564 }
10565
10566 return rc;
10567}
10568
10569/**
10570 * Ensures that the given medium is added to a media registry. If this machine
10571 * was created with 4.0 or later, then the machine registry is used. Otherwise
10572 * the global VirtualBox media registry is used.
10573 *
10574 * Caller must NOT hold machine lock, media tree or any medium locks!
10575 *
10576 * @param pMedium
10577 */
10578void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10579{
10580 /* Paranoia checks: do not hold machine or media tree locks. */
10581 AssertReturnVoid(!isWriteLockOnCurrentThread());
10582 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10583
10584 ComObjPtr<Medium> pBase;
10585 {
10586 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10587 pBase = pMedium->i_getBase();
10588 }
10589
10590 /* Paranoia checks: do not hold medium locks. */
10591 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10592 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10593
10594 // decide which medium registry to use now that the medium is attached:
10595 Guid uuid;
10596 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10597 if (fCanHaveOwnMediaRegistry)
10598 // machine XML is VirtualBox 4.0 or higher:
10599 uuid = i_getId(); // machine UUID
10600 else
10601 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10602
10603 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10604 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10605 if (pMedium->i_addRegistry(uuid))
10606 mParent->i_markRegistryModified(uuid);
10607
10608 /* For more complex hard disk structures it can happen that the base
10609 * medium isn't yet associated with any medium registry. Do that now. */
10610 if (pMedium != pBase)
10611 {
10612 /* Tree lock needed by Medium::addRegistry when recursing. */
10613 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10614 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10615 {
10616 treeLock.release();
10617 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10618 treeLock.acquire();
10619 }
10620 if (pBase->i_addRegistryRecursive(uuid))
10621 {
10622 treeLock.release();
10623 mParent->i_markRegistryModified(uuid);
10624 }
10625 }
10626}
10627
10628/**
10629 * Creates differencing hard disks for all normal hard disks attached to this
10630 * machine and a new set of attachments to refer to created disks.
10631 *
10632 * Used when taking a snapshot or when deleting the current state. Gets called
10633 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10634 *
10635 * This method assumes that mMediumAttachments contains the original hard disk
10636 * attachments it needs to create diffs for. On success, these attachments will
10637 * be replaced with the created diffs.
10638 *
10639 * Attachments with non-normal hard disks are left as is.
10640 *
10641 * If @a aOnline is @c false then the original hard disks that require implicit
10642 * diffs will be locked for reading. Otherwise it is assumed that they are
10643 * already locked for writing (when the VM was started). Note that in the latter
10644 * case it is responsibility of the caller to lock the newly created diffs for
10645 * writing if this method succeeds.
10646 *
10647 * @param aProgress Progress object to run (must contain at least as
10648 * many operations left as the number of hard disks
10649 * attached).
10650 * @param aWeight Weight of this operation.
10651 * @param aOnline Whether the VM was online prior to this operation.
10652 *
10653 * @note The progress object is not marked as completed, neither on success nor
10654 * on failure. This is a responsibility of the caller.
10655 *
10656 * @note Locks this object and the media tree for writing.
10657 */
10658HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10659 ULONG aWeight,
10660 bool aOnline)
10661{
10662 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10663
10664 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10665 AssertReturn(!!pProgressControl, E_INVALIDARG);
10666
10667 AutoCaller autoCaller(this);
10668 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10669
10670 AutoMultiWriteLock2 alock(this->lockHandle(),
10671 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10672
10673 /* must be in a protective state because we release the lock below */
10674 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10675 || mData->mMachineState == MachineState_OnlineSnapshotting
10676 || mData->mMachineState == MachineState_LiveSnapshotting
10677 || mData->mMachineState == MachineState_RestoringSnapshot
10678 || mData->mMachineState == MachineState_DeletingSnapshot
10679 , E_FAIL);
10680
10681 HRESULT rc = S_OK;
10682
10683 // use appropriate locked media map (online or offline)
10684 MediumLockListMap lockedMediaOffline;
10685 MediumLockListMap *lockedMediaMap;
10686 if (aOnline)
10687 lockedMediaMap = &mData->mSession.mLockedMedia;
10688 else
10689 lockedMediaMap = &lockedMediaOffline;
10690
10691 try
10692 {
10693 if (!aOnline)
10694 {
10695 /* lock all attached hard disks early to detect "in use"
10696 * situations before creating actual diffs */
10697 for (MediumAttachmentList::const_iterator
10698 it = mMediumAttachments->begin();
10699 it != mMediumAttachments->end();
10700 ++it)
10701 {
10702 MediumAttachment *pAtt = *it;
10703 if (pAtt->i_getType() == DeviceType_HardDisk)
10704 {
10705 Medium *pMedium = pAtt->i_getMedium();
10706 Assert(pMedium);
10707
10708 MediumLockList *pMediumLockList(new MediumLockList());
10709 alock.release();
10710 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10711 NULL /* pToLockWrite */,
10712 false /* fMediumLockWriteAll */,
10713 NULL,
10714 *pMediumLockList);
10715 alock.acquire();
10716 if (FAILED(rc))
10717 {
10718 delete pMediumLockList;
10719 throw rc;
10720 }
10721 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10722 if (FAILED(rc))
10723 {
10724 throw setError(rc,
10725 tr("Collecting locking information for all attached media failed"));
10726 }
10727 }
10728 }
10729
10730 /* Now lock all media. If this fails, nothing is locked. */
10731 alock.release();
10732 rc = lockedMediaMap->Lock();
10733 alock.acquire();
10734 if (FAILED(rc))
10735 {
10736 throw setError(rc,
10737 tr("Locking of attached media failed"));
10738 }
10739 }
10740
10741 /* remember the current list (note that we don't use backup() since
10742 * mMediumAttachments may be already backed up) */
10743 MediumAttachmentList atts = *mMediumAttachments.data();
10744
10745 /* start from scratch */
10746 mMediumAttachments->clear();
10747
10748 /* go through remembered attachments and create diffs for normal hard
10749 * disks and attach them */
10750 for (MediumAttachmentList::const_iterator
10751 it = atts.begin();
10752 it != atts.end();
10753 ++it)
10754 {
10755 MediumAttachment *pAtt = *it;
10756
10757 DeviceType_T devType = pAtt->i_getType();
10758 Medium *pMedium = pAtt->i_getMedium();
10759
10760 if ( devType != DeviceType_HardDisk
10761 || pMedium == NULL
10762 || pMedium->i_getType() != MediumType_Normal)
10763 {
10764 /* copy the attachment as is */
10765
10766 /** @todo the progress object created in SessionMachine::TakeSnaphot
10767 * only expects operations for hard disks. Later other
10768 * device types need to show up in the progress as well. */
10769 if (devType == DeviceType_HardDisk)
10770 {
10771 if (pMedium == NULL)
10772 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10773 aWeight); // weight
10774 else
10775 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10776 pMedium->i_getBase()->i_getName().c_str()).raw(),
10777 aWeight); // weight
10778 }
10779
10780 mMediumAttachments->push_back(pAtt);
10781 continue;
10782 }
10783
10784 /* need a diff */
10785 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10786 pMedium->i_getBase()->i_getName().c_str()).raw(),
10787 aWeight); // weight
10788
10789 Utf8Str strFullSnapshotFolder;
10790 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10791
10792 ComObjPtr<Medium> diff;
10793 diff.createObject();
10794 // store the diff in the same registry as the parent
10795 // (this cannot fail here because we can't create implicit diffs for
10796 // unregistered images)
10797 Guid uuidRegistryParent;
10798 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10799 Assert(fInRegistry); NOREF(fInRegistry);
10800 rc = diff->init(mParent,
10801 pMedium->i_getPreferredDiffFormat(),
10802 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10803 uuidRegistryParent,
10804 DeviceType_HardDisk);
10805 if (FAILED(rc)) throw rc;
10806
10807 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10808 * the push_back? Looks like we're going to release medium with the
10809 * wrong kind of lock (general issue with if we fail anywhere at all)
10810 * and an orphaned VDI in the snapshots folder. */
10811
10812 /* update the appropriate lock list */
10813 MediumLockList *pMediumLockList;
10814 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10815 AssertComRCThrowRC(rc);
10816 if (aOnline)
10817 {
10818 alock.release();
10819 /* The currently attached medium will be read-only, change
10820 * the lock type to read. */
10821 rc = pMediumLockList->Update(pMedium, false);
10822 alock.acquire();
10823 AssertComRCThrowRC(rc);
10824 }
10825
10826 /* release the locks before the potentially lengthy operation */
10827 alock.release();
10828 rc = pMedium->i_createDiffStorage(diff,
10829 pMedium->i_getPreferredDiffVariant(),
10830 pMediumLockList,
10831 NULL /* aProgress */,
10832 true /* aWait */,
10833 false /* aNotify */);
10834 alock.acquire();
10835 if (FAILED(rc)) throw rc;
10836
10837 /* actual lock list update is done in Machine::i_commitMedia */
10838
10839 rc = diff->i_addBackReference(mData->mUuid);
10840 AssertComRCThrowRC(rc);
10841
10842 /* add a new attachment */
10843 ComObjPtr<MediumAttachment> attachment;
10844 attachment.createObject();
10845 rc = attachment->init(this,
10846 diff,
10847 pAtt->i_getControllerName(),
10848 pAtt->i_getPort(),
10849 pAtt->i_getDevice(),
10850 DeviceType_HardDisk,
10851 true /* aImplicit */,
10852 false /* aPassthrough */,
10853 false /* aTempEject */,
10854 pAtt->i_getNonRotational(),
10855 pAtt->i_getDiscard(),
10856 pAtt->i_getHotPluggable(),
10857 pAtt->i_getBandwidthGroup());
10858 if (FAILED(rc)) throw rc;
10859
10860 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10861 AssertComRCThrowRC(rc);
10862 mMediumAttachments->push_back(attachment);
10863 }
10864 }
10865 catch (HRESULT aRC) { rc = aRC; }
10866
10867 /* unlock all hard disks we locked when there is no VM */
10868 if (!aOnline)
10869 {
10870 ErrorInfoKeeper eik;
10871
10872 HRESULT rc1 = lockedMediaMap->Clear();
10873 AssertComRC(rc1);
10874 }
10875
10876 return rc;
10877}
10878
10879/**
10880 * Deletes implicit differencing hard disks created either by
10881 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10882 * mMediumAttachments.
10883 *
10884 * Note that to delete hard disks created by #attachDevice() this method is
10885 * called from #i_rollbackMedia() when the changes are rolled back.
10886 *
10887 * @note Locks this object and the media tree for writing.
10888 */
10889HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10890{
10891 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10892
10893 AutoCaller autoCaller(this);
10894 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10895
10896 AutoMultiWriteLock2 alock(this->lockHandle(),
10897 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10898
10899 /* We absolutely must have backed up state. */
10900 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10901
10902 /* Check if there are any implicitly created diff images. */
10903 bool fImplicitDiffs = false;
10904 for (MediumAttachmentList::const_iterator
10905 it = mMediumAttachments->begin();
10906 it != mMediumAttachments->end();
10907 ++it)
10908 {
10909 const ComObjPtr<MediumAttachment> &pAtt = *it;
10910 if (pAtt->i_isImplicit())
10911 {
10912 fImplicitDiffs = true;
10913 break;
10914 }
10915 }
10916 /* If there is nothing to do, leave early. This saves lots of image locking
10917 * effort. It also avoids a MachineStateChanged event without real reason.
10918 * This is important e.g. when loading a VM config, because there should be
10919 * no events. Otherwise API clients can become thoroughly confused for
10920 * inaccessible VMs (the code for loading VM configs uses this method for
10921 * cleanup if the config makes no sense), as they take such events as an
10922 * indication that the VM is alive, and they would force the VM config to
10923 * be reread, leading to an endless loop. */
10924 if (!fImplicitDiffs)
10925 return S_OK;
10926
10927 HRESULT rc = S_OK;
10928 MachineState_T oldState = mData->mMachineState;
10929
10930 /* will release the lock before the potentially lengthy operation,
10931 * so protect with the special state (unless already protected) */
10932 if ( oldState != MachineState_Snapshotting
10933 && oldState != MachineState_OnlineSnapshotting
10934 && oldState != MachineState_LiveSnapshotting
10935 && oldState != MachineState_RestoringSnapshot
10936 && oldState != MachineState_DeletingSnapshot
10937 && oldState != MachineState_DeletingSnapshotOnline
10938 && oldState != MachineState_DeletingSnapshotPaused
10939 )
10940 i_setMachineState(MachineState_SettingUp);
10941
10942 // use appropriate locked media map (online or offline)
10943 MediumLockListMap lockedMediaOffline;
10944 MediumLockListMap *lockedMediaMap;
10945 if (aOnline)
10946 lockedMediaMap = &mData->mSession.mLockedMedia;
10947 else
10948 lockedMediaMap = &lockedMediaOffline;
10949
10950 try
10951 {
10952 if (!aOnline)
10953 {
10954 /* lock all attached hard disks early to detect "in use"
10955 * situations before deleting actual diffs */
10956 for (MediumAttachmentList::const_iterator
10957 it = mMediumAttachments->begin();
10958 it != mMediumAttachments->end();
10959 ++it)
10960 {
10961 MediumAttachment *pAtt = *it;
10962 if (pAtt->i_getType() == DeviceType_HardDisk)
10963 {
10964 Medium *pMedium = pAtt->i_getMedium();
10965 Assert(pMedium);
10966
10967 MediumLockList *pMediumLockList(new MediumLockList());
10968 alock.release();
10969 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10970 NULL /* pToLockWrite */,
10971 false /* fMediumLockWriteAll */,
10972 NULL,
10973 *pMediumLockList);
10974 alock.acquire();
10975
10976 if (FAILED(rc))
10977 {
10978 delete pMediumLockList;
10979 throw rc;
10980 }
10981
10982 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10983 if (FAILED(rc))
10984 throw rc;
10985 }
10986 }
10987
10988 if (FAILED(rc))
10989 throw rc;
10990 } // end of offline
10991
10992 /* Lock lists are now up to date and include implicitly created media */
10993
10994 /* Go through remembered attachments and delete all implicitly created
10995 * diffs and fix up the attachment information */
10996 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10997 MediumAttachmentList implicitAtts;
10998 for (MediumAttachmentList::const_iterator
10999 it = mMediumAttachments->begin();
11000 it != mMediumAttachments->end();
11001 ++it)
11002 {
11003 ComObjPtr<MediumAttachment> pAtt = *it;
11004 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11005 if (pMedium.isNull())
11006 continue;
11007
11008 // Implicit attachments go on the list for deletion and back references are removed.
11009 if (pAtt->i_isImplicit())
11010 {
11011 /* Deassociate and mark for deletion */
11012 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11013 rc = pMedium->i_removeBackReference(mData->mUuid);
11014 if (FAILED(rc))
11015 throw rc;
11016 implicitAtts.push_back(pAtt);
11017 continue;
11018 }
11019
11020 /* Was this medium attached before? */
11021 if (!i_findAttachment(oldAtts, pMedium))
11022 {
11023 /* no: de-associate */
11024 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11025 rc = pMedium->i_removeBackReference(mData->mUuid);
11026 if (FAILED(rc))
11027 throw rc;
11028 continue;
11029 }
11030 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11031 }
11032
11033 /* If there are implicit attachments to delete, throw away the lock
11034 * map contents (which will unlock all media) since the medium
11035 * attachments will be rolled back. Below we need to completely
11036 * recreate the lock map anyway since it is infinitely complex to
11037 * do this incrementally (would need reconstructing each attachment
11038 * change, which would be extremely hairy). */
11039 if (implicitAtts.size() != 0)
11040 {
11041 ErrorInfoKeeper eik;
11042
11043 HRESULT rc1 = lockedMediaMap->Clear();
11044 AssertComRC(rc1);
11045 }
11046
11047 /* rollback hard disk changes */
11048 mMediumAttachments.rollback();
11049
11050 MultiResult mrc(S_OK);
11051
11052 // Delete unused implicit diffs.
11053 if (implicitAtts.size() != 0)
11054 {
11055 alock.release();
11056
11057 for (MediumAttachmentList::const_iterator
11058 it = implicitAtts.begin();
11059 it != implicitAtts.end();
11060 ++it)
11061 {
11062 // Remove medium associated with this attachment.
11063 ComObjPtr<MediumAttachment> pAtt = *it;
11064 Assert(pAtt);
11065 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11066 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11067 Assert(pMedium);
11068
11069 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11070 // continue on delete failure, just collect error messages
11071 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11072 pMedium->i_getLocationFull().c_str() ));
11073 mrc = rc;
11074 }
11075 // Clear the list of deleted implicit attachments now, while not
11076 // holding the lock, as it will ultimately trigger Medium::uninit()
11077 // calls which assume that the media tree lock isn't held.
11078 implicitAtts.clear();
11079
11080 alock.acquire();
11081
11082 /* if there is a VM recreate media lock map as mentioned above,
11083 * otherwise it is a waste of time and we leave things unlocked */
11084 if (aOnline)
11085 {
11086 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11087 /* must never be NULL, but better safe than sorry */
11088 if (!pMachine.isNull())
11089 {
11090 alock.release();
11091 rc = mData->mSession.mMachine->i_lockMedia();
11092 alock.acquire();
11093 if (FAILED(rc))
11094 throw rc;
11095 }
11096 }
11097 }
11098 }
11099 catch (HRESULT aRC) {rc = aRC;}
11100
11101 if (mData->mMachineState == MachineState_SettingUp)
11102 i_setMachineState(oldState);
11103
11104 /* unlock all hard disks we locked when there is no VM */
11105 if (!aOnline)
11106 {
11107 ErrorInfoKeeper eik;
11108
11109 HRESULT rc1 = lockedMediaMap->Clear();
11110 AssertComRC(rc1);
11111 }
11112
11113 return rc;
11114}
11115
11116
11117/**
11118 * Looks through the given list of media attachments for one with the given parameters
11119 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11120 * can be searched as well if needed.
11121 *
11122 * @param ll
11123 * @param aControllerName
11124 * @param aControllerPort
11125 * @param aDevice
11126 * @return
11127 */
11128MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11129 const Utf8Str &aControllerName,
11130 LONG aControllerPort,
11131 LONG aDevice)
11132{
11133 for (MediumAttachmentList::const_iterator
11134 it = ll.begin();
11135 it != ll.end();
11136 ++it)
11137 {
11138 MediumAttachment *pAttach = *it;
11139 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11140 return pAttach;
11141 }
11142
11143 return NULL;
11144}
11145
11146/**
11147 * Looks through the given list of media attachments for one with the given parameters
11148 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11149 * can be searched as well if needed.
11150 *
11151 * @param ll
11152 * @param pMedium
11153 * @return
11154 */
11155MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11156 ComObjPtr<Medium> pMedium)
11157{
11158 for (MediumAttachmentList::const_iterator
11159 it = ll.begin();
11160 it != ll.end();
11161 ++it)
11162 {
11163 MediumAttachment *pAttach = *it;
11164 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11165 if (pMediumThis == pMedium)
11166 return pAttach;
11167 }
11168
11169 return NULL;
11170}
11171
11172/**
11173 * Looks through the given list of media attachments for one with the given parameters
11174 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11175 * can be searched as well if needed.
11176 *
11177 * @param ll
11178 * @param id
11179 * @return
11180 */
11181MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11182 Guid &id)
11183{
11184 for (MediumAttachmentList::const_iterator
11185 it = ll.begin();
11186 it != ll.end();
11187 ++it)
11188 {
11189 MediumAttachment *pAttach = *it;
11190 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11191 if (pMediumThis->i_getId() == id)
11192 return pAttach;
11193 }
11194
11195 return NULL;
11196}
11197
11198/**
11199 * Main implementation for Machine::DetachDevice. This also gets called
11200 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11201 *
11202 * @param pAttach Medium attachment to detach.
11203 * @param writeLock Machine write lock which the caller must have locked once.
11204 * This may be released temporarily in here.
11205 * @param pSnapshot If NULL, then the detachment is for the current machine.
11206 * Otherwise this is for a SnapshotMachine, and this must be
11207 * its snapshot.
11208 * @return
11209 */
11210HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11211 AutoWriteLock &writeLock,
11212 Snapshot *pSnapshot)
11213{
11214 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11215 DeviceType_T mediumType = pAttach->i_getType();
11216
11217 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11218
11219 if (pAttach->i_isImplicit())
11220 {
11221 /* attempt to implicitly delete the implicitly created diff */
11222
11223 /// @todo move the implicit flag from MediumAttachment to Medium
11224 /// and forbid any hard disk operation when it is implicit. Or maybe
11225 /// a special media state for it to make it even more simple.
11226
11227 Assert(mMediumAttachments.isBackedUp());
11228
11229 /* will release the lock before the potentially lengthy operation, so
11230 * protect with the special state */
11231 MachineState_T oldState = mData->mMachineState;
11232 i_setMachineState(MachineState_SettingUp);
11233
11234 writeLock.release();
11235
11236 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11237 true /*aWait*/,
11238 false /*aNotify*/);
11239
11240 writeLock.acquire();
11241
11242 i_setMachineState(oldState);
11243
11244 if (FAILED(rc)) return rc;
11245 }
11246
11247 i_setModified(IsModified_Storage);
11248 mMediumAttachments.backup();
11249 mMediumAttachments->remove(pAttach);
11250
11251 if (!oldmedium.isNull())
11252 {
11253 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11254 if (pSnapshot)
11255 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11256 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11257 else if (mediumType != DeviceType_HardDisk)
11258 oldmedium->i_removeBackReference(mData->mUuid);
11259 }
11260
11261 return S_OK;
11262}
11263
11264/**
11265 * Goes thru all media of the given list and
11266 *
11267 * 1) calls i_detachDevice() on each of them for this machine and
11268 * 2) adds all Medium objects found in the process to the given list,
11269 * depending on cleanupMode.
11270 *
11271 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11272 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11273 * media to the list.
11274 *
11275 * This gets called from Machine::Unregister, both for the actual Machine and
11276 * the SnapshotMachine objects that might be found in the snapshots.
11277 *
11278 * Requires caller and locking. The machine lock must be passed in because it
11279 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11280 *
11281 * @param writeLock Machine lock from top-level caller; this gets passed to
11282 * i_detachDevice.
11283 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11284 * object if called for a SnapshotMachine.
11285 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11286 * added to llMedia; if Full, then all media get added;
11287 * otherwise no media get added.
11288 * @param llMedia Caller's list to receive Medium objects which got detached so
11289 * caller can close() them, depending on cleanupMode.
11290 * @return
11291 */
11292HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11293 Snapshot *pSnapshot,
11294 CleanupMode_T cleanupMode,
11295 MediaList &llMedia)
11296{
11297 Assert(isWriteLockOnCurrentThread());
11298
11299 HRESULT rc;
11300
11301 // make a temporary list because i_detachDevice invalidates iterators into
11302 // mMediumAttachments
11303 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11304
11305 for (MediumAttachmentList::iterator
11306 it = llAttachments2.begin();
11307 it != llAttachments2.end();
11308 ++it)
11309 {
11310 ComObjPtr<MediumAttachment> &pAttach = *it;
11311 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11312
11313 if (!pMedium.isNull())
11314 {
11315 AutoCaller mac(pMedium);
11316 if (FAILED(mac.rc())) return mac.rc();
11317 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11318 DeviceType_T devType = pMedium->i_getDeviceType();
11319 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11320 && devType == DeviceType_HardDisk)
11321 || (cleanupMode == CleanupMode_Full)
11322 )
11323 {
11324 llMedia.push_back(pMedium);
11325 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11326 /* Not allowed to keep this lock as below we need the parent
11327 * medium lock, and the lock order is parent to child. */
11328 lock.release();
11329 /*
11330 * Search for medias which are not attached to any machine, but
11331 * in the chain to an attached disk. Mediums are only consided
11332 * if they are:
11333 * - have only one child
11334 * - no references to any machines
11335 * - are of normal medium type
11336 */
11337 while (!pParent.isNull())
11338 {
11339 AutoCaller mac1(pParent);
11340 if (FAILED(mac1.rc())) return mac1.rc();
11341 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11342 if (pParent->i_getChildren().size() == 1)
11343 {
11344 if ( pParent->i_getMachineBackRefCount() == 0
11345 && pParent->i_getType() == MediumType_Normal
11346 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11347 llMedia.push_back(pParent);
11348 }
11349 else
11350 break;
11351 pParent = pParent->i_getParent();
11352 }
11353 }
11354 }
11355
11356 // real machine: then we need to use the proper method
11357 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11358
11359 if (FAILED(rc))
11360 return rc;
11361 }
11362
11363 return S_OK;
11364}
11365
11366/**
11367 * Perform deferred hard disk detachments.
11368 *
11369 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11370 * changed (not backed up).
11371 *
11372 * If @a aOnline is @c true then this method will also unlock the old hard
11373 * disks for which the new implicit diffs were created and will lock these new
11374 * diffs for writing.
11375 *
11376 * @param aOnline Whether the VM was online prior to this operation.
11377 *
11378 * @note Locks this object for writing!
11379 */
11380void Machine::i_commitMedia(bool aOnline /*= false*/)
11381{
11382 AutoCaller autoCaller(this);
11383 AssertComRCReturnVoid(autoCaller.rc());
11384
11385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11386
11387 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11388
11389 HRESULT rc = S_OK;
11390
11391 /* no attach/detach operations -- nothing to do */
11392 if (!mMediumAttachments.isBackedUp())
11393 return;
11394
11395 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11396 bool fMediaNeedsLocking = false;
11397
11398 /* enumerate new attachments */
11399 for (MediumAttachmentList::const_iterator
11400 it = mMediumAttachments->begin();
11401 it != mMediumAttachments->end();
11402 ++it)
11403 {
11404 MediumAttachment *pAttach = *it;
11405
11406 pAttach->i_commit();
11407
11408 Medium *pMedium = pAttach->i_getMedium();
11409 bool fImplicit = pAttach->i_isImplicit();
11410
11411 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11412 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11413 fImplicit));
11414
11415 /** @todo convert all this Machine-based voodoo to MediumAttachment
11416 * based commit logic. */
11417 if (fImplicit)
11418 {
11419 /* convert implicit attachment to normal */
11420 pAttach->i_setImplicit(false);
11421
11422 if ( aOnline
11423 && pMedium
11424 && pAttach->i_getType() == DeviceType_HardDisk
11425 )
11426 {
11427 /* update the appropriate lock list */
11428 MediumLockList *pMediumLockList;
11429 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11430 AssertComRC(rc);
11431 if (pMediumLockList)
11432 {
11433 /* unlock if there's a need to change the locking */
11434 if (!fMediaNeedsLocking)
11435 {
11436 rc = mData->mSession.mLockedMedia.Unlock();
11437 AssertComRC(rc);
11438 fMediaNeedsLocking = true;
11439 }
11440 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11441 AssertComRC(rc);
11442 rc = pMediumLockList->Append(pMedium, true);
11443 AssertComRC(rc);
11444 }
11445 }
11446
11447 continue;
11448 }
11449
11450 if (pMedium)
11451 {
11452 /* was this medium attached before? */
11453 for (MediumAttachmentList::iterator
11454 oldIt = oldAtts.begin();
11455 oldIt != oldAtts.end();
11456 ++oldIt)
11457 {
11458 MediumAttachment *pOldAttach = *oldIt;
11459 if (pOldAttach->i_getMedium() == pMedium)
11460 {
11461 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11462
11463 /* yes: remove from old to avoid de-association */
11464 oldAtts.erase(oldIt);
11465 break;
11466 }
11467 }
11468 }
11469 }
11470
11471 /* enumerate remaining old attachments and de-associate from the
11472 * current machine state */
11473 for (MediumAttachmentList::const_iterator
11474 it = oldAtts.begin();
11475 it != oldAtts.end();
11476 ++it)
11477 {
11478 MediumAttachment *pAttach = *it;
11479 Medium *pMedium = pAttach->i_getMedium();
11480
11481 /* Detach only hard disks, since DVD/floppy media is detached
11482 * instantly in MountMedium. */
11483 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11484 {
11485 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11486
11487 /* now de-associate from the current machine state */
11488 rc = pMedium->i_removeBackReference(mData->mUuid);
11489 AssertComRC(rc);
11490
11491 if (aOnline)
11492 {
11493 /* unlock since medium is not used anymore */
11494 MediumLockList *pMediumLockList;
11495 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11496 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11497 {
11498 /* this happens for online snapshots, there the attachment
11499 * is changing, but only to a diff image created under
11500 * the old one, so there is no separate lock list */
11501 Assert(!pMediumLockList);
11502 }
11503 else
11504 {
11505 AssertComRC(rc);
11506 if (pMediumLockList)
11507 {
11508 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11509 AssertComRC(rc);
11510 }
11511 }
11512 }
11513 }
11514 }
11515
11516 /* take media locks again so that the locking state is consistent */
11517 if (fMediaNeedsLocking)
11518 {
11519 Assert(aOnline);
11520 rc = mData->mSession.mLockedMedia.Lock();
11521 AssertComRC(rc);
11522 }
11523
11524 /* commit the hard disk changes */
11525 mMediumAttachments.commit();
11526
11527 if (i_isSessionMachine())
11528 {
11529 /*
11530 * Update the parent machine to point to the new owner.
11531 * This is necessary because the stored parent will point to the
11532 * session machine otherwise and cause crashes or errors later
11533 * when the session machine gets invalid.
11534 */
11535 /** @todo Change the MediumAttachment class to behave like any other
11536 * class in this regard by creating peer MediumAttachment
11537 * objects for session machines and share the data with the peer
11538 * machine.
11539 */
11540 for (MediumAttachmentList::const_iterator
11541 it = mMediumAttachments->begin();
11542 it != mMediumAttachments->end();
11543 ++it)
11544 (*it)->i_updateParentMachine(mPeer);
11545
11546 /* attach new data to the primary machine and reshare it */
11547 mPeer->mMediumAttachments.attach(mMediumAttachments);
11548 }
11549
11550 return;
11551}
11552
11553/**
11554 * Perform deferred deletion of implicitly created diffs.
11555 *
11556 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11557 * changed (not backed up).
11558 *
11559 * @note Locks this object for writing!
11560 */
11561void Machine::i_rollbackMedia()
11562{
11563 AutoCaller autoCaller(this);
11564 AssertComRCReturnVoid(autoCaller.rc());
11565
11566 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11567 LogFlowThisFunc(("Entering rollbackMedia\n"));
11568
11569 HRESULT rc = S_OK;
11570
11571 /* no attach/detach operations -- nothing to do */
11572 if (!mMediumAttachments.isBackedUp())
11573 return;
11574
11575 /* enumerate new attachments */
11576 for (MediumAttachmentList::const_iterator
11577 it = mMediumAttachments->begin();
11578 it != mMediumAttachments->end();
11579 ++it)
11580 {
11581 MediumAttachment *pAttach = *it;
11582 /* Fix up the backrefs for DVD/floppy media. */
11583 if (pAttach->i_getType() != DeviceType_HardDisk)
11584 {
11585 Medium *pMedium = pAttach->i_getMedium();
11586 if (pMedium)
11587 {
11588 rc = pMedium->i_removeBackReference(mData->mUuid);
11589 AssertComRC(rc);
11590 }
11591 }
11592
11593 (*it)->i_rollback();
11594
11595 pAttach = *it;
11596 /* Fix up the backrefs for DVD/floppy media. */
11597 if (pAttach->i_getType() != DeviceType_HardDisk)
11598 {
11599 Medium *pMedium = pAttach->i_getMedium();
11600 if (pMedium)
11601 {
11602 rc = pMedium->i_addBackReference(mData->mUuid);
11603 AssertComRC(rc);
11604 }
11605 }
11606 }
11607
11608 /** @todo convert all this Machine-based voodoo to MediumAttachment
11609 * based rollback logic. */
11610 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11611
11612 return;
11613}
11614
11615/**
11616 * Returns true if the settings file is located in the directory named exactly
11617 * as the machine; this means, among other things, that the machine directory
11618 * should be auto-renamed.
11619 *
11620 * @param aSettingsDir if not NULL, the full machine settings file directory
11621 * name will be assigned there.
11622 *
11623 * @note Doesn't lock anything.
11624 * @note Not thread safe (must be called from this object's lock).
11625 */
11626bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11627{
11628 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11629 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11630 if (aSettingsDir)
11631 *aSettingsDir = strMachineDirName;
11632 strMachineDirName.stripPath(); // vmname
11633 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11634 strConfigFileOnly.stripPath() // vmname.vbox
11635 .stripSuffix(); // vmname
11636 /** @todo hack, make somehow use of ComposeMachineFilename */
11637 if (mUserData->s.fDirectoryIncludesUUID)
11638 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11639
11640 AssertReturn(!strMachineDirName.isEmpty(), false);
11641 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11642
11643 return strMachineDirName == strConfigFileOnly;
11644}
11645
11646/**
11647 * Discards all changes to machine settings.
11648 *
11649 * @param aNotify Whether to notify the direct session about changes or not.
11650 *
11651 * @note Locks objects for writing!
11652 */
11653void Machine::i_rollback(bool aNotify)
11654{
11655 AutoCaller autoCaller(this);
11656 AssertComRCReturn(autoCaller.rc(), (void)0);
11657
11658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11659
11660 if (!mStorageControllers.isNull())
11661 {
11662 if (mStorageControllers.isBackedUp())
11663 {
11664 /* unitialize all new devices (absent in the backed up list). */
11665 StorageControllerList *backedList = mStorageControllers.backedUpData();
11666 for (StorageControllerList::const_iterator
11667 it = mStorageControllers->begin();
11668 it != mStorageControllers->end();
11669 ++it)
11670 {
11671 if ( std::find(backedList->begin(), backedList->end(), *it)
11672 == backedList->end()
11673 )
11674 {
11675 (*it)->uninit();
11676 }
11677 }
11678
11679 /* restore the list */
11680 mStorageControllers.rollback();
11681 }
11682
11683 /* rollback any changes to devices after restoring the list */
11684 if (mData->flModifications & IsModified_Storage)
11685 {
11686 for (StorageControllerList::const_iterator
11687 it = mStorageControllers->begin();
11688 it != mStorageControllers->end();
11689 ++it)
11690 {
11691 (*it)->i_rollback();
11692 }
11693 }
11694 }
11695
11696 if (!mUSBControllers.isNull())
11697 {
11698 if (mUSBControllers.isBackedUp())
11699 {
11700 /* unitialize all new devices (absent in the backed up list). */
11701 USBControllerList *backedList = mUSBControllers.backedUpData();
11702 for (USBControllerList::const_iterator
11703 it = mUSBControllers->begin();
11704 it != mUSBControllers->end();
11705 ++it)
11706 {
11707 if ( std::find(backedList->begin(), backedList->end(), *it)
11708 == backedList->end()
11709 )
11710 {
11711 (*it)->uninit();
11712 }
11713 }
11714
11715 /* restore the list */
11716 mUSBControllers.rollback();
11717 }
11718
11719 /* rollback any changes to devices after restoring the list */
11720 if (mData->flModifications & IsModified_USB)
11721 {
11722 for (USBControllerList::const_iterator
11723 it = mUSBControllers->begin();
11724 it != mUSBControllers->end();
11725 ++it)
11726 {
11727 (*it)->i_rollback();
11728 }
11729 }
11730 }
11731
11732 mUserData.rollback();
11733
11734 mHWData.rollback();
11735
11736 if (mData->flModifications & IsModified_Storage)
11737 i_rollbackMedia();
11738
11739 if (mBIOSSettings)
11740 mBIOSSettings->i_rollback();
11741
11742 if (mTrustedPlatformModule)
11743 mTrustedPlatformModule->i_rollback();
11744
11745 if (mNvramStore)
11746 mNvramStore->i_rollback();
11747
11748 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11749 mRecordingSettings->i_rollback();
11750
11751 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11752 mGraphicsAdapter->i_rollback();
11753
11754 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11755 mVRDEServer->i_rollback();
11756
11757 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11758 mAudioAdapter->i_rollback();
11759
11760 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11761 mUSBDeviceFilters->i_rollback();
11762
11763 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11764 mBandwidthControl->i_rollback();
11765
11766 if (!mHWData.isNull())
11767 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11768 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11769 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11770 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11771
11772 if (mData->flModifications & IsModified_NetworkAdapters)
11773 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11774 if ( mNetworkAdapters[slot]
11775 && mNetworkAdapters[slot]->i_isModified())
11776 {
11777 mNetworkAdapters[slot]->i_rollback();
11778 networkAdapters[slot] = mNetworkAdapters[slot];
11779 }
11780
11781 if (mData->flModifications & IsModified_SerialPorts)
11782 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11783 if ( mSerialPorts[slot]
11784 && mSerialPorts[slot]->i_isModified())
11785 {
11786 mSerialPorts[slot]->i_rollback();
11787 serialPorts[slot] = mSerialPorts[slot];
11788 }
11789
11790 if (mData->flModifications & IsModified_ParallelPorts)
11791 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11792 if ( mParallelPorts[slot]
11793 && mParallelPorts[slot]->i_isModified())
11794 {
11795 mParallelPorts[slot]->i_rollback();
11796 parallelPorts[slot] = mParallelPorts[slot];
11797 }
11798
11799 if (aNotify)
11800 {
11801 /* inform the direct session about changes */
11802
11803 ComObjPtr<Machine> that = this;
11804 uint32_t flModifications = mData->flModifications;
11805 alock.release();
11806
11807 if (flModifications & IsModified_SharedFolders)
11808 that->i_onSharedFolderChange();
11809
11810 if (flModifications & IsModified_VRDEServer)
11811 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11812 if (flModifications & IsModified_USB)
11813 that->i_onUSBControllerChange();
11814
11815 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11816 if (networkAdapters[slot])
11817 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11818 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11819 if (serialPorts[slot])
11820 that->i_onSerialPortChange(serialPorts[slot]);
11821 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11822 if (parallelPorts[slot])
11823 that->i_onParallelPortChange(parallelPorts[slot]);
11824
11825 if (flModifications & IsModified_Storage)
11826 {
11827 for (StorageControllerList::const_iterator
11828 it = mStorageControllers->begin();
11829 it != mStorageControllers->end();
11830 ++it)
11831 {
11832 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11833 }
11834 }
11835
11836
11837#if 0
11838 if (flModifications & IsModified_BandwidthControl)
11839 that->onBandwidthControlChange();
11840#endif
11841 }
11842}
11843
11844/**
11845 * Commits all the changes to machine settings.
11846 *
11847 * Note that this operation is supposed to never fail.
11848 *
11849 * @note Locks this object and children for writing.
11850 */
11851void Machine::i_commit()
11852{
11853 AutoCaller autoCaller(this);
11854 AssertComRCReturnVoid(autoCaller.rc());
11855
11856 AutoCaller peerCaller(mPeer);
11857 AssertComRCReturnVoid(peerCaller.rc());
11858
11859 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11860
11861 /*
11862 * use safe commit to ensure Snapshot machines (that share mUserData)
11863 * will still refer to a valid memory location
11864 */
11865 mUserData.commitCopy();
11866
11867 mHWData.commit();
11868
11869 if (mMediumAttachments.isBackedUp())
11870 i_commitMedia(Global::IsOnline(mData->mMachineState));
11871
11872 mBIOSSettings->i_commit();
11873 mTrustedPlatformModule->i_commit();
11874 mNvramStore->i_commit();
11875 mRecordingSettings->i_commit();
11876 mGraphicsAdapter->i_commit();
11877 mVRDEServer->i_commit();
11878 mAudioAdapter->i_commit();
11879 mUSBDeviceFilters->i_commit();
11880 mBandwidthControl->i_commit();
11881
11882 /* Since mNetworkAdapters is a list which might have been changed (resized)
11883 * without using the Backupable<> template we need to handle the copying
11884 * of the list entries manually, including the creation of peers for the
11885 * new objects. */
11886 bool commitNetworkAdapters = false;
11887 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11888 if (mPeer)
11889 {
11890 /* commit everything, even the ones which will go away */
11891 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11892 mNetworkAdapters[slot]->i_commit();
11893 /* copy over the new entries, creating a peer and uninit the original */
11894 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11895 for (size_t slot = 0; slot < newSize; slot++)
11896 {
11897 /* look if this adapter has a peer device */
11898 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11899 if (!peer)
11900 {
11901 /* no peer means the adapter is a newly created one;
11902 * create a peer owning data this data share it with */
11903 peer.createObject();
11904 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11905 }
11906 mPeer->mNetworkAdapters[slot] = peer;
11907 }
11908 /* uninit any no longer needed network adapters */
11909 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11910 mNetworkAdapters[slot]->uninit();
11911 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11912 {
11913 if (mPeer->mNetworkAdapters[slot])
11914 mPeer->mNetworkAdapters[slot]->uninit();
11915 }
11916 /* Keep the original network adapter count until this point, so that
11917 * discarding a chipset type change will not lose settings. */
11918 mNetworkAdapters.resize(newSize);
11919 mPeer->mNetworkAdapters.resize(newSize);
11920 }
11921 else
11922 {
11923 /* we have no peer (our parent is the newly created machine);
11924 * just commit changes to the network adapters */
11925 commitNetworkAdapters = true;
11926 }
11927 if (commitNetworkAdapters)
11928 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11929 mNetworkAdapters[slot]->i_commit();
11930
11931 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11932 mSerialPorts[slot]->i_commit();
11933 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11934 mParallelPorts[slot]->i_commit();
11935
11936 bool commitStorageControllers = false;
11937
11938 if (mStorageControllers.isBackedUp())
11939 {
11940 mStorageControllers.commit();
11941
11942 if (mPeer)
11943 {
11944 /* Commit all changes to new controllers (this will reshare data with
11945 * peers for those who have peers) */
11946 StorageControllerList *newList = new StorageControllerList();
11947 for (StorageControllerList::const_iterator
11948 it = mStorageControllers->begin();
11949 it != mStorageControllers->end();
11950 ++it)
11951 {
11952 (*it)->i_commit();
11953
11954 /* look if this controller has a peer device */
11955 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11956 if (!peer)
11957 {
11958 /* no peer means the device is a newly created one;
11959 * create a peer owning data this device share it with */
11960 peer.createObject();
11961 peer->init(mPeer, *it, true /* aReshare */);
11962 }
11963 else
11964 {
11965 /* remove peer from the old list */
11966 mPeer->mStorageControllers->remove(peer);
11967 }
11968 /* and add it to the new list */
11969 newList->push_back(peer);
11970 }
11971
11972 /* uninit old peer's controllers that are left */
11973 for (StorageControllerList::const_iterator
11974 it = mPeer->mStorageControllers->begin();
11975 it != mPeer->mStorageControllers->end();
11976 ++it)
11977 {
11978 (*it)->uninit();
11979 }
11980
11981 /* attach new list of controllers to our peer */
11982 mPeer->mStorageControllers.attach(newList);
11983 }
11984 else
11985 {
11986 /* we have no peer (our parent is the newly created machine);
11987 * just commit changes to devices */
11988 commitStorageControllers = true;
11989 }
11990 }
11991 else
11992 {
11993 /* the list of controllers itself is not changed,
11994 * just commit changes to controllers themselves */
11995 commitStorageControllers = true;
11996 }
11997
11998 if (commitStorageControllers)
11999 {
12000 for (StorageControllerList::const_iterator
12001 it = mStorageControllers->begin();
12002 it != mStorageControllers->end();
12003 ++it)
12004 {
12005 (*it)->i_commit();
12006 }
12007 }
12008
12009 bool commitUSBControllers = false;
12010
12011 if (mUSBControllers.isBackedUp())
12012 {
12013 mUSBControllers.commit();
12014
12015 if (mPeer)
12016 {
12017 /* Commit all changes to new controllers (this will reshare data with
12018 * peers for those who have peers) */
12019 USBControllerList *newList = new USBControllerList();
12020 for (USBControllerList::const_iterator
12021 it = mUSBControllers->begin();
12022 it != mUSBControllers->end();
12023 ++it)
12024 {
12025 (*it)->i_commit();
12026
12027 /* look if this controller has a peer device */
12028 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12029 if (!peer)
12030 {
12031 /* no peer means the device is a newly created one;
12032 * create a peer owning data this device share it with */
12033 peer.createObject();
12034 peer->init(mPeer, *it, true /* aReshare */);
12035 }
12036 else
12037 {
12038 /* remove peer from the old list */
12039 mPeer->mUSBControllers->remove(peer);
12040 }
12041 /* and add it to the new list */
12042 newList->push_back(peer);
12043 }
12044
12045 /* uninit old peer's controllers that are left */
12046 for (USBControllerList::const_iterator
12047 it = mPeer->mUSBControllers->begin();
12048 it != mPeer->mUSBControllers->end();
12049 ++it)
12050 {
12051 (*it)->uninit();
12052 }
12053
12054 /* attach new list of controllers to our peer */
12055 mPeer->mUSBControllers.attach(newList);
12056 }
12057 else
12058 {
12059 /* we have no peer (our parent is the newly created machine);
12060 * just commit changes to devices */
12061 commitUSBControllers = true;
12062 }
12063 }
12064 else
12065 {
12066 /* the list of controllers itself is not changed,
12067 * just commit changes to controllers themselves */
12068 commitUSBControllers = true;
12069 }
12070
12071 if (commitUSBControllers)
12072 {
12073 for (USBControllerList::const_iterator
12074 it = mUSBControllers->begin();
12075 it != mUSBControllers->end();
12076 ++it)
12077 {
12078 (*it)->i_commit();
12079 }
12080 }
12081
12082 if (i_isSessionMachine())
12083 {
12084 /* attach new data to the primary machine and reshare it */
12085 mPeer->mUserData.attach(mUserData);
12086 mPeer->mHWData.attach(mHWData);
12087 /* mmMediumAttachments is reshared by fixupMedia */
12088 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12089 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12090 }
12091}
12092
12093/**
12094 * Copies all the hardware data from the given machine.
12095 *
12096 * Currently, only called when the VM is being restored from a snapshot. In
12097 * particular, this implies that the VM is not running during this method's
12098 * call.
12099 *
12100 * @note This method must be called from under this object's lock.
12101 *
12102 * @note This method doesn't call #i_commit(), so all data remains backed up and
12103 * unsaved.
12104 */
12105void Machine::i_copyFrom(Machine *aThat)
12106{
12107 AssertReturnVoid(!i_isSnapshotMachine());
12108 AssertReturnVoid(aThat->i_isSnapshotMachine());
12109
12110 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12111
12112 mHWData.assignCopy(aThat->mHWData);
12113
12114 // create copies of all shared folders (mHWData after attaching a copy
12115 // contains just references to original objects)
12116 for (HWData::SharedFolderList::iterator
12117 it = mHWData->mSharedFolders.begin();
12118 it != mHWData->mSharedFolders.end();
12119 ++it)
12120 {
12121 ComObjPtr<SharedFolder> folder;
12122 folder.createObject();
12123 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12124 AssertComRC(rc);
12125 *it = folder;
12126 }
12127
12128 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12129 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12130 mNvramStore->i_copyFrom(aThat->mNvramStore);
12131 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12132 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12133 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12134 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12135 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12136 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12137
12138 /* create private copies of all controllers */
12139 mStorageControllers.backup();
12140 mStorageControllers->clear();
12141 for (StorageControllerList::const_iterator
12142 it = aThat->mStorageControllers->begin();
12143 it != aThat->mStorageControllers->end();
12144 ++it)
12145 {
12146 ComObjPtr<StorageController> ctrl;
12147 ctrl.createObject();
12148 ctrl->initCopy(this, *it);
12149 mStorageControllers->push_back(ctrl);
12150 }
12151
12152 /* create private copies of all USB controllers */
12153 mUSBControllers.backup();
12154 mUSBControllers->clear();
12155 for (USBControllerList::const_iterator
12156 it = aThat->mUSBControllers->begin();
12157 it != aThat->mUSBControllers->end();
12158 ++it)
12159 {
12160 ComObjPtr<USBController> ctrl;
12161 ctrl.createObject();
12162 ctrl->initCopy(this, *it);
12163 mUSBControllers->push_back(ctrl);
12164 }
12165
12166 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12167 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12168 {
12169 if (mNetworkAdapters[slot].isNotNull())
12170 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12171 else
12172 {
12173 unconst(mNetworkAdapters[slot]).createObject();
12174 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12175 }
12176 }
12177 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12178 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12179 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12180 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12181}
12182
12183/**
12184 * Returns whether the given storage controller is hotplug capable.
12185 *
12186 * @returns true if the controller supports hotplugging
12187 * false otherwise.
12188 * @param enmCtrlType The controller type to check for.
12189 */
12190bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12191{
12192 ComPtr<ISystemProperties> systemProperties;
12193 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12194 if (FAILED(rc))
12195 return false;
12196
12197 BOOL aHotplugCapable = FALSE;
12198 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12199
12200 return RT_BOOL(aHotplugCapable);
12201}
12202
12203#ifdef VBOX_WITH_RESOURCE_USAGE_API
12204
12205void Machine::i_getDiskList(MediaList &list)
12206{
12207 for (MediumAttachmentList::const_iterator
12208 it = mMediumAttachments->begin();
12209 it != mMediumAttachments->end();
12210 ++it)
12211 {
12212 MediumAttachment *pAttach = *it;
12213 /* just in case */
12214 AssertContinue(pAttach);
12215
12216 AutoCaller localAutoCallerA(pAttach);
12217 if (FAILED(localAutoCallerA.rc())) continue;
12218
12219 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12220
12221 if (pAttach->i_getType() == DeviceType_HardDisk)
12222 list.push_back(pAttach->i_getMedium());
12223 }
12224}
12225
12226void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12227{
12228 AssertReturnVoid(isWriteLockOnCurrentThread());
12229 AssertPtrReturnVoid(aCollector);
12230
12231 pm::CollectorHAL *hal = aCollector->getHAL();
12232 /* Create sub metrics */
12233 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12234 "Percentage of processor time spent in user mode by the VM process.");
12235 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12236 "Percentage of processor time spent in kernel mode by the VM process.");
12237 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12238 "Size of resident portion of VM process in memory.");
12239 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12240 "Actual size of all VM disks combined.");
12241 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12242 "Network receive rate.");
12243 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12244 "Network transmit rate.");
12245 /* Create and register base metrics */
12246 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12247 cpuLoadUser, cpuLoadKernel);
12248 aCollector->registerBaseMetric(cpuLoad);
12249 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12250 ramUsageUsed);
12251 aCollector->registerBaseMetric(ramUsage);
12252 MediaList disks;
12253 i_getDiskList(disks);
12254 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12255 diskUsageUsed);
12256 aCollector->registerBaseMetric(diskUsage);
12257
12258 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12259 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12260 new pm::AggregateAvg()));
12261 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12262 new pm::AggregateMin()));
12263 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12264 new pm::AggregateMax()));
12265 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12266 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12267 new pm::AggregateAvg()));
12268 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12269 new pm::AggregateMin()));
12270 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12271 new pm::AggregateMax()));
12272
12273 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12274 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12275 new pm::AggregateAvg()));
12276 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12277 new pm::AggregateMin()));
12278 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12279 new pm::AggregateMax()));
12280
12281 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12282 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12283 new pm::AggregateAvg()));
12284 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12285 new pm::AggregateMin()));
12286 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12287 new pm::AggregateMax()));
12288
12289
12290 /* Guest metrics collector */
12291 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12292 aCollector->registerGuest(mCollectorGuest);
12293 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12294
12295 /* Create sub metrics */
12296 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12297 "Percentage of processor time spent in user mode as seen by the guest.");
12298 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12299 "Percentage of processor time spent in kernel mode as seen by the guest.");
12300 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12301 "Percentage of processor time spent idling as seen by the guest.");
12302
12303 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12304 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12305 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12306 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12307 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12308 pm::SubMetric *guestMemCache = new pm::SubMetric(
12309 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12310
12311 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12312 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12313
12314 /* Create and register base metrics */
12315 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12316 machineNetRx, machineNetTx);
12317 aCollector->registerBaseMetric(machineNetRate);
12318
12319 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12320 guestLoadUser, guestLoadKernel, guestLoadIdle);
12321 aCollector->registerBaseMetric(guestCpuLoad);
12322
12323 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12324 guestMemTotal, guestMemFree,
12325 guestMemBalloon, guestMemShared,
12326 guestMemCache, guestPagedTotal);
12327 aCollector->registerBaseMetric(guestCpuMem);
12328
12329 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12330 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12331 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12332 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12333
12334 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12335 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12336 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12337 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12338
12339 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12340 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12341 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12343
12344 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12345 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12346 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12347 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12348
12349 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12350 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12351 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12352 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12353
12354 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12355 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12356 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12357 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12358
12359 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12360 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12361 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12363
12364 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12366 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12368
12369 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12373
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12378
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12383}
12384
12385void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12386{
12387 AssertReturnVoid(isWriteLockOnCurrentThread());
12388
12389 if (aCollector)
12390 {
12391 aCollector->unregisterMetricsFor(aMachine);
12392 aCollector->unregisterBaseMetricsFor(aMachine);
12393 }
12394}
12395
12396#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12397
12398
12399////////////////////////////////////////////////////////////////////////////////
12400
12401DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12402
12403HRESULT SessionMachine::FinalConstruct()
12404{
12405 LogFlowThisFunc(("\n"));
12406
12407 mClientToken = NULL;
12408
12409 return BaseFinalConstruct();
12410}
12411
12412void SessionMachine::FinalRelease()
12413{
12414 LogFlowThisFunc(("\n"));
12415
12416 Assert(!mClientToken);
12417 /* paranoia, should not hang around any more */
12418 if (mClientToken)
12419 {
12420 delete mClientToken;
12421 mClientToken = NULL;
12422 }
12423
12424 uninit(Uninit::Unexpected);
12425
12426 BaseFinalRelease();
12427}
12428
12429/**
12430 * @note Must be called only by Machine::LockMachine() from its own write lock.
12431 */
12432HRESULT SessionMachine::init(Machine *aMachine)
12433{
12434 LogFlowThisFuncEnter();
12435 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12436
12437 AssertReturn(aMachine, E_INVALIDARG);
12438
12439 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12440
12441 /* Enclose the state transition NotReady->InInit->Ready */
12442 AutoInitSpan autoInitSpan(this);
12443 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12444
12445 HRESULT rc = S_OK;
12446
12447 RT_ZERO(mAuthLibCtx);
12448
12449 /* create the machine client token */
12450 try
12451 {
12452 mClientToken = new ClientToken(aMachine, this);
12453 if (!mClientToken->isReady())
12454 {
12455 delete mClientToken;
12456 mClientToken = NULL;
12457 rc = E_FAIL;
12458 }
12459 }
12460 catch (std::bad_alloc &)
12461 {
12462 rc = E_OUTOFMEMORY;
12463 }
12464 if (FAILED(rc))
12465 return rc;
12466
12467 /* memorize the peer Machine */
12468 unconst(mPeer) = aMachine;
12469 /* share the parent pointer */
12470 unconst(mParent) = aMachine->mParent;
12471
12472 /* take the pointers to data to share */
12473 mData.share(aMachine->mData);
12474 mSSData.share(aMachine->mSSData);
12475
12476 mUserData.share(aMachine->mUserData);
12477 mHWData.share(aMachine->mHWData);
12478 mMediumAttachments.share(aMachine->mMediumAttachments);
12479
12480 mStorageControllers.allocate();
12481 for (StorageControllerList::const_iterator
12482 it = aMachine->mStorageControllers->begin();
12483 it != aMachine->mStorageControllers->end();
12484 ++it)
12485 {
12486 ComObjPtr<StorageController> ctl;
12487 ctl.createObject();
12488 ctl->init(this, *it);
12489 mStorageControllers->push_back(ctl);
12490 }
12491
12492 mUSBControllers.allocate();
12493 for (USBControllerList::const_iterator
12494 it = aMachine->mUSBControllers->begin();
12495 it != aMachine->mUSBControllers->end();
12496 ++it)
12497 {
12498 ComObjPtr<USBController> ctl;
12499 ctl.createObject();
12500 ctl->init(this, *it);
12501 mUSBControllers->push_back(ctl);
12502 }
12503
12504 unconst(mBIOSSettings).createObject();
12505 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12506
12507 unconst(mTrustedPlatformModule).createObject();
12508 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12509
12510 unconst(mNvramStore).createObject();
12511 mNvramStore->init(this, aMachine->mNvramStore);
12512
12513 unconst(mRecordingSettings).createObject();
12514 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12515 /* create another GraphicsAdapter object that will be mutable */
12516 unconst(mGraphicsAdapter).createObject();
12517 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12518 /* create another VRDEServer object that will be mutable */
12519 unconst(mVRDEServer).createObject();
12520 mVRDEServer->init(this, aMachine->mVRDEServer);
12521 /* create another audio adapter object that will be mutable */
12522 unconst(mAudioAdapter).createObject();
12523 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12524 /* create a list of serial ports that will be mutable */
12525 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12526 {
12527 unconst(mSerialPorts[slot]).createObject();
12528 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12529 }
12530 /* create a list of parallel ports that will be mutable */
12531 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12532 {
12533 unconst(mParallelPorts[slot]).createObject();
12534 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12535 }
12536
12537 /* create another USB device filters object that will be mutable */
12538 unconst(mUSBDeviceFilters).createObject();
12539 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12540
12541 /* create a list of network adapters that will be mutable */
12542 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12543 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12544 {
12545 unconst(mNetworkAdapters[slot]).createObject();
12546 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12547 }
12548
12549 /* create another bandwidth control object that will be mutable */
12550 unconst(mBandwidthControl).createObject();
12551 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12552
12553 /* default is to delete saved state on Saved -> PoweredOff transition */
12554 mRemoveSavedState = true;
12555
12556 /* Confirm a successful initialization when it's the case */
12557 autoInitSpan.setSucceeded();
12558
12559 miNATNetworksStarted = 0;
12560
12561 LogFlowThisFuncLeave();
12562 return rc;
12563}
12564
12565/**
12566 * Uninitializes this session object. If the reason is other than
12567 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12568 * or the client watcher code.
12569 *
12570 * @param aReason uninitialization reason
12571 *
12572 * @note Locks mParent + this object for writing.
12573 */
12574void SessionMachine::uninit(Uninit::Reason aReason)
12575{
12576 LogFlowThisFuncEnter();
12577 LogFlowThisFunc(("reason=%d\n", aReason));
12578
12579 /*
12580 * Strongly reference ourselves to prevent this object deletion after
12581 * mData->mSession.mMachine.setNull() below (which can release the last
12582 * reference and call the destructor). Important: this must be done before
12583 * accessing any members (and before AutoUninitSpan that does it as well).
12584 * This self reference will be released as the very last step on return.
12585 */
12586 ComObjPtr<SessionMachine> selfRef;
12587 if (aReason != Uninit::Unexpected)
12588 selfRef = this;
12589
12590 /* Enclose the state transition Ready->InUninit->NotReady */
12591 AutoUninitSpan autoUninitSpan(this);
12592 if (autoUninitSpan.uninitDone())
12593 {
12594 LogFlowThisFunc(("Already uninitialized\n"));
12595 LogFlowThisFuncLeave();
12596 return;
12597 }
12598
12599 if (autoUninitSpan.initFailed())
12600 {
12601 /* We've been called by init() because it's failed. It's not really
12602 * necessary (nor it's safe) to perform the regular uninit sequence
12603 * below, the following is enough.
12604 */
12605 LogFlowThisFunc(("Initialization failed.\n"));
12606 /* destroy the machine client token */
12607 if (mClientToken)
12608 {
12609 delete mClientToken;
12610 mClientToken = NULL;
12611 }
12612 uninitDataAndChildObjects();
12613 mData.free();
12614 unconst(mParent) = NULL;
12615 unconst(mPeer) = NULL;
12616 LogFlowThisFuncLeave();
12617 return;
12618 }
12619
12620 MachineState_T lastState;
12621 {
12622 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12623 lastState = mData->mMachineState;
12624 }
12625 NOREF(lastState);
12626
12627#ifdef VBOX_WITH_USB
12628 // release all captured USB devices, but do this before requesting the locks below
12629 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12630 {
12631 /* Console::captureUSBDevices() is called in the VM process only after
12632 * setting the machine state to Starting or Restoring.
12633 * Console::detachAllUSBDevices() will be called upon successful
12634 * termination. So, we need to release USB devices only if there was
12635 * an abnormal termination of a running VM.
12636 *
12637 * This is identical to SessionMachine::DetachAllUSBDevices except
12638 * for the aAbnormal argument. */
12639 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12640 AssertComRC(rc);
12641 NOREF(rc);
12642
12643 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12644 if (service)
12645 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12646 }
12647#endif /* VBOX_WITH_USB */
12648
12649 // we need to lock this object in uninit() because the lock is shared
12650 // with mPeer (as well as data we modify below). mParent lock is needed
12651 // by several calls to it.
12652 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12653
12654#ifdef VBOX_WITH_RESOURCE_USAGE_API
12655 /*
12656 * It is safe to call Machine::i_unregisterMetrics() here because
12657 * PerformanceCollector::samplerCallback no longer accesses guest methods
12658 * holding the lock.
12659 */
12660 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12661 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12662 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12663 if (mCollectorGuest)
12664 {
12665 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12666 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12667 mCollectorGuest = NULL;
12668 }
12669#endif
12670
12671 if (aReason == Uninit::Abnormal)
12672 {
12673 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12674
12675 /*
12676 * Move the VM to the 'Aborted' machine state unless we are restoring a
12677 * VM that was in the 'Saved' machine state. In that case, if the VM
12678 * fails before reaching either the 'Restoring' machine state or the
12679 * 'Running' machine state then we set the machine state to
12680 * 'AbortedSaved' in order to preserve the saved state file so that the
12681 * VM can be restored in the future.
12682 */
12683 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12684 i_setMachineState(MachineState_AbortedSaved);
12685 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12686 i_setMachineState(MachineState_Aborted);
12687 }
12688
12689 // any machine settings modified?
12690 if (mData->flModifications)
12691 {
12692 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12693 i_rollback(false /* aNotify */);
12694 }
12695
12696 mData->mSession.mPID = NIL_RTPROCESS;
12697
12698 if (aReason == Uninit::Unexpected)
12699 {
12700 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12701 * client watcher thread to update the set of machines that have open
12702 * sessions. */
12703 mParent->i_updateClientWatcher();
12704 }
12705
12706 /* uninitialize all remote controls */
12707 if (mData->mSession.mRemoteControls.size())
12708 {
12709 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12710 mData->mSession.mRemoteControls.size()));
12711
12712 /* Always restart a the beginning, since the iterator is invalidated
12713 * by using erase(). */
12714 for (Data::Session::RemoteControlList::iterator
12715 it = mData->mSession.mRemoteControls.begin();
12716 it != mData->mSession.mRemoteControls.end();
12717 it = mData->mSession.mRemoteControls.begin())
12718 {
12719 ComPtr<IInternalSessionControl> pControl = *it;
12720 mData->mSession.mRemoteControls.erase(it);
12721 multilock.release();
12722 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12723 HRESULT rc = pControl->Uninitialize();
12724 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12725 if (FAILED(rc))
12726 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12727 multilock.acquire();
12728 }
12729 mData->mSession.mRemoteControls.clear();
12730 }
12731
12732 /* Remove all references to the NAT network service. The service will stop
12733 * if all references (also from other VMs) are removed. */
12734 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12735 {
12736 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12737 {
12738 BOOL enabled;
12739 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12740 if ( FAILED(hrc)
12741 || !enabled)
12742 continue;
12743
12744 NetworkAttachmentType_T type;
12745 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12746 if ( SUCCEEDED(hrc)
12747 && type == NetworkAttachmentType_NATNetwork)
12748 {
12749 Bstr name;
12750 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12751 if (SUCCEEDED(hrc))
12752 {
12753 multilock.release();
12754 Utf8Str strName(name);
12755 LogRel(("VM '%s' stops using NAT network '%s'\n",
12756 mUserData->s.strName.c_str(), strName.c_str()));
12757 mParent->i_natNetworkRefDec(strName);
12758 multilock.acquire();
12759 }
12760 }
12761 }
12762 }
12763
12764 /*
12765 * An expected uninitialization can come only from #i_checkForDeath().
12766 * Otherwise it means that something's gone really wrong (for example,
12767 * the Session implementation has released the VirtualBox reference
12768 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12769 * etc). However, it's also possible, that the client releases the IPC
12770 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12771 * but the VirtualBox release event comes first to the server process.
12772 * This case is practically possible, so we should not assert on an
12773 * unexpected uninit, just log a warning.
12774 */
12775
12776 if (aReason == Uninit::Unexpected)
12777 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12778
12779 if (aReason != Uninit::Normal)
12780 {
12781 mData->mSession.mDirectControl.setNull();
12782 }
12783 else
12784 {
12785 /* this must be null here (see #OnSessionEnd()) */
12786 Assert(mData->mSession.mDirectControl.isNull());
12787 Assert(mData->mSession.mState == SessionState_Unlocking);
12788 Assert(!mData->mSession.mProgress.isNull());
12789 }
12790 if (mData->mSession.mProgress)
12791 {
12792 if (aReason == Uninit::Normal)
12793 mData->mSession.mProgress->i_notifyComplete(S_OK);
12794 else
12795 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12796 COM_IIDOF(ISession),
12797 getComponentName(),
12798 tr("The VM session was aborted"));
12799 mData->mSession.mProgress.setNull();
12800 }
12801
12802 if (mConsoleTaskData.mProgress)
12803 {
12804 Assert(aReason == Uninit::Abnormal);
12805 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12806 COM_IIDOF(ISession),
12807 getComponentName(),
12808 tr("The VM session was aborted"));
12809 mConsoleTaskData.mProgress.setNull();
12810 }
12811
12812 /* remove the association between the peer machine and this session machine */
12813 Assert( (SessionMachine*)mData->mSession.mMachine == this
12814 || aReason == Uninit::Unexpected);
12815
12816 /* reset the rest of session data */
12817 mData->mSession.mLockType = LockType_Null;
12818 mData->mSession.mMachine.setNull();
12819 mData->mSession.mState = SessionState_Unlocked;
12820 mData->mSession.mName.setNull();
12821
12822 /* destroy the machine client token before leaving the exclusive lock */
12823 if (mClientToken)
12824 {
12825 delete mClientToken;
12826 mClientToken = NULL;
12827 }
12828
12829 /* fire an event */
12830 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12831
12832 uninitDataAndChildObjects();
12833
12834 /* free the essential data structure last */
12835 mData.free();
12836
12837 /* release the exclusive lock before setting the below two to NULL */
12838 multilock.release();
12839
12840 unconst(mParent) = NULL;
12841 unconst(mPeer) = NULL;
12842
12843 AuthLibUnload(&mAuthLibCtx);
12844
12845 LogFlowThisFuncLeave();
12846}
12847
12848// util::Lockable interface
12849////////////////////////////////////////////////////////////////////////////////
12850
12851/**
12852 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12853 * with the primary Machine instance (mPeer).
12854 */
12855RWLockHandle *SessionMachine::lockHandle() const
12856{
12857 AssertReturn(mPeer != NULL, NULL);
12858 return mPeer->lockHandle();
12859}
12860
12861// IInternalMachineControl methods
12862////////////////////////////////////////////////////////////////////////////////
12863
12864/**
12865 * Passes collected guest statistics to performance collector object
12866 */
12867HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12868 ULONG aCpuKernel, ULONG aCpuIdle,
12869 ULONG aMemTotal, ULONG aMemFree,
12870 ULONG aMemBalloon, ULONG aMemShared,
12871 ULONG aMemCache, ULONG aPageTotal,
12872 ULONG aAllocVMM, ULONG aFreeVMM,
12873 ULONG aBalloonedVMM, ULONG aSharedVMM,
12874 ULONG aVmNetRx, ULONG aVmNetTx)
12875{
12876#ifdef VBOX_WITH_RESOURCE_USAGE_API
12877 if (mCollectorGuest)
12878 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12879 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12880 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12881 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12882
12883 return S_OK;
12884#else
12885 NOREF(aValidStats);
12886 NOREF(aCpuUser);
12887 NOREF(aCpuKernel);
12888 NOREF(aCpuIdle);
12889 NOREF(aMemTotal);
12890 NOREF(aMemFree);
12891 NOREF(aMemBalloon);
12892 NOREF(aMemShared);
12893 NOREF(aMemCache);
12894 NOREF(aPageTotal);
12895 NOREF(aAllocVMM);
12896 NOREF(aFreeVMM);
12897 NOREF(aBalloonedVMM);
12898 NOREF(aSharedVMM);
12899 NOREF(aVmNetRx);
12900 NOREF(aVmNetTx);
12901 return E_NOTIMPL;
12902#endif
12903}
12904
12905////////////////////////////////////////////////////////////////////////////////
12906//
12907// SessionMachine task records
12908//
12909////////////////////////////////////////////////////////////////////////////////
12910
12911/**
12912 * Task record for saving the machine state.
12913 */
12914class SessionMachine::SaveStateTask
12915 : public Machine::Task
12916{
12917public:
12918 SaveStateTask(SessionMachine *m,
12919 Progress *p,
12920 const Utf8Str &t,
12921 Reason_T enmReason,
12922 const Utf8Str &strStateFilePath)
12923 : Task(m, p, t),
12924 m_enmReason(enmReason),
12925 m_strStateFilePath(strStateFilePath)
12926 {}
12927
12928private:
12929 void handler()
12930 {
12931 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12932 }
12933
12934 Reason_T m_enmReason;
12935 Utf8Str m_strStateFilePath;
12936
12937 friend class SessionMachine;
12938};
12939
12940/**
12941 * Task thread implementation for SessionMachine::SaveState(), called from
12942 * SessionMachine::taskHandler().
12943 *
12944 * @note Locks this object for writing.
12945 *
12946 * @param task
12947 * @return
12948 */
12949void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12950{
12951 LogFlowThisFuncEnter();
12952
12953 AutoCaller autoCaller(this);
12954 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12955 if (FAILED(autoCaller.rc()))
12956 {
12957 /* we might have been uninitialized because the session was accidentally
12958 * closed by the client, so don't assert */
12959 HRESULT rc = setError(E_FAIL,
12960 tr("The session has been accidentally closed"));
12961 task.m_pProgress->i_notifyComplete(rc);
12962 LogFlowThisFuncLeave();
12963 return;
12964 }
12965
12966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12967
12968 HRESULT rc = S_OK;
12969
12970 try
12971 {
12972 ComPtr<IInternalSessionControl> directControl;
12973 if (mData->mSession.mLockType == LockType_VM)
12974 directControl = mData->mSession.mDirectControl;
12975 if (directControl.isNull())
12976 throw setError(VBOX_E_INVALID_VM_STATE,
12977 tr("Trying to save state without a running VM"));
12978 alock.release();
12979 BOOL fSuspendedBySave;
12980 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12981 Assert(!fSuspendedBySave);
12982 alock.acquire();
12983
12984 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12985 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12986 throw E_FAIL);
12987
12988 if (SUCCEEDED(rc))
12989 {
12990 mSSData->strStateFilePath = task.m_strStateFilePath;
12991
12992 /* save all VM settings */
12993 rc = i_saveSettings(NULL);
12994 // no need to check whether VirtualBox.xml needs saving also since
12995 // we can't have a name change pending at this point
12996 }
12997 else
12998 {
12999 // On failure, set the state to the state we had at the beginning.
13000 i_setMachineState(task.m_machineStateBackup);
13001 i_updateMachineStateOnClient();
13002
13003 // Delete the saved state file (might have been already created).
13004 // No need to check whether this is shared with a snapshot here
13005 // because we certainly created a fresh saved state file here.
13006 RTFileDelete(task.m_strStateFilePath.c_str());
13007 }
13008 }
13009 catch (HRESULT aRC) { rc = aRC; }
13010
13011 task.m_pProgress->i_notifyComplete(rc);
13012
13013 LogFlowThisFuncLeave();
13014}
13015
13016/**
13017 * @note Locks this object for writing.
13018 */
13019HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13020{
13021 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13022}
13023
13024HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13025{
13026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13027
13028 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13029 if (FAILED(rc)) return rc;
13030
13031 if ( mData->mMachineState != MachineState_Running
13032 && mData->mMachineState != MachineState_Paused
13033 )
13034 return setError(VBOX_E_INVALID_VM_STATE,
13035 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13036 Global::stringifyMachineState(mData->mMachineState));
13037
13038 ComObjPtr<Progress> pProgress;
13039 pProgress.createObject();
13040 rc = pProgress->init(i_getVirtualBox(),
13041 static_cast<IMachine *>(this) /* aInitiator */,
13042 tr("Saving the execution state of the virtual machine"),
13043 FALSE /* aCancelable */);
13044 if (FAILED(rc))
13045 return rc;
13046
13047 Utf8Str strStateFilePath;
13048 i_composeSavedStateFilename(strStateFilePath);
13049
13050 /* create and start the task on a separate thread (note that it will not
13051 * start working until we release alock) */
13052 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13053 rc = pTask->createThread();
13054 if (FAILED(rc))
13055 return rc;
13056
13057 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13058 i_setMachineState(MachineState_Saving);
13059 i_updateMachineStateOnClient();
13060
13061 pProgress.queryInterfaceTo(aProgress.asOutParam());
13062
13063 return S_OK;
13064}
13065
13066/**
13067 * @note Locks this object for writing.
13068 */
13069HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13070{
13071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13072
13073 HRESULT rc = i_checkStateDependency(MutableStateDep);
13074 if (FAILED(rc)) return rc;
13075
13076 if ( mData->mMachineState != MachineState_PoweredOff
13077 && mData->mMachineState != MachineState_Teleported
13078 && mData->mMachineState != MachineState_Aborted
13079 )
13080 return setError(VBOX_E_INVALID_VM_STATE,
13081 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13082 Global::stringifyMachineState(mData->mMachineState));
13083
13084 com::Utf8Str stateFilePathFull;
13085 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13086 if (RT_FAILURE(vrc))
13087 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13088 tr("Invalid saved state file path '%s' (%Rrc)"),
13089 aSavedStateFile.c_str(),
13090 vrc);
13091
13092 mSSData->strStateFilePath = stateFilePathFull;
13093
13094 /* The below i_setMachineState() will detect the state transition and will
13095 * update the settings file */
13096
13097 return i_setMachineState(MachineState_Saved);
13098}
13099
13100/**
13101 * @note Locks this object for writing.
13102 */
13103HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13104{
13105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13106
13107 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13108 if (FAILED(rc)) return rc;
13109
13110 if ( mData->mMachineState != MachineState_Saved
13111 && mData->mMachineState != MachineState_AbortedSaved)
13112 return setError(VBOX_E_INVALID_VM_STATE,
13113 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13114 Global::stringifyMachineState(mData->mMachineState));
13115
13116 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13117
13118 /*
13119 * Saved -> PoweredOff transition will be detected in the SessionMachine
13120 * and properly handled.
13121 */
13122 rc = i_setMachineState(MachineState_PoweredOff);
13123 return rc;
13124}
13125
13126
13127/**
13128 * @note Locks the same as #i_setMachineState() does.
13129 */
13130HRESULT SessionMachine::updateState(MachineState_T aState)
13131{
13132 return i_setMachineState(aState);
13133}
13134
13135/**
13136 * @note Locks this object for writing.
13137 */
13138HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13139{
13140 IProgress *pProgress(aProgress);
13141
13142 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13143
13144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13145
13146 if (mData->mSession.mState != SessionState_Locked)
13147 return VBOX_E_INVALID_OBJECT_STATE;
13148
13149 if (!mData->mSession.mProgress.isNull())
13150 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13151
13152 /* If we didn't reference the NAT network service yet, add a reference to
13153 * force a start */
13154 if (miNATNetworksStarted < 1)
13155 {
13156 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13157 {
13158 BOOL enabled;
13159 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13160 if ( FAILED(hrc)
13161 || !enabled)
13162 continue;
13163
13164 NetworkAttachmentType_T type;
13165 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13166 if ( SUCCEEDED(hrc)
13167 && type == NetworkAttachmentType_NATNetwork)
13168 {
13169 Bstr name;
13170 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13171 if (SUCCEEDED(hrc))
13172 {
13173 Utf8Str strName(name);
13174 LogRel(("VM '%s' starts using NAT network '%s'\n",
13175 mUserData->s.strName.c_str(), strName.c_str()));
13176 mPeer->lockHandle()->unlockWrite();
13177 mParent->i_natNetworkRefInc(strName);
13178#ifdef RT_LOCK_STRICT
13179 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13180#else
13181 mPeer->lockHandle()->lockWrite();
13182#endif
13183 }
13184 }
13185 }
13186 miNATNetworksStarted++;
13187 }
13188
13189 LogFlowThisFunc(("returns S_OK.\n"));
13190 return S_OK;
13191}
13192
13193/**
13194 * @note Locks this object for writing.
13195 */
13196HRESULT SessionMachine::endPowerUp(LONG aResult)
13197{
13198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13199
13200 if (mData->mSession.mState != SessionState_Locked)
13201 return VBOX_E_INVALID_OBJECT_STATE;
13202
13203 /* Finalize the LaunchVMProcess progress object. */
13204 if (mData->mSession.mProgress)
13205 {
13206 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13207 mData->mSession.mProgress.setNull();
13208 }
13209
13210 if (SUCCEEDED((HRESULT)aResult))
13211 {
13212#ifdef VBOX_WITH_RESOURCE_USAGE_API
13213 /* The VM has been powered up successfully, so it makes sense
13214 * now to offer the performance metrics for a running machine
13215 * object. Doing it earlier wouldn't be safe. */
13216 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13217 mData->mSession.mPID);
13218#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13219 }
13220
13221 return S_OK;
13222}
13223
13224/**
13225 * @note Locks this object for writing.
13226 */
13227HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13228{
13229 LogFlowThisFuncEnter();
13230
13231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13232
13233 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13234 E_FAIL);
13235
13236 /* create a progress object to track operation completion */
13237 ComObjPtr<Progress> pProgress;
13238 pProgress.createObject();
13239 pProgress->init(i_getVirtualBox(),
13240 static_cast<IMachine *>(this) /* aInitiator */,
13241 tr("Stopping the virtual machine"),
13242 FALSE /* aCancelable */);
13243
13244 /* fill in the console task data */
13245 mConsoleTaskData.mLastState = mData->mMachineState;
13246 mConsoleTaskData.mProgress = pProgress;
13247
13248 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13249 i_setMachineState(MachineState_Stopping);
13250
13251 pProgress.queryInterfaceTo(aProgress.asOutParam());
13252
13253 return S_OK;
13254}
13255
13256/**
13257 * @note Locks this object for writing.
13258 */
13259HRESULT SessionMachine::endPoweringDown(LONG aResult,
13260 const com::Utf8Str &aErrMsg)
13261{
13262 HRESULT const hrcResult = (HRESULT)aResult;
13263 LogFlowThisFuncEnter();
13264
13265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13266
13267 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13268 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13269 && mConsoleTaskData.mLastState != MachineState_Null,
13270 E_FAIL);
13271
13272 /*
13273 * On failure, set the state to the state we had when BeginPoweringDown()
13274 * was called (this is expected by Console::PowerDown() and the associated
13275 * task). On success the VM process already changed the state to
13276 * MachineState_PoweredOff, so no need to do anything.
13277 */
13278 if (FAILED(hrcResult))
13279 i_setMachineState(mConsoleTaskData.mLastState);
13280
13281 /* notify the progress object about operation completion */
13282 Assert(mConsoleTaskData.mProgress);
13283 if (SUCCEEDED(hrcResult))
13284 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13285 else
13286 {
13287 if (aErrMsg.length())
13288 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13289 COM_IIDOF(ISession),
13290 getComponentName(),
13291 aErrMsg.c_str());
13292 else
13293 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13294 }
13295
13296 /* clear out the temporary saved state data */
13297 mConsoleTaskData.mLastState = MachineState_Null;
13298 mConsoleTaskData.mProgress.setNull();
13299
13300 LogFlowThisFuncLeave();
13301 return S_OK;
13302}
13303
13304
13305/**
13306 * Goes through the USB filters of the given machine to see if the given
13307 * device matches any filter or not.
13308 *
13309 * @note Locks the same as USBController::hasMatchingFilter() does.
13310 */
13311HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13312 BOOL *aMatched,
13313 ULONG *aMaskedInterfaces)
13314{
13315 LogFlowThisFunc(("\n"));
13316
13317#ifdef VBOX_WITH_USB
13318 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13319#else
13320 NOREF(aDevice);
13321 NOREF(aMaskedInterfaces);
13322 *aMatched = FALSE;
13323#endif
13324
13325 return S_OK;
13326}
13327
13328/**
13329 * @note Locks the same as Host::captureUSBDevice() does.
13330 */
13331HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13332{
13333 LogFlowThisFunc(("\n"));
13334
13335#ifdef VBOX_WITH_USB
13336 /* if captureDeviceForVM() fails, it must have set extended error info */
13337 clearError();
13338 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13339 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13340 return rc;
13341
13342 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13343 AssertReturn(service, E_FAIL);
13344 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13345#else
13346 RT_NOREF(aId, aCaptureFilename);
13347 return E_NOTIMPL;
13348#endif
13349}
13350
13351/**
13352 * @note Locks the same as Host::detachUSBDevice() does.
13353 */
13354HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13355 BOOL aDone)
13356{
13357 LogFlowThisFunc(("\n"));
13358
13359#ifdef VBOX_WITH_USB
13360 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13361 AssertReturn(service, E_FAIL);
13362 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13363#else
13364 NOREF(aId);
13365 NOREF(aDone);
13366 return E_NOTIMPL;
13367#endif
13368}
13369
13370/**
13371 * Inserts all machine filters to the USB proxy service and then calls
13372 * Host::autoCaptureUSBDevices().
13373 *
13374 * Called by Console from the VM process upon VM startup.
13375 *
13376 * @note Locks what called methods lock.
13377 */
13378HRESULT SessionMachine::autoCaptureUSBDevices()
13379{
13380 LogFlowThisFunc(("\n"));
13381
13382#ifdef VBOX_WITH_USB
13383 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13384 AssertComRC(rc);
13385 NOREF(rc);
13386
13387 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13388 AssertReturn(service, E_FAIL);
13389 return service->autoCaptureDevicesForVM(this);
13390#else
13391 return S_OK;
13392#endif
13393}
13394
13395/**
13396 * Removes all machine filters from the USB proxy service and then calls
13397 * Host::detachAllUSBDevices().
13398 *
13399 * Called by Console from the VM process upon normal VM termination or by
13400 * SessionMachine::uninit() upon abnormal VM termination (from under the
13401 * Machine/SessionMachine lock).
13402 *
13403 * @note Locks what called methods lock.
13404 */
13405HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13406{
13407 LogFlowThisFunc(("\n"));
13408
13409#ifdef VBOX_WITH_USB
13410 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13411 AssertComRC(rc);
13412 NOREF(rc);
13413
13414 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13415 AssertReturn(service, E_FAIL);
13416 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13417#else
13418 NOREF(aDone);
13419 return S_OK;
13420#endif
13421}
13422
13423/**
13424 * @note Locks this object for writing.
13425 */
13426HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13427 ComPtr<IProgress> &aProgress)
13428{
13429 LogFlowThisFuncEnter();
13430
13431 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13432 /*
13433 * We don't assert below because it might happen that a non-direct session
13434 * informs us it is closed right after we've been uninitialized -- it's ok.
13435 */
13436
13437 /* get IInternalSessionControl interface */
13438 ComPtr<IInternalSessionControl> control(aSession);
13439
13440 ComAssertRet(!control.isNull(), E_INVALIDARG);
13441
13442 /* Creating a Progress object requires the VirtualBox lock, and
13443 * thus locking it here is required by the lock order rules. */
13444 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13445
13446 if (control == mData->mSession.mDirectControl)
13447 {
13448 /* The direct session is being normally closed by the client process
13449 * ----------------------------------------------------------------- */
13450
13451 /* go to the closing state (essential for all open*Session() calls and
13452 * for #i_checkForDeath()) */
13453 Assert(mData->mSession.mState == SessionState_Locked);
13454 mData->mSession.mState = SessionState_Unlocking;
13455
13456 /* set direct control to NULL to release the remote instance */
13457 mData->mSession.mDirectControl.setNull();
13458 LogFlowThisFunc(("Direct control is set to NULL\n"));
13459
13460 if (mData->mSession.mProgress)
13461 {
13462 /* finalize the progress, someone might wait if a frontend
13463 * closes the session before powering on the VM. */
13464 mData->mSession.mProgress->notifyComplete(E_FAIL,
13465 COM_IIDOF(ISession),
13466 getComponentName(),
13467 tr("The VM session was closed before any attempt to power it on"));
13468 mData->mSession.mProgress.setNull();
13469 }
13470
13471 /* Create the progress object the client will use to wait until
13472 * #i_checkForDeath() is called to uninitialize this session object after
13473 * it releases the IPC semaphore.
13474 * Note! Because we're "reusing" mProgress here, this must be a proxy
13475 * object just like for LaunchVMProcess. */
13476 Assert(mData->mSession.mProgress.isNull());
13477 ComObjPtr<ProgressProxy> progress;
13478 progress.createObject();
13479 ComPtr<IUnknown> pPeer(mPeer);
13480 progress->init(mParent, pPeer,
13481 Bstr(tr("Closing session")).raw(),
13482 FALSE /* aCancelable */);
13483 progress.queryInterfaceTo(aProgress.asOutParam());
13484 mData->mSession.mProgress = progress;
13485 }
13486 else
13487 {
13488 /* the remote session is being normally closed */
13489 bool found = false;
13490 for (Data::Session::RemoteControlList::iterator
13491 it = mData->mSession.mRemoteControls.begin();
13492 it != mData->mSession.mRemoteControls.end();
13493 ++it)
13494 {
13495 if (control == *it)
13496 {
13497 found = true;
13498 // This MUST be erase(it), not remove(*it) as the latter
13499 // triggers a very nasty use after free due to the place where
13500 // the value "lives".
13501 mData->mSession.mRemoteControls.erase(it);
13502 break;
13503 }
13504 }
13505 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13506 E_INVALIDARG);
13507 }
13508
13509 /* signal the client watcher thread, because the client is going away */
13510 mParent->i_updateClientWatcher();
13511
13512 LogFlowThisFuncLeave();
13513 return S_OK;
13514}
13515
13516HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13517 std::vector<com::Utf8Str> &aValues,
13518 std::vector<LONG64> &aTimestamps,
13519 std::vector<com::Utf8Str> &aFlags)
13520{
13521 LogFlowThisFunc(("\n"));
13522
13523#ifdef VBOX_WITH_GUEST_PROPS
13524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13525
13526 size_t cEntries = mHWData->mGuestProperties.size();
13527 aNames.resize(cEntries);
13528 aValues.resize(cEntries);
13529 aTimestamps.resize(cEntries);
13530 aFlags.resize(cEntries);
13531
13532 size_t i = 0;
13533 for (HWData::GuestPropertyMap::const_iterator
13534 it = mHWData->mGuestProperties.begin();
13535 it != mHWData->mGuestProperties.end();
13536 ++it, ++i)
13537 {
13538 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13539 aNames[i] = it->first;
13540 aValues[i] = it->second.strValue;
13541 aTimestamps[i] = it->second.mTimestamp;
13542
13543 /* If it is NULL, keep it NULL. */
13544 if (it->second.mFlags)
13545 {
13546 GuestPropWriteFlags(it->second.mFlags, szFlags);
13547 aFlags[i] = szFlags;
13548 }
13549 else
13550 aFlags[i] = "";
13551 }
13552 return S_OK;
13553#else
13554 ReturnComNotImplemented();
13555#endif
13556}
13557
13558HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13559 const com::Utf8Str &aValue,
13560 LONG64 aTimestamp,
13561 const com::Utf8Str &aFlags)
13562{
13563 LogFlowThisFunc(("\n"));
13564
13565#ifdef VBOX_WITH_GUEST_PROPS
13566 try
13567 {
13568 /*
13569 * Convert input up front.
13570 */
13571 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13572 if (aFlags.length())
13573 {
13574 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13575 AssertRCReturn(vrc, E_INVALIDARG);
13576 }
13577
13578 /*
13579 * Now grab the object lock, validate the state and do the update.
13580 */
13581
13582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13583
13584 if (!Global::IsOnline(mData->mMachineState))
13585 {
13586 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13587 VBOX_E_INVALID_VM_STATE);
13588 }
13589
13590 i_setModified(IsModified_MachineData);
13591 mHWData.backup();
13592
13593 bool fDelete = !aValue.length();
13594 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13595 if (it != mHWData->mGuestProperties.end())
13596 {
13597 if (!fDelete)
13598 {
13599 it->second.strValue = aValue;
13600 it->second.mTimestamp = aTimestamp;
13601 it->second.mFlags = fFlags;
13602 }
13603 else
13604 mHWData->mGuestProperties.erase(it);
13605
13606 mData->mGuestPropertiesModified = TRUE;
13607 }
13608 else if (!fDelete)
13609 {
13610 HWData::GuestProperty prop;
13611 prop.strValue = aValue;
13612 prop.mTimestamp = aTimestamp;
13613 prop.mFlags = fFlags;
13614
13615 mHWData->mGuestProperties[aName] = prop;
13616 mData->mGuestPropertiesModified = TRUE;
13617 }
13618
13619 alock.release();
13620
13621 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13622 }
13623 catch (...)
13624 {
13625 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13626 }
13627 return S_OK;
13628#else
13629 ReturnComNotImplemented();
13630#endif
13631}
13632
13633
13634HRESULT SessionMachine::lockMedia()
13635{
13636 AutoMultiWriteLock2 alock(this->lockHandle(),
13637 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13638
13639 AssertReturn( mData->mMachineState == MachineState_Starting
13640 || mData->mMachineState == MachineState_Restoring
13641 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13642
13643 clearError();
13644 alock.release();
13645 return i_lockMedia();
13646}
13647
13648HRESULT SessionMachine::unlockMedia()
13649{
13650 HRESULT hrc = i_unlockMedia();
13651 return hrc;
13652}
13653
13654HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13655 ComPtr<IMediumAttachment> &aNewAttachment)
13656{
13657 // request the host lock first, since might be calling Host methods for getting host drives;
13658 // next, protect the media tree all the while we're in here, as well as our member variables
13659 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13660 this->lockHandle(),
13661 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13662
13663 IMediumAttachment *iAttach = aAttachment;
13664 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13665
13666 Utf8Str ctrlName;
13667 LONG lPort;
13668 LONG lDevice;
13669 bool fTempEject;
13670 {
13671 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13672
13673 /* Need to query the details first, as the IMediumAttachment reference
13674 * might be to the original settings, which we are going to change. */
13675 ctrlName = pAttach->i_getControllerName();
13676 lPort = pAttach->i_getPort();
13677 lDevice = pAttach->i_getDevice();
13678 fTempEject = pAttach->i_getTempEject();
13679 }
13680
13681 if (!fTempEject)
13682 {
13683 /* Remember previously mounted medium. The medium before taking the
13684 * backup is not necessarily the same thing. */
13685 ComObjPtr<Medium> oldmedium;
13686 oldmedium = pAttach->i_getMedium();
13687
13688 i_setModified(IsModified_Storage);
13689 mMediumAttachments.backup();
13690
13691 // The backup operation makes the pAttach reference point to the
13692 // old settings. Re-get the correct reference.
13693 pAttach = i_findAttachment(*mMediumAttachments.data(),
13694 ctrlName,
13695 lPort,
13696 lDevice);
13697
13698 {
13699 AutoCaller autoAttachCaller(this);
13700 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13701
13702 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13703 if (!oldmedium.isNull())
13704 oldmedium->i_removeBackReference(mData->mUuid);
13705
13706 pAttach->i_updateMedium(NULL);
13707 pAttach->i_updateEjected();
13708 }
13709
13710 i_setModified(IsModified_Storage);
13711 }
13712 else
13713 {
13714 {
13715 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13716 pAttach->i_updateEjected();
13717 }
13718 }
13719
13720 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13721
13722 return S_OK;
13723}
13724
13725HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13726 com::Utf8Str &aResult)
13727{
13728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13729
13730 HRESULT hr = S_OK;
13731
13732 if (!mAuthLibCtx.hAuthLibrary)
13733 {
13734 /* Load the external authentication library. */
13735 Bstr authLibrary;
13736 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13737
13738 Utf8Str filename = authLibrary;
13739
13740 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13741 if (RT_FAILURE(vrc))
13742 hr = setErrorBoth(E_FAIL, vrc,
13743 tr("Could not load the external authentication library '%s' (%Rrc)"),
13744 filename.c_str(), vrc);
13745 }
13746
13747 /* The auth library might need the machine lock. */
13748 alock.release();
13749
13750 if (FAILED(hr))
13751 return hr;
13752
13753 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13754 {
13755 enum VRDEAuthParams
13756 {
13757 parmUuid = 1,
13758 parmGuestJudgement,
13759 parmUser,
13760 parmPassword,
13761 parmDomain,
13762 parmClientId
13763 };
13764
13765 AuthResult result = AuthResultAccessDenied;
13766
13767 Guid uuid(aAuthParams[parmUuid]);
13768 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13769 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13770
13771 result = AuthLibAuthenticate(&mAuthLibCtx,
13772 uuid.raw(), guestJudgement,
13773 aAuthParams[parmUser].c_str(),
13774 aAuthParams[parmPassword].c_str(),
13775 aAuthParams[parmDomain].c_str(),
13776 u32ClientId);
13777
13778 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13779 size_t cbPassword = aAuthParams[parmPassword].length();
13780 if (cbPassword)
13781 {
13782 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13783 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13784 }
13785
13786 if (result == AuthResultAccessGranted)
13787 aResult = "granted";
13788 else
13789 aResult = "denied";
13790
13791 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13792 aAuthParams[parmUser].c_str(), aResult.c_str()));
13793 }
13794 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13795 {
13796 enum VRDEAuthDisconnectParams
13797 {
13798 parmUuid = 1,
13799 parmClientId
13800 };
13801
13802 Guid uuid(aAuthParams[parmUuid]);
13803 uint32_t u32ClientId = 0;
13804 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13805 }
13806 else
13807 {
13808 hr = E_INVALIDARG;
13809 }
13810
13811 return hr;
13812}
13813
13814// public methods only for internal purposes
13815/////////////////////////////////////////////////////////////////////////////
13816
13817#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13818/**
13819 * Called from the client watcher thread to check for expected or unexpected
13820 * death of the client process that has a direct session to this machine.
13821 *
13822 * On Win32 and on OS/2, this method is called only when we've got the
13823 * mutex (i.e. the client has either died or terminated normally) so it always
13824 * returns @c true (the client is terminated, the session machine is
13825 * uninitialized).
13826 *
13827 * On other platforms, the method returns @c true if the client process has
13828 * terminated normally or abnormally and the session machine was uninitialized,
13829 * and @c false if the client process is still alive.
13830 *
13831 * @note Locks this object for writing.
13832 */
13833bool SessionMachine::i_checkForDeath()
13834{
13835 Uninit::Reason reason;
13836 bool terminated = false;
13837
13838 /* Enclose autoCaller with a block because calling uninit() from under it
13839 * will deadlock. */
13840 {
13841 AutoCaller autoCaller(this);
13842 if (!autoCaller.isOk())
13843 {
13844 /* return true if not ready, to cause the client watcher to exclude
13845 * the corresponding session from watching */
13846 LogFlowThisFunc(("Already uninitialized!\n"));
13847 return true;
13848 }
13849
13850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13851
13852 /* Determine the reason of death: if the session state is Closing here,
13853 * everything is fine. Otherwise it means that the client did not call
13854 * OnSessionEnd() before it released the IPC semaphore. This may happen
13855 * either because the client process has abnormally terminated, or
13856 * because it simply forgot to call ISession::Close() before exiting. We
13857 * threat the latter also as an abnormal termination (see
13858 * Session::uninit() for details). */
13859 reason = mData->mSession.mState == SessionState_Unlocking ?
13860 Uninit::Normal :
13861 Uninit::Abnormal;
13862
13863 if (mClientToken)
13864 terminated = mClientToken->release();
13865 } /* AutoCaller block */
13866
13867 if (terminated)
13868 uninit(reason);
13869
13870 return terminated;
13871}
13872
13873void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13874{
13875 LogFlowThisFunc(("\n"));
13876
13877 strTokenId.setNull();
13878
13879 AutoCaller autoCaller(this);
13880 AssertComRCReturnVoid(autoCaller.rc());
13881
13882 Assert(mClientToken);
13883 if (mClientToken)
13884 mClientToken->getId(strTokenId);
13885}
13886#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13887IToken *SessionMachine::i_getToken()
13888{
13889 LogFlowThisFunc(("\n"));
13890
13891 AutoCaller autoCaller(this);
13892 AssertComRCReturn(autoCaller.rc(), NULL);
13893
13894 Assert(mClientToken);
13895 if (mClientToken)
13896 return mClientToken->getToken();
13897 else
13898 return NULL;
13899}
13900#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13901
13902Machine::ClientToken *SessionMachine::i_getClientToken()
13903{
13904 LogFlowThisFunc(("\n"));
13905
13906 AutoCaller autoCaller(this);
13907 AssertComRCReturn(autoCaller.rc(), NULL);
13908
13909 return mClientToken;
13910}
13911
13912
13913/**
13914 * @note Locks this object for reading.
13915 */
13916HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13917{
13918 LogFlowThisFunc(("\n"));
13919
13920 AutoCaller autoCaller(this);
13921 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13922
13923 ComPtr<IInternalSessionControl> directControl;
13924 {
13925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13926 if (mData->mSession.mLockType == LockType_VM)
13927 directControl = mData->mSession.mDirectControl;
13928 }
13929
13930 /* ignore notifications sent after #OnSessionEnd() is called */
13931 if (!directControl)
13932 return S_OK;
13933
13934 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13935}
13936
13937/**
13938 * @note Locks this object for reading.
13939 */
13940HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13941 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13942 const Utf8Str &aGuestIp, LONG aGuestPort)
13943{
13944 LogFlowThisFunc(("\n"));
13945
13946 AutoCaller autoCaller(this);
13947 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13948
13949 ComPtr<IInternalSessionControl> directControl;
13950 {
13951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13952 if (mData->mSession.mLockType == LockType_VM)
13953 directControl = mData->mSession.mDirectControl;
13954 }
13955
13956 /* ignore notifications sent after #OnSessionEnd() is called */
13957 if (!directControl)
13958 return S_OK;
13959 /*
13960 * instead acting like callback we ask IVirtualBox deliver corresponding event
13961 */
13962
13963 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13964 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13965 return S_OK;
13966}
13967
13968/**
13969 * @note Locks this object for reading.
13970 */
13971HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13972{
13973 LogFlowThisFunc(("\n"));
13974
13975 AutoCaller autoCaller(this);
13976 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13977
13978 ComPtr<IInternalSessionControl> directControl;
13979 {
13980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13981 if (mData->mSession.mLockType == LockType_VM)
13982 directControl = mData->mSession.mDirectControl;
13983 }
13984
13985 /* ignore notifications sent after #OnSessionEnd() is called */
13986 if (!directControl)
13987 return S_OK;
13988
13989 return directControl->OnAudioAdapterChange(audioAdapter);
13990}
13991
13992/**
13993 * @note Locks this object for reading.
13994 */
13995HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13996{
13997 LogFlowThisFunc(("\n"));
13998
13999 AutoCaller autoCaller(this);
14000 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14001
14002 ComPtr<IInternalSessionControl> directControl;
14003 {
14004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14005 if (mData->mSession.mLockType == LockType_VM)
14006 directControl = mData->mSession.mDirectControl;
14007 }
14008
14009 /* ignore notifications sent after #OnSessionEnd() is called */
14010 if (!directControl)
14011 return S_OK;
14012
14013 return directControl->OnSerialPortChange(serialPort);
14014}
14015
14016/**
14017 * @note Locks this object for reading.
14018 */
14019HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14020{
14021 LogFlowThisFunc(("\n"));
14022
14023 AutoCaller autoCaller(this);
14024 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14025
14026 ComPtr<IInternalSessionControl> directControl;
14027 {
14028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14029 if (mData->mSession.mLockType == LockType_VM)
14030 directControl = mData->mSession.mDirectControl;
14031 }
14032
14033 /* ignore notifications sent after #OnSessionEnd() is called */
14034 if (!directControl)
14035 return S_OK;
14036
14037 return directControl->OnParallelPortChange(parallelPort);
14038}
14039
14040/**
14041 * @note Locks this object for reading.
14042 */
14043HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14044{
14045 LogFlowThisFunc(("\n"));
14046
14047 AutoCaller autoCaller(this);
14048 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14049
14050 ComPtr<IInternalSessionControl> directControl;
14051 {
14052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14053 if (mData->mSession.mLockType == LockType_VM)
14054 directControl = mData->mSession.mDirectControl;
14055 }
14056
14057 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14058
14059 /* ignore notifications sent after #OnSessionEnd() is called */
14060 if (!directControl)
14061 return S_OK;
14062
14063 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14064}
14065
14066/**
14067 * @note Locks this object for reading.
14068 */
14069HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14070{
14071 LogFlowThisFunc(("\n"));
14072
14073 AutoCaller autoCaller(this);
14074 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14075
14076 ComPtr<IInternalSessionControl> directControl;
14077 {
14078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14079 if (mData->mSession.mLockType == LockType_VM)
14080 directControl = mData->mSession.mDirectControl;
14081 }
14082
14083 mParent->i_onMediumChanged(aAttachment);
14084
14085 /* ignore notifications sent after #OnSessionEnd() is called */
14086 if (!directControl)
14087 return S_OK;
14088
14089 return directControl->OnMediumChange(aAttachment, aForce);
14090}
14091
14092HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14093{
14094 LogFlowThisFunc(("\n"));
14095
14096 AutoCaller autoCaller(this);
14097 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14098
14099 ComPtr<IInternalSessionControl> directControl;
14100 {
14101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14102 if (mData->mSession.mLockType == LockType_VM)
14103 directControl = mData->mSession.mDirectControl;
14104 }
14105
14106 /* ignore notifications sent after #OnSessionEnd() is called */
14107 if (!directControl)
14108 return S_OK;
14109
14110 return directControl->OnVMProcessPriorityChange(aPriority);
14111}
14112
14113/**
14114 * @note Locks this object for reading.
14115 */
14116HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14117{
14118 LogFlowThisFunc(("\n"));
14119
14120 AutoCaller autoCaller(this);
14121 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14122
14123 ComPtr<IInternalSessionControl> directControl;
14124 {
14125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14126 if (mData->mSession.mLockType == LockType_VM)
14127 directControl = mData->mSession.mDirectControl;
14128 }
14129
14130 /* ignore notifications sent after #OnSessionEnd() is called */
14131 if (!directControl)
14132 return S_OK;
14133
14134 return directControl->OnCPUChange(aCPU, aRemove);
14135}
14136
14137HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14138{
14139 LogFlowThisFunc(("\n"));
14140
14141 AutoCaller autoCaller(this);
14142 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14143
14144 ComPtr<IInternalSessionControl> directControl;
14145 {
14146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14147 if (mData->mSession.mLockType == LockType_VM)
14148 directControl = mData->mSession.mDirectControl;
14149 }
14150
14151 /* ignore notifications sent after #OnSessionEnd() is called */
14152 if (!directControl)
14153 return S_OK;
14154
14155 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14156}
14157
14158/**
14159 * @note Locks this object for reading.
14160 */
14161HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14162{
14163 LogFlowThisFunc(("\n"));
14164
14165 AutoCaller autoCaller(this);
14166 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14167
14168 ComPtr<IInternalSessionControl> directControl;
14169 {
14170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14171 if (mData->mSession.mLockType == LockType_VM)
14172 directControl = mData->mSession.mDirectControl;
14173 }
14174
14175 /* ignore notifications sent after #OnSessionEnd() is called */
14176 if (!directControl)
14177 return S_OK;
14178
14179 return directControl->OnVRDEServerChange(aRestart);
14180}
14181
14182/**
14183 * @note Locks this object for reading.
14184 */
14185HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14186{
14187 LogFlowThisFunc(("\n"));
14188
14189 AutoCaller autoCaller(this);
14190 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14191
14192 ComPtr<IInternalSessionControl> directControl;
14193 {
14194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14195 if (mData->mSession.mLockType == LockType_VM)
14196 directControl = mData->mSession.mDirectControl;
14197 }
14198
14199 /* ignore notifications sent after #OnSessionEnd() is called */
14200 if (!directControl)
14201 return S_OK;
14202
14203 return directControl->OnRecordingChange(aEnable);
14204}
14205
14206/**
14207 * @note Locks this object for reading.
14208 */
14209HRESULT SessionMachine::i_onUSBControllerChange()
14210{
14211 LogFlowThisFunc(("\n"));
14212
14213 AutoCaller autoCaller(this);
14214 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14215
14216 ComPtr<IInternalSessionControl> directControl;
14217 {
14218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14219 if (mData->mSession.mLockType == LockType_VM)
14220 directControl = mData->mSession.mDirectControl;
14221 }
14222
14223 /* ignore notifications sent after #OnSessionEnd() is called */
14224 if (!directControl)
14225 return S_OK;
14226
14227 return directControl->OnUSBControllerChange();
14228}
14229
14230/**
14231 * @note Locks this object for reading.
14232 */
14233HRESULT SessionMachine::i_onSharedFolderChange()
14234{
14235 LogFlowThisFunc(("\n"));
14236
14237 AutoCaller autoCaller(this);
14238 AssertComRCReturnRC(autoCaller.rc());
14239
14240 ComPtr<IInternalSessionControl> directControl;
14241 {
14242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14243 if (mData->mSession.mLockType == LockType_VM)
14244 directControl = mData->mSession.mDirectControl;
14245 }
14246
14247 /* ignore notifications sent after #OnSessionEnd() is called */
14248 if (!directControl)
14249 return S_OK;
14250
14251 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14252}
14253
14254/**
14255 * @note Locks this object for reading.
14256 */
14257HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14258{
14259 LogFlowThisFunc(("\n"));
14260
14261 AutoCaller autoCaller(this);
14262 AssertComRCReturnRC(autoCaller.rc());
14263
14264 ComPtr<IInternalSessionControl> directControl;
14265 {
14266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14267 if (mData->mSession.mLockType == LockType_VM)
14268 directControl = mData->mSession.mDirectControl;
14269 }
14270
14271 /* ignore notifications sent after #OnSessionEnd() is called */
14272 if (!directControl)
14273 return S_OK;
14274
14275 return directControl->OnClipboardModeChange(aClipboardMode);
14276}
14277
14278/**
14279 * @note Locks this object for reading.
14280 */
14281HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14282{
14283 LogFlowThisFunc(("\n"));
14284
14285 AutoCaller autoCaller(this);
14286 AssertComRCReturnRC(autoCaller.rc());
14287
14288 ComPtr<IInternalSessionControl> directControl;
14289 {
14290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14291 if (mData->mSession.mLockType == LockType_VM)
14292 directControl = mData->mSession.mDirectControl;
14293 }
14294
14295 /* ignore notifications sent after #OnSessionEnd() is called */
14296 if (!directControl)
14297 return S_OK;
14298
14299 return directControl->OnClipboardFileTransferModeChange(aEnable);
14300}
14301
14302/**
14303 * @note Locks this object for reading.
14304 */
14305HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14306{
14307 LogFlowThisFunc(("\n"));
14308
14309 AutoCaller autoCaller(this);
14310 AssertComRCReturnRC(autoCaller.rc());
14311
14312 ComPtr<IInternalSessionControl> directControl;
14313 {
14314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14315 if (mData->mSession.mLockType == LockType_VM)
14316 directControl = mData->mSession.mDirectControl;
14317 }
14318
14319 /* ignore notifications sent after #OnSessionEnd() is called */
14320 if (!directControl)
14321 return S_OK;
14322
14323 return directControl->OnDnDModeChange(aDnDMode);
14324}
14325
14326/**
14327 * @note Locks this object for reading.
14328 */
14329HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14330{
14331 LogFlowThisFunc(("\n"));
14332
14333 AutoCaller autoCaller(this);
14334 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14335
14336 ComPtr<IInternalSessionControl> directControl;
14337 {
14338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14339 if (mData->mSession.mLockType == LockType_VM)
14340 directControl = mData->mSession.mDirectControl;
14341 }
14342
14343 /* ignore notifications sent after #OnSessionEnd() is called */
14344 if (!directControl)
14345 return S_OK;
14346
14347 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14348}
14349
14350/**
14351 * @note Locks this object for reading.
14352 */
14353HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14354{
14355 LogFlowThisFunc(("\n"));
14356
14357 AutoCaller autoCaller(this);
14358 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14359
14360 ComPtr<IInternalSessionControl> directControl;
14361 {
14362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14363 if (mData->mSession.mLockType == LockType_VM)
14364 directControl = mData->mSession.mDirectControl;
14365 }
14366
14367 /* ignore notifications sent after #OnSessionEnd() is called */
14368 if (!directControl)
14369 return S_OK;
14370
14371 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14372}
14373
14374/**
14375 * Returns @c true if this machine's USB controller reports it has a matching
14376 * filter for the given USB device and @c false otherwise.
14377 *
14378 * @note locks this object for reading.
14379 */
14380bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14381{
14382 AutoCaller autoCaller(this);
14383 /* silently return if not ready -- this method may be called after the
14384 * direct machine session has been called */
14385 if (!autoCaller.isOk())
14386 return false;
14387
14388#ifdef VBOX_WITH_USB
14389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14390
14391 switch (mData->mMachineState)
14392 {
14393 case MachineState_Starting:
14394 case MachineState_Restoring:
14395 case MachineState_TeleportingIn:
14396 case MachineState_Paused:
14397 case MachineState_Running:
14398 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14399 * elsewhere... */
14400 alock.release();
14401 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14402 default: break;
14403 }
14404#else
14405 NOREF(aDevice);
14406 NOREF(aMaskedIfs);
14407#endif
14408 return false;
14409}
14410
14411/**
14412 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14413 */
14414HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14415 IVirtualBoxErrorInfo *aError,
14416 ULONG aMaskedIfs,
14417 const com::Utf8Str &aCaptureFilename)
14418{
14419 LogFlowThisFunc(("\n"));
14420
14421 AutoCaller autoCaller(this);
14422
14423 /* This notification may happen after the machine object has been
14424 * uninitialized (the session was closed), so don't assert. */
14425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14426
14427 ComPtr<IInternalSessionControl> directControl;
14428 {
14429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14430 if (mData->mSession.mLockType == LockType_VM)
14431 directControl = mData->mSession.mDirectControl;
14432 }
14433
14434 /* fail on notifications sent after #OnSessionEnd() is called, it is
14435 * expected by the caller */
14436 if (!directControl)
14437 return E_FAIL;
14438
14439 /* No locks should be held at this point. */
14440 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14441 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14442
14443 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14444}
14445
14446/**
14447 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14448 */
14449HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14450 IVirtualBoxErrorInfo *aError)
14451{
14452 LogFlowThisFunc(("\n"));
14453
14454 AutoCaller autoCaller(this);
14455
14456 /* This notification may happen after the machine object has been
14457 * uninitialized (the session was closed), so don't assert. */
14458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14459
14460 ComPtr<IInternalSessionControl> directControl;
14461 {
14462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14463 if (mData->mSession.mLockType == LockType_VM)
14464 directControl = mData->mSession.mDirectControl;
14465 }
14466
14467 /* fail on notifications sent after #OnSessionEnd() is called, it is
14468 * expected by the caller */
14469 if (!directControl)
14470 return E_FAIL;
14471
14472 /* No locks should be held at this point. */
14473 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14474 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14475
14476 return directControl->OnUSBDeviceDetach(aId, aError);
14477}
14478
14479// protected methods
14480/////////////////////////////////////////////////////////////////////////////
14481
14482/**
14483 * Deletes the given file if it is no longer in use by either the current machine state
14484 * (if the machine is "saved") or any of the machine's snapshots.
14485 *
14486 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14487 * but is different for each SnapshotMachine. When calling this, the order of calling this
14488 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14489 * is therefore critical. I know, it's all rather messy.
14490 *
14491 * @param strStateFile
14492 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14493 * the test for whether the saved state file is in use.
14494 */
14495void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14496 Snapshot *pSnapshotToIgnore)
14497{
14498 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14499 if ( (strStateFile.isNotEmpty())
14500 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14501 )
14502 // ... and it must also not be shared with other snapshots
14503 if ( !mData->mFirstSnapshot
14504 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14505 // this checks the SnapshotMachine's state file paths
14506 )
14507 RTFileDelete(strStateFile.c_str());
14508}
14509
14510/**
14511 * Locks the attached media.
14512 *
14513 * All attached hard disks are locked for writing and DVD/floppy are locked for
14514 * reading. Parents of attached hard disks (if any) are locked for reading.
14515 *
14516 * This method also performs accessibility check of all media it locks: if some
14517 * media is inaccessible, the method will return a failure and a bunch of
14518 * extended error info objects per each inaccessible medium.
14519 *
14520 * Note that this method is atomic: if it returns a success, all media are
14521 * locked as described above; on failure no media is locked at all (all
14522 * succeeded individual locks will be undone).
14523 *
14524 * The caller is responsible for doing the necessary state sanity checks.
14525 *
14526 * The locks made by this method must be undone by calling #unlockMedia() when
14527 * no more needed.
14528 */
14529HRESULT SessionMachine::i_lockMedia()
14530{
14531 AutoCaller autoCaller(this);
14532 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14533
14534 AutoMultiWriteLock2 alock(this->lockHandle(),
14535 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14536
14537 /* bail out if trying to lock things with already set up locking */
14538 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14539
14540 MultiResult mrc(S_OK);
14541
14542 /* Collect locking information for all medium objects attached to the VM. */
14543 for (MediumAttachmentList::const_iterator
14544 it = mMediumAttachments->begin();
14545 it != mMediumAttachments->end();
14546 ++it)
14547 {
14548 MediumAttachment *pAtt = *it;
14549 DeviceType_T devType = pAtt->i_getType();
14550 Medium *pMedium = pAtt->i_getMedium();
14551
14552 MediumLockList *pMediumLockList(new MediumLockList());
14553 // There can be attachments without a medium (floppy/dvd), and thus
14554 // it's impossible to create a medium lock list. It still makes sense
14555 // to have the empty medium lock list in the map in case a medium is
14556 // attached later.
14557 if (pMedium != NULL)
14558 {
14559 MediumType_T mediumType = pMedium->i_getType();
14560 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14561 || mediumType == MediumType_Shareable;
14562 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14563
14564 alock.release();
14565 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14566 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14567 false /* fMediumLockWriteAll */,
14568 NULL,
14569 *pMediumLockList);
14570 alock.acquire();
14571 if (FAILED(mrc))
14572 {
14573 delete pMediumLockList;
14574 mData->mSession.mLockedMedia.Clear();
14575 break;
14576 }
14577 }
14578
14579 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14580 if (FAILED(rc))
14581 {
14582 mData->mSession.mLockedMedia.Clear();
14583 mrc = setError(rc,
14584 tr("Collecting locking information for all attached media failed"));
14585 break;
14586 }
14587 }
14588
14589 if (SUCCEEDED(mrc))
14590 {
14591 /* Now lock all media. If this fails, nothing is locked. */
14592 alock.release();
14593 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14594 alock.acquire();
14595 if (FAILED(rc))
14596 {
14597 mrc = setError(rc,
14598 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14599 }
14600 }
14601
14602 return mrc;
14603}
14604
14605/**
14606 * Undoes the locks made by by #lockMedia().
14607 */
14608HRESULT SessionMachine::i_unlockMedia()
14609{
14610 AutoCaller autoCaller(this);
14611 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14612
14613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14614
14615 /* we may be holding important error info on the current thread;
14616 * preserve it */
14617 ErrorInfoKeeper eik;
14618
14619 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14620 AssertComRC(rc);
14621 return rc;
14622}
14623
14624/**
14625 * Helper to change the machine state (reimplementation).
14626 *
14627 * @note Locks this object for writing.
14628 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14629 * it can cause crashes in random places due to unexpectedly committing
14630 * the current settings. The caller is responsible for that. The call
14631 * to saveStateSettings is fine, because this method does not commit.
14632 */
14633HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14634{
14635 LogFlowThisFuncEnter();
14636
14637 AutoCaller autoCaller(this);
14638 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14639
14640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14641
14642 MachineState_T oldMachineState = mData->mMachineState;
14643
14644 AssertMsgReturn(oldMachineState != aMachineState,
14645 ("oldMachineState=%s, aMachineState=%s\n",
14646 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14647 E_FAIL);
14648
14649 HRESULT rc = S_OK;
14650
14651 int stsFlags = 0;
14652 bool deleteSavedState = false;
14653
14654 /* detect some state transitions */
14655
14656 if ( ( ( oldMachineState == MachineState_Saved
14657 || oldMachineState == MachineState_AbortedSaved
14658 )
14659 && aMachineState == MachineState_Restoring
14660 )
14661 || ( ( oldMachineState == MachineState_PoweredOff
14662 || oldMachineState == MachineState_Teleported
14663 || oldMachineState == MachineState_Aborted
14664 )
14665 && ( aMachineState == MachineState_TeleportingIn
14666 || aMachineState == MachineState_Starting
14667 )
14668 )
14669 )
14670 {
14671 /* The EMT thread is about to start */
14672
14673 /* Nothing to do here for now... */
14674
14675 /// @todo NEWMEDIA don't let mDVDDrive and other children
14676 /// change anything when in the Starting/Restoring state
14677 }
14678 else if ( ( oldMachineState == MachineState_Running
14679 || oldMachineState == MachineState_Paused
14680 || oldMachineState == MachineState_Teleporting
14681 || oldMachineState == MachineState_OnlineSnapshotting
14682 || oldMachineState == MachineState_LiveSnapshotting
14683 || oldMachineState == MachineState_Stuck
14684 || oldMachineState == MachineState_Starting
14685 || oldMachineState == MachineState_Stopping
14686 || oldMachineState == MachineState_Saving
14687 || oldMachineState == MachineState_Restoring
14688 || oldMachineState == MachineState_TeleportingPausedVM
14689 || oldMachineState == MachineState_TeleportingIn
14690 )
14691 && ( aMachineState == MachineState_PoweredOff
14692 || aMachineState == MachineState_Saved
14693 || aMachineState == MachineState_Teleported
14694 || aMachineState == MachineState_Aborted
14695 || aMachineState == MachineState_AbortedSaved
14696 )
14697 )
14698 {
14699 /* The EMT thread has just stopped, unlock attached media. Note that as
14700 * opposed to locking that is done from Console, we do unlocking here
14701 * because the VM process may have aborted before having a chance to
14702 * properly unlock all media it locked. */
14703
14704 unlockMedia();
14705 }
14706
14707 if (oldMachineState == MachineState_Restoring)
14708 {
14709 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14710 {
14711 /*
14712 * delete the saved state file once the machine has finished
14713 * restoring from it (note that Console sets the state from
14714 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14715 * to give the user an ability to fix an error and retry --
14716 * we keep the saved state file in this case)
14717 */
14718 deleteSavedState = true;
14719 }
14720 }
14721 else if ( oldMachineState == MachineState_Saved
14722 && ( aMachineState == MachineState_PoweredOff
14723 || aMachineState == MachineState_Teleported
14724 )
14725 )
14726 {
14727 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14728 deleteSavedState = true;
14729 mData->mCurrentStateModified = TRUE;
14730 stsFlags |= SaveSTS_CurStateModified;
14731 }
14732 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14733 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14734
14735 if ( aMachineState == MachineState_Starting
14736 || aMachineState == MachineState_Restoring
14737 || aMachineState == MachineState_TeleportingIn
14738 )
14739 {
14740 /* set the current state modified flag to indicate that the current
14741 * state is no more identical to the state in the
14742 * current snapshot */
14743 if (!mData->mCurrentSnapshot.isNull())
14744 {
14745 mData->mCurrentStateModified = TRUE;
14746 stsFlags |= SaveSTS_CurStateModified;
14747 }
14748 }
14749
14750 if (deleteSavedState)
14751 {
14752 if (mRemoveSavedState)
14753 {
14754 Assert(!mSSData->strStateFilePath.isEmpty());
14755
14756 // it is safe to delete the saved state file if ...
14757 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14758 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14759 // ... none of the snapshots share the saved state file
14760 )
14761 RTFileDelete(mSSData->strStateFilePath.c_str());
14762 }
14763
14764 mSSData->strStateFilePath.setNull();
14765 stsFlags |= SaveSTS_StateFilePath;
14766 }
14767
14768 /* redirect to the underlying peer machine */
14769 mPeer->i_setMachineState(aMachineState);
14770
14771 if ( oldMachineState != MachineState_RestoringSnapshot
14772 && ( aMachineState == MachineState_PoweredOff
14773 || aMachineState == MachineState_Teleported
14774 || aMachineState == MachineState_Aborted
14775 || aMachineState == MachineState_AbortedSaved
14776 || aMachineState == MachineState_Saved))
14777 {
14778 /* the machine has stopped execution
14779 * (or the saved state file was adopted) */
14780 stsFlags |= SaveSTS_StateTimeStamp;
14781 }
14782
14783 if ( ( oldMachineState == MachineState_PoweredOff
14784 || oldMachineState == MachineState_Aborted
14785 || oldMachineState == MachineState_Teleported
14786 )
14787 && aMachineState == MachineState_Saved)
14788 {
14789 /* the saved state file was adopted */
14790 Assert(!mSSData->strStateFilePath.isEmpty());
14791 stsFlags |= SaveSTS_StateFilePath;
14792 }
14793
14794#ifdef VBOX_WITH_GUEST_PROPS
14795 if ( aMachineState == MachineState_PoweredOff
14796 || aMachineState == MachineState_Aborted
14797 || aMachineState == MachineState_Teleported)
14798 {
14799 /* Make sure any transient guest properties get removed from the
14800 * property store on shutdown. */
14801 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14802
14803 /* remove it from the settings representation */
14804 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14805 for (settings::GuestPropertiesList::iterator
14806 it = llGuestProperties.begin();
14807 it != llGuestProperties.end();
14808 /*nothing*/)
14809 {
14810 const settings::GuestProperty &prop = *it;
14811 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14812 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14813 {
14814 it = llGuestProperties.erase(it);
14815 fNeedsSaving = true;
14816 }
14817 else
14818 {
14819 ++it;
14820 }
14821 }
14822
14823 /* Additionally remove it from the HWData representation. Required to
14824 * keep everything in sync, as this is what the API keeps using. */
14825 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14826 for (HWData::GuestPropertyMap::iterator
14827 it = llHWGuestProperties.begin();
14828 it != llHWGuestProperties.end();
14829 /*nothing*/)
14830 {
14831 uint32_t fFlags = it->second.mFlags;
14832 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14833 {
14834 /* iterator where we need to continue after the erase call
14835 * (C++03 is a fact still, and it doesn't return the iterator
14836 * which would allow continuing) */
14837 HWData::GuestPropertyMap::iterator it2 = it;
14838 ++it2;
14839 llHWGuestProperties.erase(it);
14840 it = it2;
14841 fNeedsSaving = true;
14842 }
14843 else
14844 {
14845 ++it;
14846 }
14847 }
14848
14849 if (fNeedsSaving)
14850 {
14851 mData->mCurrentStateModified = TRUE;
14852 stsFlags |= SaveSTS_CurStateModified;
14853 }
14854 }
14855#endif /* VBOX_WITH_GUEST_PROPS */
14856
14857 rc = i_saveStateSettings(stsFlags);
14858
14859 if ( ( oldMachineState != MachineState_PoweredOff
14860 && oldMachineState != MachineState_Aborted
14861 && oldMachineState != MachineState_Teleported
14862 )
14863 && ( aMachineState == MachineState_PoweredOff
14864 || aMachineState == MachineState_Aborted
14865 || aMachineState == MachineState_Teleported
14866 )
14867 )
14868 {
14869 /* we've been shut down for any reason */
14870 /* no special action so far */
14871 }
14872
14873 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14874 LogFlowThisFuncLeave();
14875 return rc;
14876}
14877
14878/**
14879 * Sends the current machine state value to the VM process.
14880 *
14881 * @note Locks this object for reading, then calls a client process.
14882 */
14883HRESULT SessionMachine::i_updateMachineStateOnClient()
14884{
14885 AutoCaller autoCaller(this);
14886 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14887
14888 ComPtr<IInternalSessionControl> directControl;
14889 {
14890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14891 AssertReturn(!!mData, E_FAIL);
14892 if (mData->mSession.mLockType == LockType_VM)
14893 directControl = mData->mSession.mDirectControl;
14894
14895 /* directControl may be already set to NULL here in #OnSessionEnd()
14896 * called too early by the direct session process while there is still
14897 * some operation (like deleting the snapshot) in progress. The client
14898 * process in this case is waiting inside Session::close() for the
14899 * "end session" process object to complete, while #uninit() called by
14900 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14901 * operation to complete. For now, we accept this inconsistent behavior
14902 * and simply do nothing here. */
14903
14904 if (mData->mSession.mState == SessionState_Unlocking)
14905 return S_OK;
14906 }
14907
14908 /* ignore notifications sent after #OnSessionEnd() is called */
14909 if (!directControl)
14910 return S_OK;
14911
14912 return directControl->UpdateMachineState(mData->mMachineState);
14913}
14914
14915
14916/*static*/
14917HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14918{
14919 va_list args;
14920 va_start(args, pcszMsg);
14921 HRESULT rc = setErrorInternalV(aResultCode,
14922 getStaticClassIID(),
14923 getStaticComponentName(),
14924 pcszMsg, args,
14925 false /* aWarning */,
14926 true /* aLogIt */);
14927 va_end(args);
14928 return rc;
14929}
14930
14931
14932HRESULT Machine::updateState(MachineState_T aState)
14933{
14934 NOREF(aState);
14935 ReturnComNotImplemented();
14936}
14937
14938HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14939{
14940 NOREF(aProgress);
14941 ReturnComNotImplemented();
14942}
14943
14944HRESULT Machine::endPowerUp(LONG aResult)
14945{
14946 NOREF(aResult);
14947 ReturnComNotImplemented();
14948}
14949
14950HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14951{
14952 NOREF(aProgress);
14953 ReturnComNotImplemented();
14954}
14955
14956HRESULT Machine::endPoweringDown(LONG aResult,
14957 const com::Utf8Str &aErrMsg)
14958{
14959 NOREF(aResult);
14960 NOREF(aErrMsg);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14965 BOOL *aMatched,
14966 ULONG *aMaskedInterfaces)
14967{
14968 NOREF(aDevice);
14969 NOREF(aMatched);
14970 NOREF(aMaskedInterfaces);
14971 ReturnComNotImplemented();
14972
14973}
14974
14975HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14976{
14977 NOREF(aId); NOREF(aCaptureFilename);
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14982 BOOL aDone)
14983{
14984 NOREF(aId);
14985 NOREF(aDone);
14986 ReturnComNotImplemented();
14987}
14988
14989HRESULT Machine::autoCaptureUSBDevices()
14990{
14991 ReturnComNotImplemented();
14992}
14993
14994HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14995{
14996 NOREF(aDone);
14997 ReturnComNotImplemented();
14998}
14999
15000HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15001 ComPtr<IProgress> &aProgress)
15002{
15003 NOREF(aSession);
15004 NOREF(aProgress);
15005 ReturnComNotImplemented();
15006}
15007
15008HRESULT Machine::finishOnlineMergeMedium()
15009{
15010 ReturnComNotImplemented();
15011}
15012
15013HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15014 std::vector<com::Utf8Str> &aValues,
15015 std::vector<LONG64> &aTimestamps,
15016 std::vector<com::Utf8Str> &aFlags)
15017{
15018 NOREF(aNames);
15019 NOREF(aValues);
15020 NOREF(aTimestamps);
15021 NOREF(aFlags);
15022 ReturnComNotImplemented();
15023}
15024
15025HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15026 const com::Utf8Str &aValue,
15027 LONG64 aTimestamp,
15028 const com::Utf8Str &aFlags)
15029{
15030 NOREF(aName);
15031 NOREF(aValue);
15032 NOREF(aTimestamp);
15033 NOREF(aFlags);
15034 ReturnComNotImplemented();
15035}
15036
15037HRESULT Machine::lockMedia()
15038{
15039 ReturnComNotImplemented();
15040}
15041
15042HRESULT Machine::unlockMedia()
15043{
15044 ReturnComNotImplemented();
15045}
15046
15047HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15048 ComPtr<IMediumAttachment> &aNewAttachment)
15049{
15050 NOREF(aAttachment);
15051 NOREF(aNewAttachment);
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15056 ULONG aCpuUser,
15057 ULONG aCpuKernel,
15058 ULONG aCpuIdle,
15059 ULONG aMemTotal,
15060 ULONG aMemFree,
15061 ULONG aMemBalloon,
15062 ULONG aMemShared,
15063 ULONG aMemCache,
15064 ULONG aPagedTotal,
15065 ULONG aMemAllocTotal,
15066 ULONG aMemFreeTotal,
15067 ULONG aMemBalloonTotal,
15068 ULONG aMemSharedTotal,
15069 ULONG aVmNetRx,
15070 ULONG aVmNetTx)
15071{
15072 NOREF(aValidStats);
15073 NOREF(aCpuUser);
15074 NOREF(aCpuKernel);
15075 NOREF(aCpuIdle);
15076 NOREF(aMemTotal);
15077 NOREF(aMemFree);
15078 NOREF(aMemBalloon);
15079 NOREF(aMemShared);
15080 NOREF(aMemCache);
15081 NOREF(aPagedTotal);
15082 NOREF(aMemAllocTotal);
15083 NOREF(aMemFreeTotal);
15084 NOREF(aMemBalloonTotal);
15085 NOREF(aMemSharedTotal);
15086 NOREF(aVmNetRx);
15087 NOREF(aVmNetTx);
15088 ReturnComNotImplemented();
15089}
15090
15091HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15092 com::Utf8Str &aResult)
15093{
15094 NOREF(aAuthParams);
15095 NOREF(aResult);
15096 ReturnComNotImplemented();
15097}
15098
15099com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15100{
15101 com::Utf8Str strControllerName = "Unknown";
15102 switch (aBusType)
15103 {
15104 case StorageBus_IDE:
15105 {
15106 strControllerName = "IDE";
15107 break;
15108 }
15109 case StorageBus_SATA:
15110 {
15111 strControllerName = "SATA";
15112 break;
15113 }
15114 case StorageBus_SCSI:
15115 {
15116 strControllerName = "SCSI";
15117 break;
15118 }
15119 case StorageBus_Floppy:
15120 {
15121 strControllerName = "Floppy";
15122 break;
15123 }
15124 case StorageBus_SAS:
15125 {
15126 strControllerName = "SAS";
15127 break;
15128 }
15129 case StorageBus_USB:
15130 {
15131 strControllerName = "USB";
15132 break;
15133 }
15134 default:
15135 break;
15136 }
15137 return strControllerName;
15138}
15139
15140HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15141{
15142 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15143
15144 AutoCaller autoCaller(this);
15145 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15146
15147 HRESULT rc = S_OK;
15148
15149 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15150 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15151 rc = getUSBDeviceFilters(usbDeviceFilters);
15152 if (FAILED(rc)) return rc;
15153
15154 NOREF(aFlags);
15155 com::Utf8Str osTypeId;
15156 ComObjPtr<GuestOSType> osType = NULL;
15157
15158 /* Get the guest os type as a string from the VB. */
15159 rc = getOSTypeId(osTypeId);
15160 if (FAILED(rc)) return rc;
15161
15162 /* Get the os type obj that coresponds, can be used to get
15163 * the defaults for this guest OS. */
15164 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15165 if (FAILED(rc)) return rc;
15166
15167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15168
15169 /* Let the OS type select 64-bit ness. */
15170 mHWData->mLongMode = osType->i_is64Bit()
15171 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15172
15173 /* Let the OS type enable the X2APIC */
15174 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15175
15176 /* This one covers IOAPICEnabled. */
15177 mBIOSSettings->i_applyDefaults(osType);
15178
15179 /* Initialize default record settings. */
15180 mRecordingSettings->i_applyDefaults();
15181
15182 /* Initialize default BIOS settings here */
15183 /* Hardware virtualization must be ON by default */
15184 mHWData->mAPIC = true;
15185 mHWData->mHWVirtExEnabled = true;
15186
15187 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15188 if (FAILED(rc)) return rc;
15189
15190 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15191 if (FAILED(rc)) return rc;
15192
15193 /* Graphics stuff. */
15194 GraphicsControllerType_T graphicsController;
15195 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15196 if (FAILED(rc)) return rc;
15197
15198 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15199 if (FAILED(rc)) return rc;
15200
15201 ULONG vramSize;
15202 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15203 if (FAILED(rc)) return rc;
15204
15205 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15206 if (FAILED(rc)) return rc;
15207
15208 BOOL fAccelerate2DVideoEnabled;
15209 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15210 if (FAILED(rc)) return rc;
15211
15212 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15213 if (FAILED(rc)) return rc;
15214
15215 BOOL fAccelerate3DEnabled;
15216 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15217 if (FAILED(rc)) return rc;
15218
15219 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15220 if (FAILED(rc)) return rc;
15221
15222 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15223 if (FAILED(rc)) return rc;
15224
15225 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15226 if (FAILED(rc)) return rc;
15227
15228 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15229 if (FAILED(rc)) return rc;
15230
15231 BOOL mRTCUseUTC;
15232 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15233 if (FAILED(rc)) return rc;
15234
15235 setRTCUseUTC(mRTCUseUTC);
15236 if (FAILED(rc)) return rc;
15237
15238 /* the setter does more than just the assignment, so use it */
15239 ChipsetType_T enmChipsetType;
15240 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15241 if (FAILED(rc)) return rc;
15242
15243 rc = COMSETTER(ChipsetType)(enmChipsetType);
15244 if (FAILED(rc)) return rc;
15245
15246 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15247 if (FAILED(rc)) return rc;
15248
15249 /* Apply IOMMU defaults. */
15250 IommuType_T enmIommuType;
15251 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15252 if (FAILED(rc)) return rc;
15253
15254 rc = COMSETTER(IommuType)(enmIommuType);
15255 if (FAILED(rc)) return rc;
15256
15257 /* Apply network adapters defaults */
15258 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15259 mNetworkAdapters[slot]->i_applyDefaults(osType);
15260
15261 /* Apply serial port defaults */
15262 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15263 mSerialPorts[slot]->i_applyDefaults(osType);
15264
15265 /* Apply parallel port defaults - not OS dependent*/
15266 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15267 mParallelPorts[slot]->i_applyDefaults();
15268
15269 /* Audio stuff. */
15270 AudioControllerType_T audioController;
15271 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15272 if (FAILED(rc)) return rc;
15273
15274 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15275 if (FAILED(rc)) return rc;
15276
15277 AudioCodecType_T audioCodec;
15278 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15279 if (FAILED(rc)) return rc;
15280
15281 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15282 if (FAILED(rc)) return rc;
15283
15284 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15285 if (FAILED(rc)) return rc;
15286
15287 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15288 if (FAILED(rc)) return rc;
15289
15290 /* Storage Controllers */
15291 StorageControllerType_T hdStorageControllerType;
15292 StorageBus_T hdStorageBusType;
15293 StorageControllerType_T dvdStorageControllerType;
15294 StorageBus_T dvdStorageBusType;
15295 BOOL recommendedFloppy;
15296 ComPtr<IStorageController> floppyController;
15297 ComPtr<IStorageController> hdController;
15298 ComPtr<IStorageController> dvdController;
15299 Utf8Str strFloppyName, strDVDName, strHDName;
15300
15301 /* GUI auto generates controller names using bus type. Do the same*/
15302 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15303
15304 /* Floppy recommended? add one. */
15305 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15306 if (FAILED(rc)) return rc;
15307 if (recommendedFloppy)
15308 {
15309 rc = addStorageController(strFloppyName,
15310 StorageBus_Floppy,
15311 floppyController);
15312 if (FAILED(rc)) return rc;
15313 }
15314
15315 /* Setup one DVD storage controller. */
15316 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15317 if (FAILED(rc)) return rc;
15318
15319 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15320 if (FAILED(rc)) return rc;
15321
15322 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15323
15324 rc = addStorageController(strDVDName,
15325 dvdStorageBusType,
15326 dvdController);
15327 if (FAILED(rc)) return rc;
15328
15329 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15330 if (FAILED(rc)) return rc;
15331
15332 /* Setup one HDD storage controller. */
15333 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15334 if (FAILED(rc)) return rc;
15335
15336 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15337 if (FAILED(rc)) return rc;
15338
15339 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15340
15341 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15342 {
15343 rc = addStorageController(strHDName,
15344 hdStorageBusType,
15345 hdController);
15346 if (FAILED(rc)) return rc;
15347
15348 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15349 if (FAILED(rc)) return rc;
15350 }
15351 else
15352 {
15353 /* The HD controller is the same as DVD: */
15354 hdController = dvdController;
15355 }
15356
15357 /* Limit the AHCI port count if it's used because windows has trouble with
15358 * too many ports and other guest (OS X in particular) may take extra long
15359 * boot: */
15360
15361 // pParent = static_cast<Medium*>(aP)
15362 IStorageController *temp = hdController;
15363 ComObjPtr<StorageController> storageController;
15364 storageController = static_cast<StorageController *>(temp);
15365
15366 // tempHDController = aHDController;
15367 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15368 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15369 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15370 storageController->COMSETTER(PortCount)(1);
15371
15372 /* USB stuff */
15373
15374 bool ohciEnabled = false;
15375
15376 ComPtr<IUSBController> usbController;
15377 BOOL recommendedUSB3;
15378 BOOL recommendedUSB;
15379 BOOL usbProxyAvailable;
15380
15381 getUSBProxyAvailable(&usbProxyAvailable);
15382 if (FAILED(rc)) return rc;
15383
15384 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15385 if (FAILED(rc)) return rc;
15386 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15387 if (FAILED(rc)) return rc;
15388
15389 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15390 {
15391#ifdef VBOX_WITH_EXTPACK
15392 /* USB 3.0 is only available if the proper ExtPack is installed. */
15393 ExtPackManager *aManager = mParent->i_getExtPackManager();
15394 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15395 {
15396 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15397 if (FAILED(rc)) return rc;
15398
15399 /* xHci includes OHCI */
15400 ohciEnabled = true;
15401 }
15402#endif
15403 }
15404 if ( !ohciEnabled
15405 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15406 {
15407 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15408 if (FAILED(rc)) return rc;
15409 ohciEnabled = true;
15410
15411#ifdef VBOX_WITH_EXTPACK
15412 /* USB 2.0 is only available if the proper ExtPack is installed.
15413 * Note. Configuring EHCI here and providing messages about
15414 * the missing extpack isn't exactly clean, but it is a
15415 * necessary evil to patch over legacy compatability issues
15416 * introduced by the new distribution model. */
15417 ExtPackManager *manager = mParent->i_getExtPackManager();
15418 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15419 {
15420 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15421 if (FAILED(rc)) return rc;
15422 }
15423#endif
15424 }
15425
15426 /* Set recommended human interface device types: */
15427 BOOL recommendedUSBHID;
15428 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15429 if (FAILED(rc)) return rc;
15430
15431 if (recommendedUSBHID)
15432 {
15433 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15434 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15435 if (!ohciEnabled && !usbDeviceFilters.isNull())
15436 {
15437 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15438 if (FAILED(rc)) return rc;
15439 }
15440 }
15441
15442 BOOL recommendedUSBTablet;
15443 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15444 if (FAILED(rc)) return rc;
15445
15446 if (recommendedUSBTablet)
15447 {
15448 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15449 if (!ohciEnabled && !usbDeviceFilters.isNull())
15450 {
15451 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15452 if (FAILED(rc)) return rc;
15453 }
15454 }
15455 return S_OK;
15456}
15457
15458/* This isn't handled entirely by the wrapper generator yet. */
15459#ifdef VBOX_WITH_XPCOM
15460NS_DECL_CLASSINFO(SessionMachine)
15461NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15462
15463NS_DECL_CLASSINFO(SnapshotMachine)
15464NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15465#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