VirtualBox

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

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

Main/MachineImpl.cpp: Use printf and appendPrintf rather than Utf8StrFmt assignements. Also, don't try initialize a Utf8Str with a Utf8StrFmt, just make the type the latter and save the extra copying. Some signed/unsigned conversion issues. bugref:9790

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 536.7 KB
Line 
1/* $Id: MachineImpl.cpp 85256 2020-07-11 23:41:06Z 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#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mL1DFlushOnSched = true;
199 mL1DFlushOnVMEntry = false;
200 mMDSClearOnSched = true;
201 mMDSClearOnVMEntry = false;
202 mNestedHWVirt = false;
203 mHPETEnabled = false;
204 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
205 mCpuIdPortabilityLevel = 0;
206 mCpuProfile = "host";
207
208 /* default boot order: floppy - DVD - HDD */
209 mBootOrder[0] = DeviceType_Floppy;
210 mBootOrder[1] = DeviceType_DVD;
211 mBootOrder[2] = DeviceType_HardDisk;
212 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
213 mBootOrder[i] = DeviceType_Null;
214
215 mClipboardMode = ClipboardMode_Disabled;
216 mClipboardFileTransfersEnabled = FALSE;
217
218 mDnDMode = DnDMode_Disabled;
219
220 mFirmwareType = FirmwareType_BIOS;
221 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
222 mPointingHIDType = PointingHIDType_PS2Mouse;
223 mChipsetType = ChipsetType_PIIX3;
224 mParavirtProvider = ParavirtProvider_Default;
225 mEmulatedUSBCardReaderEnabled = FALSE;
226
227 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
228 mCPUAttached[i] = false;
229
230 mIOCacheEnabled = true;
231 mIOCacheSize = 5; /* 5MB */
232}
233
234Machine::HWData::~HWData()
235{
236}
237
238/////////////////////////////////////////////////////////////////////////////
239// Machine class
240/////////////////////////////////////////////////////////////////////////////
241
242// constructor / destructor
243/////////////////////////////////////////////////////////////////////////////
244
245Machine::Machine() :
246#ifdef VBOX_WITH_RESOURCE_USAGE_API
247 mCollectorGuest(NULL),
248#endif
249 mPeer(NULL),
250 mParent(NULL),
251 mSerialPorts(),
252 mParallelPorts(),
253 uRegistryNeedsSaving(0)
254{}
255
256Machine::~Machine()
257{}
258
259HRESULT Machine::FinalConstruct()
260{
261 LogFlowThisFunc(("\n"));
262 return BaseFinalConstruct();
263}
264
265void Machine::FinalRelease()
266{
267 LogFlowThisFunc(("\n"));
268 uninit();
269 BaseFinalRelease();
270}
271
272/**
273 * Initializes a new machine instance; this init() variant creates a new, empty machine.
274 * This gets called from VirtualBox::CreateMachine().
275 *
276 * @param aParent Associated parent object
277 * @param strConfigFile Local file system path to the VM settings file (can
278 * be relative to the VirtualBox config directory).
279 * @param strName name for the machine
280 * @param llGroups list of groups for the machine
281 * @param strOsType OS Type string (stored as is if aOsType is NULL).
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
286 * scheme (includes the UUID).
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 const Utf8Str &strOsType,
295 GuestOSType *aOsType,
296 const Guid &aId,
297 bool fForceOverwrite,
298 bool fDirectoryIncludesUUID)
299{
300 LogFlowThisFuncEnter();
301 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
302
303 /* Enclose the state transition NotReady->InInit->Ready */
304 AutoInitSpan autoInitSpan(this);
305 AssertReturn(autoInitSpan.isOk(), E_FAIL);
306
307 HRESULT rc = initImpl(aParent, strConfigFile);
308 if (FAILED(rc)) return rc;
309
310 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
311 if (FAILED(rc)) return rc;
312
313 if (SUCCEEDED(rc))
314 {
315 // create an empty machine config
316 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
317
318 rc = initDataAndChildObjects();
319 }
320
321 if (SUCCEEDED(rc))
322 {
323 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
324 mData->mAccessible = TRUE;
325
326 unconst(mData->mUuid) = aId;
327
328 mUserData->s.strName = strName;
329
330 if (llGroups.size())
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Let the OS type select 64-bit ness. */
349 mHWData->mLongMode = aOsType->i_is64Bit()
350 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
351
352 /* Let the OS type enable the X2APIC */
353 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
354 }
355 else if (!strOsType.isEmpty())
356 {
357 /* Store OS type */
358 mUserData->s.strOsType = strOsType;
359
360 /* No guest OS type object. Pick some plausible defaults which the
361 * host can handle. There's no way to know or validate anything. */
362 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363 mHWData->mX2APIC = false;
364 }
365
366 /* Apply BIOS defaults. */
367 mBIOSSettings->i_applyDefaults(aOsType);
368
369 /* Apply record defaults. */
370 mRecordingSettings->i_applyDefaults();
371
372 /* Apply network adapters defaults */
373 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
374 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
375
376 /* Apply serial port defaults */
377 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
378 mSerialPorts[slot]->i_applyDefaults(aOsType);
379
380 /* Apply parallel port defaults */
381 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
382 mParallelPorts[slot]->i_applyDefaults();
383
384 /* At this point the changing of the current state modification
385 * flag is allowed. */
386 i_allowStateModification();
387
388 /* commit all changes made during the initialization */
389 i_commit();
390 }
391
392 /* Confirm a successful initialization when it's the case */
393 if (SUCCEEDED(rc))
394 {
395 if (mData->mAccessible)
396 autoInitSpan.setSucceeded();
397 else
398 autoInitSpan.setLimited();
399 }
400
401 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
402 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
403 mData->mRegistered,
404 mData->mAccessible,
405 rc));
406
407 LogFlowThisFuncLeave();
408
409 return rc;
410}
411
412/**
413 * Initializes a new instance with data from machine XML (formerly Init_Registered).
414 * Gets called in two modes:
415 *
416 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
417 * UUID is specified and we mark the machine as "registered";
418 *
419 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
420 * and the machine remains unregistered until RegisterMachine() is called.
421 *
422 * @param aParent Associated parent object
423 * @param strConfigFile Local file system path to the VM settings file (can
424 * be relative to the VirtualBox config directory).
425 * @param aId UUID of the machine or NULL (see above).
426 *
427 * @return Success indicator. if not S_OK, the machine object is invalid
428 */
429HRESULT Machine::initFromSettings(VirtualBox *aParent,
430 const Utf8Str &strConfigFile,
431 const Guid *aId)
432{
433 LogFlowThisFuncEnter();
434 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
435
436 /* Enclose the state transition NotReady->InInit->Ready */
437 AutoInitSpan autoInitSpan(this);
438 AssertReturn(autoInitSpan.isOk(), E_FAIL);
439
440 HRESULT rc = initImpl(aParent, strConfigFile);
441 if (FAILED(rc)) return rc;
442
443 if (aId)
444 {
445 // loading a registered VM:
446 unconst(mData->mUuid) = *aId;
447 mData->mRegistered = TRUE;
448 // now load the settings from XML:
449 rc = i_registeredInit();
450 // this calls initDataAndChildObjects() and loadSettings()
451 }
452 else
453 {
454 // opening an unregistered VM (VirtualBox::OpenMachine()):
455 rc = initDataAndChildObjects();
456
457 if (SUCCEEDED(rc))
458 {
459 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
460 mData->mAccessible = TRUE;
461
462 try
463 {
464 // load and parse machine XML; this will throw on XML or logic errors
465 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
466
467 // reject VM UUID duplicates, they can happen if someone
468 // tries to register an already known VM config again
469 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
470 true /* fPermitInaccessible */,
471 false /* aDoSetError */,
472 NULL) != VBOX_E_OBJECT_NOT_FOUND)
473 {
474 throw setError(E_FAIL,
475 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
476 mData->m_strConfigFile.c_str());
477 }
478
479 // use UUID from machine config
480 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
481
482 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
483 NULL /* puuidRegistry */);
484 if (FAILED(rc)) throw rc;
485
486 /* At this point the changing of the current state modification
487 * flag is allowed. */
488 i_allowStateModification();
489
490 i_commit();
491 }
492 catch (HRESULT err)
493 {
494 /* we assume that error info is set by the thrower */
495 rc = err;
496 }
497 catch (...)
498 {
499 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
500 }
501 }
502 }
503
504 /* Confirm a successful initialization when it's the case */
505 if (SUCCEEDED(rc))
506 {
507 if (mData->mAccessible)
508 autoInitSpan.setSucceeded();
509 else
510 {
511 autoInitSpan.setLimited();
512
513 // uninit media from this machine's media registry, or else
514 // reloading the settings will fail
515 mParent->i_unregisterMachineMedia(i_getId());
516 }
517 }
518
519 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
520 "rc=%08X\n",
521 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
522 mData->mRegistered, mData->mAccessible, rc));
523
524 LogFlowThisFuncLeave();
525
526 return rc;
527}
528
529/**
530 * Initializes a new instance from a machine config that is already in memory
531 * (import OVF case). Since we are importing, the UUID in the machine
532 * config is ignored and we always generate a fresh one.
533 *
534 * @param aParent Associated parent object.
535 * @param strName Name for the new machine; this overrides what is specified in config.
536 * @param strSettingsFilename File name of .vbox file.
537 * @param config Machine configuration loaded and parsed from XML.
538 *
539 * @return Success indicator. if not S_OK, the machine object is invalid
540 */
541HRESULT Machine::init(VirtualBox *aParent,
542 const Utf8Str &strName,
543 const Utf8Str &strSettingsFilename,
544 const settings::MachineConfigFile &config)
545{
546 LogFlowThisFuncEnter();
547
548 /* Enclose the state transition NotReady->InInit->Ready */
549 AutoInitSpan autoInitSpan(this);
550 AssertReturn(autoInitSpan.isOk(), E_FAIL);
551
552 HRESULT rc = initImpl(aParent, strSettingsFilename);
553 if (FAILED(rc)) return rc;
554
555 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
556 if (FAILED(rc)) return rc;
557
558 rc = initDataAndChildObjects();
559
560 if (SUCCEEDED(rc))
561 {
562 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
563 mData->mAccessible = TRUE;
564
565 // create empty machine config for instance data
566 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
567
568 // generate fresh UUID, ignore machine config
569 unconst(mData->mUuid).create();
570
571 rc = i_loadMachineDataFromSettings(config,
572 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
573
574 // override VM name as well, it may be different
575 mUserData->s.strName = strName;
576
577 if (SUCCEEDED(rc))
578 {
579 /* At this point the changing of the current state modification
580 * flag is allowed. */
581 i_allowStateModification();
582
583 /* commit all changes made during the initialization */
584 i_commit();
585 }
586 }
587
588 /* Confirm a successful initialization when it's the case */
589 if (SUCCEEDED(rc))
590 {
591 if (mData->mAccessible)
592 autoInitSpan.setSucceeded();
593 else
594 {
595 /* Ignore all errors from unregistering, they would destroy
596- * the more interesting error information we already have,
597- * pinpointing the issue with the VM config. */
598 ErrorInfoKeeper eik;
599
600 autoInitSpan.setLimited();
601
602 // uninit media from this machine's media registry, or else
603 // reloading the settings will fail
604 mParent->i_unregisterMachineMedia(i_getId());
605 }
606 }
607
608 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
609 "rc=%08X\n",
610 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
611 mData->mRegistered, mData->mAccessible, rc));
612
613 LogFlowThisFuncLeave();
614
615 return rc;
616}
617
618/**
619 * Shared code between the various init() implementations.
620 * @param aParent The VirtualBox object.
621 * @param strConfigFile Settings file.
622 * @return
623 */
624HRESULT Machine::initImpl(VirtualBox *aParent,
625 const Utf8Str &strConfigFile)
626{
627 LogFlowThisFuncEnter();
628
629 AssertReturn(aParent, E_INVALIDARG);
630 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
631
632 HRESULT rc = S_OK;
633
634 /* share the parent weakly */
635 unconst(mParent) = aParent;
636
637 /* allocate the essential machine data structure (the rest will be
638 * allocated later by initDataAndChildObjects() */
639 mData.allocate();
640
641 /* memorize the config file name (as provided) */
642 mData->m_strConfigFile = strConfigFile;
643
644 /* get the full file name */
645 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
646 if (RT_FAILURE(vrc1))
647 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
648 tr("Invalid machine settings file name '%s' (%Rrc)"),
649 strConfigFile.c_str(),
650 vrc1);
651
652 LogFlowThisFuncLeave();
653
654 return rc;
655}
656
657/**
658 * Tries to create a machine settings file in the path stored in the machine
659 * instance data. Used when a new machine is created to fail gracefully if
660 * the settings file could not be written (e.g. because machine dir is read-only).
661 * @return
662 */
663HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
664{
665 HRESULT rc = S_OK;
666
667 // when we create a new machine, we must be able to create the settings file
668 RTFILE f = NIL_RTFILE;
669 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
670 if ( RT_SUCCESS(vrc)
671 || vrc == VERR_SHARING_VIOLATION
672 )
673 {
674 if (RT_SUCCESS(vrc))
675 RTFileClose(f);
676 if (!fForceOverwrite)
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Machine settings file '%s' already exists"),
679 mData->m_strConfigFileFull.c_str());
680 else
681 {
682 /* try to delete the config file, as otherwise the creation
683 * of a new settings file will fail. */
684 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
685 if (RT_FAILURE(vrc2))
686 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
687 tr("Could not delete the existing settings file '%s' (%Rrc)"),
688 mData->m_strConfigFileFull.c_str(), vrc2);
689 }
690 }
691 else if ( vrc != VERR_FILE_NOT_FOUND
692 && vrc != VERR_PATH_NOT_FOUND
693 )
694 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
695 tr("Invalid machine settings file name '%s' (%Rrc)"),
696 mData->m_strConfigFileFull.c_str(),
697 vrc);
698 return rc;
699}
700
701/**
702 * Initializes the registered machine by loading the settings file.
703 * This method is separated from #init() in order to make it possible to
704 * retry the operation after VirtualBox startup instead of refusing to
705 * startup the whole VirtualBox server in case if the settings file of some
706 * registered VM is invalid or inaccessible.
707 *
708 * @note Must be always called from this object's write lock
709 * (unless called from #init() that doesn't need any locking).
710 * @note Locks the mUSBController method for writing.
711 * @note Subclasses must not call this method.
712 */
713HRESULT Machine::i_registeredInit()
714{
715 AssertReturn(!i_isSessionMachine(), E_FAIL);
716 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
717 AssertReturn(mData->mUuid.isValid(), E_FAIL);
718 AssertReturn(!mData->mAccessible, E_FAIL);
719
720 HRESULT rc = initDataAndChildObjects();
721
722 if (SUCCEEDED(rc))
723 {
724 /* Temporarily reset the registered flag in order to let setters
725 * potentially called from loadSettings() succeed (isMutable() used in
726 * all setters will return FALSE for a Machine instance if mRegistered
727 * is TRUE). */
728 mData->mRegistered = FALSE;
729
730 try
731 {
732 // load and parse machine XML; this will throw on XML or logic errors
733 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
734
735 if (mData->mUuid != mData->pMachineConfigFile->uuid)
736 throw setError(E_FAIL,
737 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
738 mData->pMachineConfigFile->uuid.raw(),
739 mData->m_strConfigFileFull.c_str(),
740 mData->mUuid.toString().c_str(),
741 mParent->i_settingsFilePath().c_str());
742
743 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
744 NULL /* const Guid *puuidRegistry */);
745 if (FAILED(rc)) throw rc;
746 }
747 catch (HRESULT err)
748 {
749 /* we assume that error info is set by the thrower */
750 rc = err;
751 }
752 catch (...)
753 {
754 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
755 }
756
757 /* Restore the registered flag (even on failure) */
758 mData->mRegistered = TRUE;
759 }
760
761 if (SUCCEEDED(rc))
762 {
763 /* Set mAccessible to TRUE only if we successfully locked and loaded
764 * the settings file */
765 mData->mAccessible = TRUE;
766
767 /* commit all changes made during loading the settings file */
768 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
769 /// @todo r=klaus for some reason the settings loading logic backs up
770 // the settings, and therefore a commit is needed. Should probably be changed.
771 }
772 else
773 {
774 /* If the machine is registered, then, instead of returning a
775 * failure, we mark it as inaccessible and set the result to
776 * success to give it a try later */
777
778 /* fetch the current error info */
779 mData->mAccessError = com::ErrorInfo();
780 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
781
782 /* rollback all changes */
783 i_rollback(false /* aNotify */);
784
785 // uninit media from this machine's media registry, or else
786 // reloading the settings will fail
787 mParent->i_unregisterMachineMedia(i_getId());
788
789 /* uninitialize the common part to make sure all data is reset to
790 * default (null) values */
791 uninitDataAndChildObjects();
792
793 rc = S_OK;
794 }
795
796 return rc;
797}
798
799/**
800 * Uninitializes the instance.
801 * Called either from FinalRelease() or by the parent when it gets destroyed.
802 *
803 * @note The caller of this method must make sure that this object
804 * a) doesn't have active callers on the current thread and b) is not locked
805 * by the current thread; otherwise uninit() will hang either a) due to
806 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
807 * a dead-lock caused by this thread waiting for all callers on the other
808 * threads are done but preventing them from doing so by holding a lock.
809 */
810void Machine::uninit()
811{
812 LogFlowThisFuncEnter();
813
814 Assert(!isWriteLockOnCurrentThread());
815
816 Assert(!uRegistryNeedsSaving);
817 if (uRegistryNeedsSaving)
818 {
819 AutoCaller autoCaller(this);
820 if (SUCCEEDED(autoCaller.rc()))
821 {
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823 i_saveSettings(NULL, Machine::SaveS_Force);
824 }
825 }
826
827 /* Enclose the state transition Ready->InUninit->NotReady */
828 AutoUninitSpan autoUninitSpan(this);
829 if (autoUninitSpan.uninitDone())
830 return;
831
832 Assert(!i_isSnapshotMachine());
833 Assert(!i_isSessionMachine());
834 Assert(!!mData);
835
836 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
837 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
838
839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
840
841 if (!mData->mSession.mMachine.isNull())
842 {
843 /* Theoretically, this can only happen if the VirtualBox server has been
844 * terminated while there were clients running that owned open direct
845 * sessions. Since in this case we are definitely called by
846 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
847 * won't happen on the client watcher thread (because it has a
848 * VirtualBox caller for the duration of the
849 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
850 * cannot happen until the VirtualBox caller is released). This is
851 * important, because SessionMachine::uninit() cannot correctly operate
852 * after we return from this method (it expects the Machine instance is
853 * still valid). We'll call it ourselves below.
854 */
855 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
856 (SessionMachine*)mData->mSession.mMachine));
857
858 if (Global::IsOnlineOrTransient(mData->mMachineState))
859 {
860 Log1WarningThisFunc(("Setting state to Aborted!\n"));
861 /* set machine state using SessionMachine reimplementation */
862 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
863 }
864
865 /*
866 * Uninitialize SessionMachine using public uninit() to indicate
867 * an unexpected uninitialization.
868 */
869 mData->mSession.mMachine->uninit();
870 /* SessionMachine::uninit() must set mSession.mMachine to null */
871 Assert(mData->mSession.mMachine.isNull());
872 }
873
874 // uninit media from this machine's media registry, if they're still there
875 Guid uuidMachine(i_getId());
876
877 /* the lock is no more necessary (SessionMachine is uninitialized) */
878 alock.release();
879
880 /* XXX This will fail with
881 * "cannot be closed because it is still attached to 1 virtual machines"
882 * because at this point we did not call uninitDataAndChildObjects() yet
883 * and therefore also removeBackReference() for all these mediums was not called! */
884
885 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
886 mParent->i_unregisterMachineMedia(uuidMachine);
887
888 // has machine been modified?
889 if (mData->flModifications)
890 {
891 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
892 i_rollback(false /* aNotify */);
893 }
894
895 if (mData->mAccessible)
896 uninitDataAndChildObjects();
897
898 /* free the essential data structure last */
899 mData.free();
900
901 LogFlowThisFuncLeave();
902}
903
904// Wrapped IMachine properties
905/////////////////////////////////////////////////////////////////////////////
906HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
907{
908 /* mParent is constant during life time, no need to lock */
909 ComObjPtr<VirtualBox> pVirtualBox(mParent);
910 aParent = pVirtualBox;
911
912 return S_OK;
913}
914
915
916HRESULT Machine::getAccessible(BOOL *aAccessible)
917{
918 /* In some cases (medium registry related), it is necessary to be able to
919 * go through the list of all machines. Happens when an inaccessible VM
920 * has a sensible medium registry. */
921 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
923
924 HRESULT rc = S_OK;
925
926 if (!mData->mAccessible)
927 {
928 /* try to initialize the VM once more if not accessible */
929
930 AutoReinitSpan autoReinitSpan(this);
931 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
932
933#ifdef DEBUG
934 LogFlowThisFunc(("Dumping media backreferences\n"));
935 mParent->i_dumpAllBackRefs();
936#endif
937
938 if (mData->pMachineConfigFile)
939 {
940 // reset the XML file to force loadSettings() (called from i_registeredInit())
941 // to parse it again; the file might have changed
942 delete mData->pMachineConfigFile;
943 mData->pMachineConfigFile = NULL;
944 }
945
946 rc = i_registeredInit();
947
948 if (SUCCEEDED(rc) && mData->mAccessible)
949 {
950 autoReinitSpan.setSucceeded();
951
952 /* make sure interesting parties will notice the accessibility
953 * state change */
954 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
955 mParent->i_onMachineDataChange(mData->mUuid);
956 }
957 }
958
959 if (SUCCEEDED(rc))
960 *aAccessible = mData->mAccessible;
961
962 LogFlowThisFuncLeave();
963
964 return rc;
965}
966
967HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
968{
969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
970
971 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
972 {
973 /* return shortly */
974 aAccessError = NULL;
975 return S_OK;
976 }
977
978 HRESULT rc = S_OK;
979
980 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
981 rc = errorInfo.createObject();
982 if (SUCCEEDED(rc))
983 {
984 errorInfo->init(mData->mAccessError.getResultCode(),
985 mData->mAccessError.getInterfaceID().ref(),
986 Utf8Str(mData->mAccessError.getComponent()).c_str(),
987 Utf8Str(mData->mAccessError.getText()));
988 aAccessError = errorInfo;
989 }
990
991 return rc;
992}
993
994HRESULT Machine::getName(com::Utf8Str &aName)
995{
996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
997
998 aName = mUserData->s.strName;
999
1000 return S_OK;
1001}
1002
1003HRESULT Machine::setName(const com::Utf8Str &aName)
1004{
1005 // prohibit setting a UUID only as the machine name, or else it can
1006 // never be found by findMachine()
1007 Guid test(aName);
1008
1009 if (test.isValid())
1010 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1011
1012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1015 if (FAILED(rc)) return rc;
1016
1017 i_setModified(IsModified_MachineData);
1018 mUserData.backup();
1019 mUserData->s.strName = aName;
1020
1021 return S_OK;
1022}
1023
1024HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1025{
1026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1027
1028 aDescription = mUserData->s.strDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1034{
1035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 // this can be done in principle in any state as it doesn't affect the VM
1038 // significantly, but play safe by not messing around while complex
1039 // activities are going on
1040 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1041 if (FAILED(rc)) return rc;
1042
1043 i_setModified(IsModified_MachineData);
1044 mUserData.backup();
1045 mUserData->s.strDescription = aDescription;
1046
1047 return S_OK;
1048}
1049
1050HRESULT Machine::getId(com::Guid &aId)
1051{
1052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1053
1054 aId = mData->mUuid;
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1060{
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062 aGroups.resize(mUserData->s.llGroups.size());
1063 size_t i = 0;
1064 for (StringsList::const_iterator
1065 it = mUserData->s.llGroups.begin();
1066 it != mUserData->s.llGroups.end();
1067 ++it, ++i)
1068 aGroups[i] = (*it);
1069
1070 return S_OK;
1071}
1072
1073HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1074{
1075 StringsList llGroups;
1076 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1077 if (FAILED(rc))
1078 return rc;
1079
1080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 rc = i_checkStateDependency(MutableOrSavedStateDep);
1083 if (FAILED(rc)) return rc;
1084
1085 i_setModified(IsModified_MachineData);
1086 mUserData.backup();
1087 mUserData->s.llGroups = llGroups;
1088
1089 return S_OK;
1090}
1091
1092HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1093{
1094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1095
1096 aOSTypeId = mUserData->s.strOsType;
1097
1098 return S_OK;
1099}
1100
1101HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1102{
1103 /* look up the object by Id to check it is valid */
1104 ComObjPtr<GuestOSType> pGuestOSType;
1105 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1106
1107 /* when setting, always use the "etalon" value for consistency -- lookup
1108 * by ID is case-insensitive and the input value may have different case */
1109 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1110
1111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 HRESULT rc = i_checkStateDependency(MutableStateDep);
1114 if (FAILED(rc)) return rc;
1115
1116 i_setModified(IsModified_MachineData);
1117 mUserData.backup();
1118 mUserData->s.strOsType = osTypeId;
1119
1120 return S_OK;
1121}
1122
1123HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1124{
1125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1126
1127 *aFirmwareType = mHWData->mFirmwareType;
1128
1129 return S_OK;
1130}
1131
1132HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1133{
1134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 HRESULT rc = i_checkStateDependency(MutableStateDep);
1137 if (FAILED(rc)) return rc;
1138
1139 i_setModified(IsModified_MachineData);
1140 mHWData.backup();
1141 mHWData->mFirmwareType = aFirmwareType;
1142 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1143 alock.release();
1144
1145 mBIOSSettings->i_updateNonVolatileStorageFile(strNVRAM);
1146
1147 return S_OK;
1148}
1149
1150HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1151{
1152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1153
1154 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1155
1156 return S_OK;
1157}
1158
1159HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1160{
1161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1162
1163 HRESULT rc = i_checkStateDependency(MutableStateDep);
1164 if (FAILED(rc)) return rc;
1165
1166 i_setModified(IsModified_MachineData);
1167 mHWData.backup();
1168 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1169
1170 return S_OK;
1171}
1172
1173HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1174{
1175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1176
1177 *aPointingHIDType = mHWData->mPointingHIDType;
1178
1179 return S_OK;
1180}
1181
1182HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1183{
1184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1185
1186 HRESULT rc = i_checkStateDependency(MutableStateDep);
1187 if (FAILED(rc)) return rc;
1188
1189 i_setModified(IsModified_MachineData);
1190 mHWData.backup();
1191 mHWData->mPointingHIDType = aPointingHIDType;
1192
1193 return S_OK;
1194}
1195
1196HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1197{
1198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1199
1200 *aChipsetType = mHWData->mChipsetType;
1201
1202 return S_OK;
1203}
1204
1205HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1206{
1207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1208
1209 HRESULT rc = i_checkStateDependency(MutableStateDep);
1210 if (FAILED(rc)) return rc;
1211
1212 if (aChipsetType != mHWData->mChipsetType)
1213 {
1214 i_setModified(IsModified_MachineData);
1215 mHWData.backup();
1216 mHWData->mChipsetType = aChipsetType;
1217
1218 // Resize network adapter array, to be finalized on commit/rollback.
1219 // We must not throw away entries yet, otherwise settings are lost
1220 // without a way to roll back.
1221 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1222 size_t oldCount = mNetworkAdapters.size();
1223 if (newCount > oldCount)
1224 {
1225 mNetworkAdapters.resize(newCount);
1226 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1227 {
1228 unconst(mNetworkAdapters[slot]).createObject();
1229 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1230 }
1231 }
1232 }
1233
1234 return S_OK;
1235}
1236
1237HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1238{
1239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 aParavirtDebug = mHWData->mParavirtDebug;
1242 return S_OK;
1243}
1244
1245HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1246{
1247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 HRESULT rc = i_checkStateDependency(MutableStateDep);
1250 if (FAILED(rc)) return rc;
1251
1252 /** @todo Parse/validate options? */
1253 if (aParavirtDebug != mHWData->mParavirtDebug)
1254 {
1255 i_setModified(IsModified_MachineData);
1256 mHWData.backup();
1257 mHWData->mParavirtDebug = aParavirtDebug;
1258 }
1259
1260 return S_OK;
1261}
1262
1263HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1264{
1265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1266
1267 *aParavirtProvider = mHWData->mParavirtProvider;
1268
1269 return S_OK;
1270}
1271
1272HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1273{
1274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 HRESULT rc = i_checkStateDependency(MutableStateDep);
1277 if (FAILED(rc)) return rc;
1278
1279 if (aParavirtProvider != mHWData->mParavirtProvider)
1280 {
1281 i_setModified(IsModified_MachineData);
1282 mHWData.backup();
1283 mHWData->mParavirtProvider = aParavirtProvider;
1284 }
1285
1286 return S_OK;
1287}
1288
1289HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1290{
1291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1292
1293 *aParavirtProvider = mHWData->mParavirtProvider;
1294 switch (mHWData->mParavirtProvider)
1295 {
1296 case ParavirtProvider_None:
1297 case ParavirtProvider_HyperV:
1298 case ParavirtProvider_KVM:
1299 case ParavirtProvider_Minimal:
1300 break;
1301
1302 /* Resolve dynamic provider types to the effective types. */
1303 default:
1304 {
1305 ComObjPtr<GuestOSType> pGuestOSType;
1306 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1307 pGuestOSType);
1308 if (FAILED(hrc2) || pGuestOSType.isNull())
1309 {
1310 *aParavirtProvider = ParavirtProvider_None;
1311 break;
1312 }
1313
1314 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1315 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1316
1317 switch (mHWData->mParavirtProvider)
1318 {
1319 case ParavirtProvider_Legacy:
1320 {
1321 if (fOsXGuest)
1322 *aParavirtProvider = ParavirtProvider_Minimal;
1323 else
1324 *aParavirtProvider = ParavirtProvider_None;
1325 break;
1326 }
1327
1328 case ParavirtProvider_Default:
1329 {
1330 if (fOsXGuest)
1331 *aParavirtProvider = ParavirtProvider_Minimal;
1332 else if ( mUserData->s.strOsType == "Windows10"
1333 || mUserData->s.strOsType == "Windows10_64"
1334 || mUserData->s.strOsType == "Windows81"
1335 || mUserData->s.strOsType == "Windows81_64"
1336 || mUserData->s.strOsType == "Windows8"
1337 || mUserData->s.strOsType == "Windows8_64"
1338 || mUserData->s.strOsType == "Windows7"
1339 || mUserData->s.strOsType == "Windows7_64"
1340 || mUserData->s.strOsType == "WindowsVista"
1341 || mUserData->s.strOsType == "WindowsVista_64"
1342 || mUserData->s.strOsType == "Windows2012"
1343 || mUserData->s.strOsType == "Windows2012_64"
1344 || mUserData->s.strOsType == "Windows2008"
1345 || mUserData->s.strOsType == "Windows2008_64")
1346 {
1347 *aParavirtProvider = ParavirtProvider_HyperV;
1348 }
1349 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1350 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1351 || mUserData->s.strOsType == "Linux"
1352 || mUserData->s.strOsType == "Linux_64"
1353 || mUserData->s.strOsType == "ArchLinux"
1354 || mUserData->s.strOsType == "ArchLinux_64"
1355 || mUserData->s.strOsType == "Debian"
1356 || mUserData->s.strOsType == "Debian_64"
1357 || mUserData->s.strOsType == "Fedora"
1358 || mUserData->s.strOsType == "Fedora_64"
1359 || mUserData->s.strOsType == "Gentoo"
1360 || mUserData->s.strOsType == "Gentoo_64"
1361 || mUserData->s.strOsType == "Mandriva"
1362 || mUserData->s.strOsType == "Mandriva_64"
1363 || mUserData->s.strOsType == "OpenSUSE"
1364 || mUserData->s.strOsType == "OpenSUSE_64"
1365 || mUserData->s.strOsType == "Oracle"
1366 || mUserData->s.strOsType == "Oracle_64"
1367 || mUserData->s.strOsType == "RedHat"
1368 || mUserData->s.strOsType == "RedHat_64"
1369 || mUserData->s.strOsType == "Turbolinux"
1370 || mUserData->s.strOsType == "Turbolinux_64"
1371 || mUserData->s.strOsType == "Ubuntu"
1372 || mUserData->s.strOsType == "Ubuntu_64"
1373 || mUserData->s.strOsType == "Xandros"
1374 || mUserData->s.strOsType == "Xandros_64")
1375 {
1376 *aParavirtProvider = ParavirtProvider_KVM;
1377 }
1378 else
1379 *aParavirtProvider = ParavirtProvider_None;
1380 break;
1381 }
1382
1383 default: AssertFailedBreak(); /* Shut up MSC. */
1384 }
1385 break;
1386 }
1387 }
1388
1389 Assert( *aParavirtProvider == ParavirtProvider_None
1390 || *aParavirtProvider == ParavirtProvider_Minimal
1391 || *aParavirtProvider == ParavirtProvider_HyperV
1392 || *aParavirtProvider == ParavirtProvider_KVM);
1393 return S_OK;
1394}
1395
1396HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1397{
1398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1399
1400 aHardwareVersion = mHWData->mHWVersion;
1401
1402 return S_OK;
1403}
1404
1405HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1406{
1407 /* check known version */
1408 Utf8Str hwVersion = aHardwareVersion;
1409 if ( hwVersion.compare("1") != 0
1410 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1411 return setError(E_INVALIDARG,
1412 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1413
1414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 HRESULT rc = i_checkStateDependency(MutableStateDep);
1417 if (FAILED(rc)) return rc;
1418
1419 i_setModified(IsModified_MachineData);
1420 mHWData.backup();
1421 mHWData->mHWVersion = aHardwareVersion;
1422
1423 return S_OK;
1424}
1425
1426HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1427{
1428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 if (!mHWData->mHardwareUUID.isZero())
1431 aHardwareUUID = mHWData->mHardwareUUID;
1432 else
1433 aHardwareUUID = mData->mUuid;
1434
1435 return S_OK;
1436}
1437
1438HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1439{
1440 if (!aHardwareUUID.isValid())
1441 return E_INVALIDARG;
1442
1443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1444
1445 HRESULT rc = i_checkStateDependency(MutableStateDep);
1446 if (FAILED(rc)) return rc;
1447
1448 i_setModified(IsModified_MachineData);
1449 mHWData.backup();
1450 if (aHardwareUUID == mData->mUuid)
1451 mHWData->mHardwareUUID.clear();
1452 else
1453 mHWData->mHardwareUUID = aHardwareUUID;
1454
1455 return S_OK;
1456}
1457
1458HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1459{
1460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 *aMemorySize = mHWData->mMemorySize;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::setMemorySize(ULONG aMemorySize)
1468{
1469 /* check RAM limits */
1470 if ( aMemorySize < MM_RAM_MIN_IN_MB
1471 || aMemorySize > MM_RAM_MAX_IN_MB
1472 )
1473 return setError(E_INVALIDARG,
1474 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1475 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1476
1477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1478
1479 HRESULT rc = i_checkStateDependency(MutableStateDep);
1480 if (FAILED(rc)) return rc;
1481
1482 i_setModified(IsModified_MachineData);
1483 mHWData.backup();
1484 mHWData->mMemorySize = aMemorySize;
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1490{
1491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 *aCPUCount = mHWData->mCPUCount;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::setCPUCount(ULONG aCPUCount)
1499{
1500 /* check CPU limits */
1501 if ( aCPUCount < SchemaDefs::MinCPUCount
1502 || aCPUCount > SchemaDefs::MaxCPUCount
1503 )
1504 return setError(E_INVALIDARG,
1505 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1506 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1507
1508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1509
1510 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1511 if (mHWData->mCPUHotPlugEnabled)
1512 {
1513 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1514 {
1515 if (mHWData->mCPUAttached[idx])
1516 return setError(E_INVALIDARG,
1517 tr("There is still a CPU attached to socket %lu."
1518 "Detach the CPU before removing the socket"),
1519 aCPUCount, idx+1);
1520 }
1521 }
1522
1523 HRESULT rc = i_checkStateDependency(MutableStateDep);
1524 if (FAILED(rc)) return rc;
1525
1526 i_setModified(IsModified_MachineData);
1527 mHWData.backup();
1528 mHWData->mCPUCount = aCPUCount;
1529
1530 return S_OK;
1531}
1532
1533HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1534{
1535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1536
1537 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1538
1539 return S_OK;
1540}
1541
1542HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1543{
1544 HRESULT rc = S_OK;
1545
1546 /* check throttle limits */
1547 if ( aCPUExecutionCap < 1
1548 || aCPUExecutionCap > 100
1549 )
1550 return setError(E_INVALIDARG,
1551 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1552 aCPUExecutionCap, 1, 100);
1553
1554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 alock.release();
1557 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1558 alock.acquire();
1559 if (FAILED(rc)) return rc;
1560
1561 i_setModified(IsModified_MachineData);
1562 mHWData.backup();
1563 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1564
1565 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1566 if (Global::IsOnline(mData->mMachineState))
1567 i_saveSettings(NULL);
1568
1569 return S_OK;
1570}
1571
1572HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1573{
1574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1575
1576 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1577
1578 return S_OK;
1579}
1580
1581HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1582{
1583 HRESULT rc = S_OK;
1584
1585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1586
1587 rc = i_checkStateDependency(MutableStateDep);
1588 if (FAILED(rc)) return rc;
1589
1590 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1591 {
1592 if (aCPUHotPlugEnabled)
1593 {
1594 i_setModified(IsModified_MachineData);
1595 mHWData.backup();
1596
1597 /* Add the amount of CPUs currently attached */
1598 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1599 mHWData->mCPUAttached[i] = true;
1600 }
1601 else
1602 {
1603 /*
1604 * We can disable hotplug only if the amount of maximum CPUs is equal
1605 * to the amount of attached CPUs
1606 */
1607 unsigned cCpusAttached = 0;
1608 unsigned iHighestId = 0;
1609
1610 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1611 {
1612 if (mHWData->mCPUAttached[i])
1613 {
1614 cCpusAttached++;
1615 iHighestId = i;
1616 }
1617 }
1618
1619 if ( (cCpusAttached != mHWData->mCPUCount)
1620 || (iHighestId >= mHWData->mCPUCount))
1621 return setError(E_INVALIDARG,
1622 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1623
1624 i_setModified(IsModified_MachineData);
1625 mHWData.backup();
1626 }
1627 }
1628
1629 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1630
1631 return rc;
1632}
1633
1634HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1635{
1636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1637
1638 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1639
1640 return S_OK;
1641}
1642
1643HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1644{
1645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1646
1647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1648 if (SUCCEEDED(hrc))
1649 {
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1653 }
1654 return hrc;
1655}
1656
1657HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1658{
1659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1660 aCPUProfile = mHWData->mCpuProfile;
1661 return S_OK;
1662}
1663
1664HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1665{
1666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1667 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1668 if (SUCCEEDED(hrc))
1669 {
1670 i_setModified(IsModified_MachineData);
1671 mHWData.backup();
1672 /* Empty equals 'host'. */
1673 if (aCPUProfile.isNotEmpty())
1674 mHWData->mCpuProfile = aCPUProfile;
1675 else
1676 mHWData->mCpuProfile = "host";
1677 }
1678 return hrc;
1679}
1680
1681HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1682{
1683#ifdef VBOX_WITH_USB_CARDREADER
1684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1685
1686 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1687
1688 return S_OK;
1689#else
1690 NOREF(aEmulatedUSBCardReaderEnabled);
1691 return E_NOTIMPL;
1692#endif
1693}
1694
1695HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1696{
1697#ifdef VBOX_WITH_USB_CARDREADER
1698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1701 if (FAILED(rc)) return rc;
1702
1703 i_setModified(IsModified_MachineData);
1704 mHWData.backup();
1705 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1706
1707 return S_OK;
1708#else
1709 NOREF(aEmulatedUSBCardReaderEnabled);
1710 return E_NOTIMPL;
1711#endif
1712}
1713
1714HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1715{
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 *aHPETEnabled = mHWData->mHPETEnabled;
1719
1720 return S_OK;
1721}
1722
1723HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1724{
1725 HRESULT rc = S_OK;
1726
1727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1728
1729 rc = i_checkStateDependency(MutableStateDep);
1730 if (FAILED(rc)) return rc;
1731
1732 i_setModified(IsModified_MachineData);
1733 mHWData.backup();
1734
1735 mHWData->mHPETEnabled = aHPETEnabled;
1736
1737 return rc;
1738}
1739
1740/** @todo this method should not be public */
1741HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1742{
1743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1744
1745 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1746
1747 return S_OK;
1748}
1749
1750/**
1751 * Set the memory balloon size.
1752 *
1753 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1754 * we have to make sure that we never call IGuest from here.
1755 */
1756HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1757{
1758 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1759#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1760 /* check limits */
1761 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1762 return setError(E_INVALIDARG,
1763 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1764 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1765
1766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1767
1768 i_setModified(IsModified_MachineData);
1769 mHWData.backup();
1770 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1771
1772 return S_OK;
1773#else
1774 NOREF(aMemoryBalloonSize);
1775 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1776#endif
1777}
1778
1779HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1780{
1781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1782
1783 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1784 return S_OK;
1785}
1786
1787HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1788{
1789#ifdef VBOX_WITH_PAGE_SHARING
1790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1791
1792 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1793 i_setModified(IsModified_MachineData);
1794 mHWData.backup();
1795 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1796 return S_OK;
1797#else
1798 NOREF(aPageFusionEnabled);
1799 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1800#endif
1801}
1802
1803HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1804{
1805 /* mBIOSSettings is constant during life time, no need to lock */
1806 aBIOSSettings = mBIOSSettings;
1807
1808 return S_OK;
1809}
1810
1811HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1812{
1813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 aRecordingSettings = mRecordingSettings;
1816
1817 return S_OK;
1818}
1819
1820HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 aGraphicsAdapter = mGraphicsAdapter;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832
1833 switch (aProperty)
1834 {
1835 case CPUPropertyType_PAE:
1836 *aValue = mHWData->mPAEEnabled;
1837 break;
1838
1839 case CPUPropertyType_LongMode:
1840 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1841 *aValue = TRUE;
1842 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1843 *aValue = FALSE;
1844#if HC_ARCH_BITS == 64
1845 else
1846 *aValue = TRUE;
1847#else
1848 else
1849 {
1850 *aValue = FALSE;
1851
1852 ComObjPtr<GuestOSType> pGuestOSType;
1853 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1854 pGuestOSType);
1855 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1856 {
1857 if (pGuestOSType->i_is64Bit())
1858 {
1859 ComObjPtr<Host> pHost = mParent->i_host();
1860 alock.release();
1861
1862 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1863 if (FAILED(hrc2))
1864 *aValue = FALSE;
1865 }
1866 }
1867 }
1868#endif
1869 break;
1870
1871 case CPUPropertyType_TripleFaultReset:
1872 *aValue = mHWData->mTripleFaultReset;
1873 break;
1874
1875 case CPUPropertyType_APIC:
1876 *aValue = mHWData->mAPIC;
1877 break;
1878
1879 case CPUPropertyType_X2APIC:
1880 *aValue = mHWData->mX2APIC;
1881 break;
1882
1883 case CPUPropertyType_IBPBOnVMExit:
1884 *aValue = mHWData->mIBPBOnVMExit;
1885 break;
1886
1887 case CPUPropertyType_IBPBOnVMEntry:
1888 *aValue = mHWData->mIBPBOnVMEntry;
1889 break;
1890
1891 case CPUPropertyType_SpecCtrl:
1892 *aValue = mHWData->mSpecCtrl;
1893 break;
1894
1895 case CPUPropertyType_SpecCtrlByHost:
1896 *aValue = mHWData->mSpecCtrlByHost;
1897 break;
1898
1899 case CPUPropertyType_HWVirt:
1900 *aValue = mHWData->mNestedHWVirt;
1901 break;
1902
1903 case CPUPropertyType_L1DFlushOnEMTScheduling:
1904 *aValue = mHWData->mL1DFlushOnSched;
1905 break;
1906
1907 case CPUPropertyType_L1DFlushOnVMEntry:
1908 *aValue = mHWData->mL1DFlushOnVMEntry;
1909 break;
1910
1911 case CPUPropertyType_MDSClearOnEMTScheduling:
1912 *aValue = mHWData->mMDSClearOnSched;
1913 break;
1914
1915 case CPUPropertyType_MDSClearOnVMEntry:
1916 *aValue = mHWData->mMDSClearOnVMEntry;
1917 break;
1918
1919 default:
1920 return E_INVALIDARG;
1921 }
1922 return S_OK;
1923}
1924
1925HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1926{
1927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 HRESULT rc = i_checkStateDependency(MutableStateDep);
1930 if (FAILED(rc)) return rc;
1931
1932 switch (aProperty)
1933 {
1934 case CPUPropertyType_PAE:
1935 i_setModified(IsModified_MachineData);
1936 mHWData.backup();
1937 mHWData->mPAEEnabled = !!aValue;
1938 break;
1939
1940 case CPUPropertyType_LongMode:
1941 i_setModified(IsModified_MachineData);
1942 mHWData.backup();
1943 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1944 break;
1945
1946 case CPUPropertyType_TripleFaultReset:
1947 i_setModified(IsModified_MachineData);
1948 mHWData.backup();
1949 mHWData->mTripleFaultReset = !!aValue;
1950 break;
1951
1952 case CPUPropertyType_APIC:
1953 if (mHWData->mX2APIC)
1954 aValue = TRUE;
1955 i_setModified(IsModified_MachineData);
1956 mHWData.backup();
1957 mHWData->mAPIC = !!aValue;
1958 break;
1959
1960 case CPUPropertyType_X2APIC:
1961 i_setModified(IsModified_MachineData);
1962 mHWData.backup();
1963 mHWData->mX2APIC = !!aValue;
1964 if (aValue)
1965 mHWData->mAPIC = !!aValue;
1966 break;
1967
1968 case CPUPropertyType_IBPBOnVMExit:
1969 i_setModified(IsModified_MachineData);
1970 mHWData.backup();
1971 mHWData->mIBPBOnVMExit = !!aValue;
1972 break;
1973
1974 case CPUPropertyType_IBPBOnVMEntry:
1975 i_setModified(IsModified_MachineData);
1976 mHWData.backup();
1977 mHWData->mIBPBOnVMEntry = !!aValue;
1978 break;
1979
1980 case CPUPropertyType_SpecCtrl:
1981 i_setModified(IsModified_MachineData);
1982 mHWData.backup();
1983 mHWData->mSpecCtrl = !!aValue;
1984 break;
1985
1986 case CPUPropertyType_SpecCtrlByHost:
1987 i_setModified(IsModified_MachineData);
1988 mHWData.backup();
1989 mHWData->mSpecCtrlByHost = !!aValue;
1990 break;
1991
1992 case CPUPropertyType_HWVirt:
1993 i_setModified(IsModified_MachineData);
1994 mHWData.backup();
1995 mHWData->mNestedHWVirt = !!aValue;
1996 break;
1997
1998 case CPUPropertyType_L1DFlushOnEMTScheduling:
1999 i_setModified(IsModified_MachineData);
2000 mHWData.backup();
2001 mHWData->mL1DFlushOnSched = !!aValue;
2002 break;
2003
2004 case CPUPropertyType_L1DFlushOnVMEntry:
2005 i_setModified(IsModified_MachineData);
2006 mHWData.backup();
2007 mHWData->mL1DFlushOnVMEntry = !!aValue;
2008 break;
2009
2010 case CPUPropertyType_MDSClearOnEMTScheduling:
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mMDSClearOnSched = !!aValue;
2014 break;
2015
2016 case CPUPropertyType_MDSClearOnVMEntry:
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mMDSClearOnVMEntry = !!aValue;
2020 break;
2021
2022 default:
2023 return E_INVALIDARG;
2024 }
2025 return S_OK;
2026}
2027
2028HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2029 ULONG *aValEcx, ULONG *aValEdx)
2030{
2031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2032 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2033 {
2034 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2035 it != mHWData->mCpuIdLeafList.end();
2036 ++it)
2037 {
2038 if (aOrdinal == 0)
2039 {
2040 const settings::CpuIdLeaf &rLeaf= *it;
2041 *aIdx = rLeaf.idx;
2042 *aSubIdx = rLeaf.idxSub;
2043 *aValEax = rLeaf.uEax;
2044 *aValEbx = rLeaf.uEbx;
2045 *aValEcx = rLeaf.uEcx;
2046 *aValEdx = rLeaf.uEdx;
2047 return S_OK;
2048 }
2049 aOrdinal--;
2050 }
2051 }
2052 return E_INVALIDARG;
2053}
2054
2055HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2056{
2057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 /*
2060 * Search the list.
2061 */
2062 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2063 {
2064 const settings::CpuIdLeaf &rLeaf= *it;
2065 if ( rLeaf.idx == aIdx
2066 && ( aSubIdx == UINT32_MAX
2067 || rLeaf.idxSub == aSubIdx) )
2068 {
2069 *aValEax = rLeaf.uEax;
2070 *aValEbx = rLeaf.uEbx;
2071 *aValEcx = rLeaf.uEcx;
2072 *aValEdx = rLeaf.uEdx;
2073 return S_OK;
2074 }
2075 }
2076
2077 return E_INVALIDARG;
2078}
2079
2080
2081HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2082{
2083 /*
2084 * Validate input before taking locks and checking state.
2085 */
2086 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2087 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2088 if ( aIdx >= UINT32_C(0x20)
2089 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2090 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2091 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2092
2093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2094 HRESULT rc = i_checkStateDependency(MutableStateDep);
2095 if (FAILED(rc)) return rc;
2096
2097 /*
2098 * Impose a maximum number of leaves.
2099 */
2100 if (mHWData->mCpuIdLeafList.size() > 256)
2101 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2102
2103 /*
2104 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2105 */
2106 i_setModified(IsModified_MachineData);
2107 mHWData.backup();
2108
2109 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2110 {
2111 settings::CpuIdLeaf &rLeaf= *it;
2112 if ( rLeaf.idx == aIdx
2113 && ( aSubIdx == UINT32_MAX
2114 || rLeaf.idxSub == aSubIdx) )
2115 it = mHWData->mCpuIdLeafList.erase(it);
2116 else
2117 ++it;
2118 }
2119
2120 settings::CpuIdLeaf NewLeaf;
2121 NewLeaf.idx = aIdx;
2122 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2123 NewLeaf.uEax = aValEax;
2124 NewLeaf.uEbx = aValEbx;
2125 NewLeaf.uEcx = aValEcx;
2126 NewLeaf.uEdx = aValEdx;
2127 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2128 return S_OK;
2129}
2130
2131HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2132{
2133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 HRESULT rc = i_checkStateDependency(MutableStateDep);
2136 if (FAILED(rc)) return rc;
2137
2138 /*
2139 * Do the removal.
2140 */
2141 bool fModified = mHWData.isBackedUp();
2142 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2143 {
2144 settings::CpuIdLeaf &rLeaf= *it;
2145 if ( rLeaf.idx == aIdx
2146 && ( aSubIdx == UINT32_MAX
2147 || rLeaf.idxSub == aSubIdx) )
2148 {
2149 if (!fModified)
2150 {
2151 fModified = true;
2152 i_setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 // Start from the beginning, since mHWData.backup() creates
2155 // a new list, causing iterator mixup. This makes sure that
2156 // the settings are not unnecessarily marked as modified,
2157 // at the price of extra list walking.
2158 it = mHWData->mCpuIdLeafList.begin();
2159 }
2160 else
2161 it = mHWData->mCpuIdLeafList.erase(it);
2162 }
2163 else
2164 ++it;
2165 }
2166
2167 return S_OK;
2168}
2169
2170HRESULT Machine::removeAllCPUIDLeaves()
2171{
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 HRESULT rc = i_checkStateDependency(MutableStateDep);
2175 if (FAILED(rc)) return rc;
2176
2177 if (mHWData->mCpuIdLeafList.size() > 0)
2178 {
2179 i_setModified(IsModified_MachineData);
2180 mHWData.backup();
2181
2182 mHWData->mCpuIdLeafList.clear();
2183 }
2184
2185 return S_OK;
2186}
2187HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2188{
2189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2190
2191 switch(aProperty)
2192 {
2193 case HWVirtExPropertyType_Enabled:
2194 *aValue = mHWData->mHWVirtExEnabled;
2195 break;
2196
2197 case HWVirtExPropertyType_VPID:
2198 *aValue = mHWData->mHWVirtExVPIDEnabled;
2199 break;
2200
2201 case HWVirtExPropertyType_NestedPaging:
2202 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2203 break;
2204
2205 case HWVirtExPropertyType_UnrestrictedExecution:
2206 *aValue = mHWData->mHWVirtExUXEnabled;
2207 break;
2208
2209 case HWVirtExPropertyType_LargePages:
2210 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2211#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2212 *aValue = FALSE;
2213#endif
2214 break;
2215
2216 case HWVirtExPropertyType_Force:
2217 *aValue = mHWData->mHWVirtExForceEnabled;
2218 break;
2219
2220 case HWVirtExPropertyType_UseNativeApi:
2221 *aValue = mHWData->mHWVirtExUseNativeApi;
2222 break;
2223
2224 default:
2225 return E_INVALIDARG;
2226 }
2227 return S_OK;
2228}
2229
2230HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2231{
2232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2233
2234 HRESULT rc = i_checkStateDependency(MutableStateDep);
2235 if (FAILED(rc)) return rc;
2236
2237 switch (aProperty)
2238 {
2239 case HWVirtExPropertyType_Enabled:
2240 i_setModified(IsModified_MachineData);
2241 mHWData.backup();
2242 mHWData->mHWVirtExEnabled = !!aValue;
2243 break;
2244
2245 case HWVirtExPropertyType_VPID:
2246 i_setModified(IsModified_MachineData);
2247 mHWData.backup();
2248 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2249 break;
2250
2251 case HWVirtExPropertyType_NestedPaging:
2252 i_setModified(IsModified_MachineData);
2253 mHWData.backup();
2254 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2255 break;
2256
2257 case HWVirtExPropertyType_UnrestrictedExecution:
2258 i_setModified(IsModified_MachineData);
2259 mHWData.backup();
2260 mHWData->mHWVirtExUXEnabled = !!aValue;
2261 break;
2262
2263 case HWVirtExPropertyType_LargePages:
2264 i_setModified(IsModified_MachineData);
2265 mHWData.backup();
2266 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2267 break;
2268
2269 case HWVirtExPropertyType_Force:
2270 i_setModified(IsModified_MachineData);
2271 mHWData.backup();
2272 mHWData->mHWVirtExForceEnabled = !!aValue;
2273 break;
2274
2275 case HWVirtExPropertyType_UseNativeApi:
2276 i_setModified(IsModified_MachineData);
2277 mHWData.backup();
2278 mHWData->mHWVirtExUseNativeApi = !!aValue;
2279 break;
2280
2281 default:
2282 return E_INVALIDARG;
2283 }
2284
2285 return S_OK;
2286}
2287
2288HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2289{
2290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2291
2292 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2293
2294 return S_OK;
2295}
2296
2297HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2298{
2299 /** @todo (r=dmik):
2300 * 1. Allow to change the name of the snapshot folder containing snapshots
2301 * 2. Rename the folder on disk instead of just changing the property
2302 * value (to be smart and not to leave garbage). Note that it cannot be
2303 * done here because the change may be rolled back. Thus, the right
2304 * place is #saveSettings().
2305 */
2306
2307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 HRESULT rc = i_checkStateDependency(MutableStateDep);
2310 if (FAILED(rc)) return rc;
2311
2312 if (!mData->mCurrentSnapshot.isNull())
2313 return setError(E_FAIL,
2314 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2315
2316 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2317
2318 if (strSnapshotFolder.isEmpty())
2319 strSnapshotFolder = "Snapshots";
2320 int vrc = i_calculateFullPath(strSnapshotFolder,
2321 strSnapshotFolder);
2322 if (RT_FAILURE(vrc))
2323 return setErrorBoth(E_FAIL, vrc,
2324 tr("Invalid snapshot folder '%s' (%Rrc)"),
2325 strSnapshotFolder.c_str(), vrc);
2326
2327 i_setModified(IsModified_MachineData);
2328 mUserData.backup();
2329
2330 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2331
2332 return S_OK;
2333}
2334
2335HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2336{
2337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2338
2339 aMediumAttachments.resize(mMediumAttachments->size());
2340 size_t i = 0;
2341 for (MediumAttachmentList::const_iterator
2342 it = mMediumAttachments->begin();
2343 it != mMediumAttachments->end();
2344 ++it, ++i)
2345 aMediumAttachments[i] = *it;
2346
2347 return S_OK;
2348}
2349
2350HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2351{
2352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2353
2354 Assert(!!mVRDEServer);
2355
2356 aVRDEServer = mVRDEServer;
2357
2358 return S_OK;
2359}
2360
2361HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2362{
2363 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2364
2365 aAudioAdapter = mAudioAdapter;
2366
2367 return S_OK;
2368}
2369
2370HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2371{
2372#ifdef VBOX_WITH_VUSB
2373 clearError();
2374 MultiResult rc(S_OK);
2375
2376# ifdef VBOX_WITH_USB
2377 rc = mParent->i_host()->i_checkUSBProxyService();
2378 if (FAILED(rc)) return rc;
2379# endif
2380
2381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2382
2383 aUSBControllers.resize(mUSBControllers->size());
2384 size_t i = 0;
2385 for (USBControllerList::const_iterator
2386 it = mUSBControllers->begin();
2387 it != mUSBControllers->end();
2388 ++it, ++i)
2389 aUSBControllers[i] = *it;
2390
2391 return S_OK;
2392#else
2393 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2394 * extended error info to indicate that USB is simply not available
2395 * (w/o treating it as a failure), for example, as in OSE */
2396 NOREF(aUSBControllers);
2397 ReturnComNotImplemented();
2398#endif /* VBOX_WITH_VUSB */
2399}
2400
2401HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2402{
2403#ifdef VBOX_WITH_VUSB
2404 clearError();
2405 MultiResult rc(S_OK);
2406
2407# ifdef VBOX_WITH_USB
2408 rc = mParent->i_host()->i_checkUSBProxyService();
2409 if (FAILED(rc)) return rc;
2410# endif
2411
2412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2413
2414 aUSBDeviceFilters = mUSBDeviceFilters;
2415 return rc;
2416#else
2417 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2418 * extended error info to indicate that USB is simply not available
2419 * (w/o treating it as a failure), for example, as in OSE */
2420 NOREF(aUSBDeviceFilters);
2421 ReturnComNotImplemented();
2422#endif /* VBOX_WITH_VUSB */
2423}
2424
2425HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2426{
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 aSettingsFilePath = mData->m_strConfigFileFull;
2430
2431 return S_OK;
2432}
2433
2434HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2435{
2436 RT_NOREF(aSettingsFilePath);
2437 ReturnComNotImplemented();
2438}
2439
2440HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2441{
2442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2443
2444 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2445 if (FAILED(rc)) return rc;
2446
2447 if (!mData->pMachineConfigFile->fileExists())
2448 // this is a new machine, and no config file exists yet:
2449 *aSettingsModified = TRUE;
2450 else
2451 *aSettingsModified = (mData->flModifications != 0);
2452
2453 return S_OK;
2454}
2455
2456HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2457{
2458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2459
2460 *aSessionState = mData->mSession.mState;
2461
2462 return S_OK;
2463}
2464
2465HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2466{
2467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2468
2469 aSessionName = mData->mSession.mName;
2470
2471 return S_OK;
2472}
2473
2474HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2475{
2476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 *aSessionPID = mData->mSession.mPID;
2479
2480 return S_OK;
2481}
2482
2483HRESULT Machine::getState(MachineState_T *aState)
2484{
2485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 *aState = mData->mMachineState;
2488 Assert(mData->mMachineState != MachineState_Null);
2489
2490 return S_OK;
2491}
2492
2493HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2494{
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2498
2499 return S_OK;
2500}
2501
2502HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2503{
2504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2505
2506 aStateFilePath = mSSData->strStateFilePath;
2507
2508 return S_OK;
2509}
2510
2511HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2512{
2513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2514
2515 i_getLogFolder(aLogFolder);
2516
2517 return S_OK;
2518}
2519
2520HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2521{
2522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2523
2524 aCurrentSnapshot = mData->mCurrentSnapshot;
2525
2526 return S_OK;
2527}
2528
2529HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2530{
2531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2532
2533 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2534 ? 0
2535 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2536
2537 return S_OK;
2538}
2539
2540HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2541{
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 /* Note: for machines with no snapshots, we always return FALSE
2545 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2546 * reasons :) */
2547
2548 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2549 ? FALSE
2550 : mData->mCurrentStateModified;
2551
2552 return S_OK;
2553}
2554
2555HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2556{
2557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2558
2559 aSharedFolders.resize(mHWData->mSharedFolders.size());
2560 size_t i = 0;
2561 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2562 it = mHWData->mSharedFolders.begin();
2563 it != mHWData->mSharedFolders.end();
2564 ++it, ++i)
2565 aSharedFolders[i] = *it;
2566
2567 return S_OK;
2568}
2569
2570HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2571{
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 *aClipboardMode = mHWData->mClipboardMode;
2575
2576 return S_OK;
2577}
2578
2579HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2580{
2581 HRESULT rc = S_OK;
2582
2583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2584
2585 alock.release();
2586 rc = i_onClipboardModeChange(aClipboardMode);
2587 alock.acquire();
2588 if (FAILED(rc)) return rc;
2589
2590 i_setModified(IsModified_MachineData);
2591 mHWData.backup();
2592 mHWData->mClipboardMode = aClipboardMode;
2593
2594 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2595 if (Global::IsOnline(mData->mMachineState))
2596 i_saveSettings(NULL);
2597
2598 return S_OK;
2599}
2600
2601HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2602{
2603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2604
2605 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2606
2607 return S_OK;
2608}
2609
2610HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2611{
2612 HRESULT rc = S_OK;
2613
2614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 alock.release();
2617 rc = i_onClipboardFileTransferModeChange(aEnabled);
2618 alock.acquire();
2619 if (FAILED(rc)) return rc;
2620
2621 i_setModified(IsModified_MachineData);
2622 mHWData.backup();
2623 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2624
2625 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2626 if (Global::IsOnline(mData->mMachineState))
2627 i_saveSettings(NULL);
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 *aDnDMode = mHWData->mDnDMode;
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2642{
2643 HRESULT rc = S_OK;
2644
2645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2646
2647 alock.release();
2648 rc = i_onDnDModeChange(aDnDMode);
2649
2650 alock.acquire();
2651 if (FAILED(rc)) return rc;
2652
2653 i_setModified(IsModified_MachineData);
2654 mHWData.backup();
2655 mHWData->mDnDMode = aDnDMode;
2656
2657 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2658 if (Global::IsOnline(mData->mMachineState))
2659 i_saveSettings(NULL);
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 aStorageControllers.resize(mStorageControllers->size());
2669 size_t i = 0;
2670 for (StorageControllerList::const_iterator
2671 it = mStorageControllers->begin();
2672 it != mStorageControllers->end();
2673 ++it, ++i)
2674 aStorageControllers[i] = *it;
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2680{
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 *aEnabled = mUserData->s.fTeleporterEnabled;
2684
2685 return S_OK;
2686}
2687
2688HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2689{
2690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 /* Only allow it to be set to true when PoweredOff or Aborted.
2693 (Clearing it is always permitted.) */
2694 if ( aTeleporterEnabled
2695 && mData->mRegistered
2696 && ( !i_isSessionMachine()
2697 || ( mData->mMachineState != MachineState_PoweredOff
2698 && mData->mMachineState != MachineState_Teleported
2699 && mData->mMachineState != MachineState_Aborted
2700 )
2701 )
2702 )
2703 return setError(VBOX_E_INVALID_VM_STATE,
2704 tr("The machine is not powered off (state is %s)"),
2705 Global::stringifyMachineState(mData->mMachineState));
2706
2707 i_setModified(IsModified_MachineData);
2708 mUserData.backup();
2709 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2710
2711 return S_OK;
2712}
2713
2714HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2715{
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2719
2720 return S_OK;
2721}
2722
2723HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2724{
2725 if (aTeleporterPort >= _64K)
2726 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2727
2728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2729
2730 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2731 if (FAILED(rc)) return rc;
2732
2733 i_setModified(IsModified_MachineData);
2734 mUserData.backup();
2735 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2736
2737 return S_OK;
2738}
2739
2740HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2741{
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2745
2746 return S_OK;
2747}
2748
2749HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2750{
2751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2752
2753 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2754 if (FAILED(rc)) return rc;
2755
2756 i_setModified(IsModified_MachineData);
2757 mUserData.backup();
2758 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2759
2760 return S_OK;
2761}
2762
2763HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2764{
2765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2766 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2767
2768 return S_OK;
2769}
2770
2771HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2772{
2773 /*
2774 * Hash the password first.
2775 */
2776 com::Utf8Str aT = aTeleporterPassword;
2777
2778 if (!aT.isEmpty())
2779 {
2780 if (VBoxIsPasswordHashed(&aT))
2781 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2782 VBoxHashPassword(&aT);
2783 }
2784
2785 /*
2786 * Do the update.
2787 */
2788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2789 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2790 if (SUCCEEDED(hrc))
2791 {
2792 i_setModified(IsModified_MachineData);
2793 mUserData.backup();
2794 mUserData->s.strTeleporterPassword = aT;
2795 }
2796
2797 return hrc;
2798}
2799
2800HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2801{
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2805
2806 return S_OK;
2807}
2808
2809HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2810{
2811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 /* Only allow it to be set to true when PoweredOff or Aborted.
2814 (Clearing it is always permitted.) */
2815 if ( aRTCUseUTC
2816 && mData->mRegistered
2817 && ( !i_isSessionMachine()
2818 || ( mData->mMachineState != MachineState_PoweredOff
2819 && mData->mMachineState != MachineState_Teleported
2820 && mData->mMachineState != MachineState_Aborted
2821 )
2822 )
2823 )
2824 return setError(VBOX_E_INVALID_VM_STATE,
2825 tr("The machine is not powered off (state is %s)"),
2826 Global::stringifyMachineState(mData->mMachineState));
2827
2828 i_setModified(IsModified_MachineData);
2829 mUserData.backup();
2830 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2831
2832 return S_OK;
2833}
2834
2835HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2836{
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2840
2841 return S_OK;
2842}
2843
2844HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2845{
2846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 HRESULT rc = i_checkStateDependency(MutableStateDep);
2849 if (FAILED(rc)) return rc;
2850
2851 i_setModified(IsModified_MachineData);
2852 mHWData.backup();
2853 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2854
2855 return S_OK;
2856}
2857
2858HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2859{
2860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 *aIOCacheSize = mHWData->mIOCacheSize;
2863
2864 return S_OK;
2865}
2866
2867HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2868{
2869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2870
2871 HRESULT rc = i_checkStateDependency(MutableStateDep);
2872 if (FAILED(rc)) return rc;
2873
2874 i_setModified(IsModified_MachineData);
2875 mHWData.backup();
2876 mHWData->mIOCacheSize = aIOCacheSize;
2877
2878 return S_OK;
2879}
2880
2881
2882/**
2883 * @note Locks objects!
2884 */
2885HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2886 LockType_T aLockType)
2887{
2888 /* check the session state */
2889 SessionState_T state;
2890 HRESULT rc = aSession->COMGETTER(State)(&state);
2891 if (FAILED(rc)) return rc;
2892
2893 if (state != SessionState_Unlocked)
2894 return setError(VBOX_E_INVALID_OBJECT_STATE,
2895 tr("The given session is busy"));
2896
2897 // get the client's IInternalSessionControl interface
2898 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2899 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2900 E_INVALIDARG);
2901
2902 // session name (only used in some code paths)
2903 Utf8Str strSessionName;
2904
2905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 if (!mData->mRegistered)
2908 return setError(E_UNEXPECTED,
2909 tr("The machine '%s' is not registered"),
2910 mUserData->s.strName.c_str());
2911
2912 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2913
2914 SessionState_T oldState = mData->mSession.mState;
2915 /* Hack: in case the session is closing and there is a progress object
2916 * which allows waiting for the session to be closed, take the opportunity
2917 * and do a limited wait (max. 1 second). This helps a lot when the system
2918 * is busy and thus session closing can take a little while. */
2919 if ( mData->mSession.mState == SessionState_Unlocking
2920 && mData->mSession.mProgress)
2921 {
2922 alock.release();
2923 mData->mSession.mProgress->WaitForCompletion(1000);
2924 alock.acquire();
2925 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2926 }
2927
2928 // try again now
2929 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2930 // (i.e. session machine exists)
2931 && (aLockType == LockType_Shared) // caller wants a shared link to the
2932 // existing session that holds the write lock:
2933 )
2934 {
2935 // OK, share the session... we are now dealing with three processes:
2936 // 1) VBoxSVC (where this code runs);
2937 // 2) process C: the caller's client process (who wants a shared session);
2938 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2939
2940 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2941 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2942 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2943 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2944 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2945
2946 /*
2947 * Release the lock before calling the client process. It's safe here
2948 * since the only thing to do after we get the lock again is to add
2949 * the remote control to the list (which doesn't directly influence
2950 * anything).
2951 */
2952 alock.release();
2953
2954 // get the console of the session holding the write lock (this is a remote call)
2955 ComPtr<IConsole> pConsoleW;
2956 if (mData->mSession.mLockType == LockType_VM)
2957 {
2958 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2959 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2960 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2961 if (FAILED(rc))
2962 // the failure may occur w/o any error info (from RPC), so provide one
2963 return setError(VBOX_E_VM_ERROR,
2964 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
2965 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2966 }
2967
2968 // share the session machine and W's console with the caller's session
2969 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2970 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2971 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
2972
2973 if (FAILED(rc))
2974 // the failure may occur w/o any error info (from RPC), so provide one
2975 return setError(VBOX_E_VM_ERROR,
2976 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
2977 alock.acquire();
2978
2979 // need to revalidate the state after acquiring the lock again
2980 if (mData->mSession.mState != SessionState_Locked)
2981 {
2982 pSessionControl->Uninitialize();
2983 return setError(VBOX_E_INVALID_SESSION_STATE,
2984 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2985 mUserData->s.strName.c_str());
2986 }
2987
2988 // add the caller's session to the list
2989 mData->mSession.mRemoteControls.push_back(pSessionControl);
2990 }
2991 else if ( mData->mSession.mState == SessionState_Locked
2992 || mData->mSession.mState == SessionState_Unlocking
2993 )
2994 {
2995 // sharing not permitted, or machine still unlocking:
2996 return setError(VBOX_E_INVALID_OBJECT_STATE,
2997 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2998 mUserData->s.strName.c_str());
2999 }
3000 else
3001 {
3002 // machine is not locked: then write-lock the machine (create the session machine)
3003
3004 // must not be busy
3005 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3006
3007 // get the caller's session PID
3008 RTPROCESS pid = NIL_RTPROCESS;
3009 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3010 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3011 Assert(pid != NIL_RTPROCESS);
3012
3013 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3014
3015 if (fLaunchingVMProcess)
3016 {
3017 if (mData->mSession.mPID == NIL_RTPROCESS)
3018 {
3019 // two or more clients racing for a lock, the one which set the
3020 // session state to Spawning will win, the others will get an
3021 // error as we can't decide here if waiting a little would help
3022 // (only for shared locks this would avoid an error)
3023 return setError(VBOX_E_INVALID_OBJECT_STATE,
3024 tr("The machine '%s' already has a lock request pending"),
3025 mUserData->s.strName.c_str());
3026 }
3027
3028 // this machine is awaiting for a spawning session to be opened:
3029 // then the calling process must be the one that got started by
3030 // LaunchVMProcess()
3031
3032 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3033 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3034
3035#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3036 /* Hardened windows builds spawns three processes when a VM is
3037 launched, the 3rd one is the one that will end up here. */
3038 RTPROCESS pidParent;
3039 int vrc = RTProcQueryParent(pid, &pidParent);
3040 if (RT_SUCCESS(vrc))
3041 vrc = RTProcQueryParent(pidParent, &pidParent);
3042 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3043 || vrc == VERR_ACCESS_DENIED)
3044 {
3045 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3046 mData->mSession.mPID = pid;
3047 }
3048#endif
3049
3050 if (mData->mSession.mPID != pid)
3051 return setError(E_ACCESSDENIED,
3052 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3053 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3054 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3055 }
3056
3057 // create the mutable SessionMachine from the current machine
3058 ComObjPtr<SessionMachine> sessionMachine;
3059 sessionMachine.createObject();
3060 rc = sessionMachine->init(this);
3061 AssertComRC(rc);
3062
3063 /* NOTE: doing return from this function after this point but
3064 * before the end is forbidden since it may call SessionMachine::uninit()
3065 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3066 * lock while still holding the Machine lock in alock so that a deadlock
3067 * is possible due to the wrong lock order. */
3068
3069 if (SUCCEEDED(rc))
3070 {
3071 /*
3072 * Set the session state to Spawning to protect against subsequent
3073 * attempts to open a session and to unregister the machine after
3074 * we release the lock.
3075 */
3076 SessionState_T origState = mData->mSession.mState;
3077 mData->mSession.mState = SessionState_Spawning;
3078
3079#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3080 /* Get the client token ID to be passed to the client process */
3081 Utf8Str strTokenId;
3082 sessionMachine->i_getTokenId(strTokenId);
3083 Assert(!strTokenId.isEmpty());
3084#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3085 /* Get the client token to be passed to the client process */
3086 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3087 /* The token is now "owned" by pToken, fix refcount */
3088 if (!pToken.isNull())
3089 pToken->Release();
3090#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3091
3092 /*
3093 * Release the lock before calling the client process -- it will call
3094 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3095 * because the state is Spawning, so that LaunchVMProcess() and
3096 * LockMachine() calls will fail. This method, called before we
3097 * acquire the lock again, will fail because of the wrong PID.
3098 *
3099 * Note that mData->mSession.mRemoteControls accessed outside
3100 * the lock may not be modified when state is Spawning, so it's safe.
3101 */
3102 alock.release();
3103
3104 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3105#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3106 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3107#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3108 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3109 /* Now the token is owned by the client process. */
3110 pToken.setNull();
3111#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3112 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3113
3114 /* The failure may occur w/o any error info (from RPC), so provide one */
3115 if (FAILED(rc))
3116 setError(VBOX_E_VM_ERROR,
3117 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3118
3119 // get session name, either to remember or to compare against
3120 // the already known session name.
3121 {
3122 Bstr bstrSessionName;
3123 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3124 if (SUCCEEDED(rc2))
3125 strSessionName = bstrSessionName;
3126 }
3127
3128 if ( SUCCEEDED(rc)
3129 && fLaunchingVMProcess
3130 )
3131 {
3132 /* complete the remote session initialization */
3133
3134 /* get the console from the direct session */
3135 ComPtr<IConsole> console;
3136 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3137 ComAssertComRC(rc);
3138
3139 if (SUCCEEDED(rc) && !console)
3140 {
3141 ComAssert(!!console);
3142 rc = E_FAIL;
3143 }
3144
3145 /* assign machine & console to the remote session */
3146 if (SUCCEEDED(rc))
3147 {
3148 /*
3149 * after LaunchVMProcess(), the first and the only
3150 * entry in remoteControls is that remote session
3151 */
3152 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3153 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3154 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3155
3156 /* The failure may occur w/o any error info (from RPC), so provide one */
3157 if (FAILED(rc))
3158 setError(VBOX_E_VM_ERROR,
3159 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3160 }
3161
3162 if (FAILED(rc))
3163 pSessionControl->Uninitialize();
3164 }
3165
3166 /* acquire the lock again */
3167 alock.acquire();
3168
3169 /* Restore the session state */
3170 mData->mSession.mState = origState;
3171 }
3172
3173 // finalize spawning anyway (this is why we don't return on errors above)
3174 if (fLaunchingVMProcess)
3175 {
3176 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3177 /* Note that the progress object is finalized later */
3178 /** @todo Consider checking mData->mSession.mProgress for cancellation
3179 * around here. */
3180
3181 /* We don't reset mSession.mPID here because it is necessary for
3182 * SessionMachine::uninit() to reap the child process later. */
3183
3184 if (FAILED(rc))
3185 {
3186 /* Close the remote session, remove the remote control from the list
3187 * and reset session state to Closed (@note keep the code in sync
3188 * with the relevant part in checkForSpawnFailure()). */
3189
3190 Assert(mData->mSession.mRemoteControls.size() == 1);
3191 if (mData->mSession.mRemoteControls.size() == 1)
3192 {
3193 ErrorInfoKeeper eik;
3194 mData->mSession.mRemoteControls.front()->Uninitialize();
3195 }
3196
3197 mData->mSession.mRemoteControls.clear();
3198 mData->mSession.mState = SessionState_Unlocked;
3199 }
3200 }
3201 else
3202 {
3203 /* memorize PID of the directly opened session */
3204 if (SUCCEEDED(rc))
3205 mData->mSession.mPID = pid;
3206 }
3207
3208 if (SUCCEEDED(rc))
3209 {
3210 mData->mSession.mLockType = aLockType;
3211 /* memorize the direct session control and cache IUnknown for it */
3212 mData->mSession.mDirectControl = pSessionControl;
3213 mData->mSession.mState = SessionState_Locked;
3214 if (!fLaunchingVMProcess)
3215 mData->mSession.mName = strSessionName;
3216 /* associate the SessionMachine with this Machine */
3217 mData->mSession.mMachine = sessionMachine;
3218
3219 /* request an IUnknown pointer early from the remote party for later
3220 * identity checks (it will be internally cached within mDirectControl
3221 * at least on XPCOM) */
3222 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3223 NOREF(unk);
3224 }
3225
3226 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3227 * would break the lock order */
3228 alock.release();
3229
3230 /* uninitialize the created session machine on failure */
3231 if (FAILED(rc))
3232 sessionMachine->uninit();
3233 }
3234
3235 if (SUCCEEDED(rc))
3236 {
3237 /*
3238 * tell the client watcher thread to update the set of
3239 * machines that have open sessions
3240 */
3241 mParent->i_updateClientWatcher();
3242
3243 if (oldState != SessionState_Locked)
3244 /* fire an event */
3245 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3246 }
3247
3248 return rc;
3249}
3250
3251/**
3252 * @note Locks objects!
3253 */
3254HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3255 const com::Utf8Str &aName,
3256 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3257 ComPtr<IProgress> &aProgress)
3258{
3259 Utf8Str strFrontend(aName);
3260 /* "emergencystop" doesn't need the session, so skip the checks/interface
3261 * retrieval. This code doesn't quite fit in here, but introducing a
3262 * special API method would be even more effort, and would require explicit
3263 * support by every API client. It's better to hide the feature a bit. */
3264 if (strFrontend != "emergencystop")
3265 CheckComArgNotNull(aSession);
3266
3267 HRESULT rc = S_OK;
3268 if (strFrontend.isEmpty())
3269 {
3270 Bstr bstrFrontend;
3271 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3272 if (FAILED(rc))
3273 return rc;
3274 strFrontend = bstrFrontend;
3275 if (strFrontend.isEmpty())
3276 {
3277 ComPtr<ISystemProperties> systemProperties;
3278 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3279 if (FAILED(rc))
3280 return rc;
3281 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3282 if (FAILED(rc))
3283 return rc;
3284 strFrontend = bstrFrontend;
3285 }
3286 /* paranoia - emergencystop is not a valid default */
3287 if (strFrontend == "emergencystop")
3288 strFrontend = Utf8Str::Empty;
3289 }
3290 /* default frontend: Qt GUI */
3291 if (strFrontend.isEmpty())
3292 strFrontend = "GUI/Qt";
3293
3294 if (strFrontend != "emergencystop")
3295 {
3296 /* check the session state */
3297 SessionState_T state;
3298 rc = aSession->COMGETTER(State)(&state);
3299 if (FAILED(rc))
3300 return rc;
3301
3302 if (state != SessionState_Unlocked)
3303 return setError(VBOX_E_INVALID_OBJECT_STATE,
3304 tr("The given session is busy"));
3305
3306 /* get the IInternalSessionControl interface */
3307 ComPtr<IInternalSessionControl> control(aSession);
3308 ComAssertMsgRet(!control.isNull(),
3309 ("No IInternalSessionControl interface"),
3310 E_INVALIDARG);
3311
3312 /* get the teleporter enable state for the progress object init. */
3313 BOOL fTeleporterEnabled;
3314 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3315 if (FAILED(rc))
3316 return rc;
3317
3318 /* create a progress object */
3319 ComObjPtr<ProgressProxy> progress;
3320 progress.createObject();
3321 rc = progress->init(mParent,
3322 static_cast<IMachine*>(this),
3323 Bstr(tr("Starting VM")).raw(),
3324 TRUE /* aCancelable */,
3325 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3326 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3327 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3328 2 /* uFirstOperationWeight */,
3329 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3330
3331 if (SUCCEEDED(rc))
3332 {
3333 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3334 if (SUCCEEDED(rc))
3335 {
3336 aProgress = progress;
3337
3338 /* signal the client watcher thread */
3339 mParent->i_updateClientWatcher();
3340
3341 /* fire an event */
3342 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3343 }
3344 }
3345 }
3346 else
3347 {
3348 /* no progress object - either instant success or failure */
3349 aProgress = NULL;
3350
3351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3352
3353 if (mData->mSession.mState != SessionState_Locked)
3354 return setError(VBOX_E_INVALID_OBJECT_STATE,
3355 tr("The machine '%s' is not locked by a session"),
3356 mUserData->s.strName.c_str());
3357
3358 /* must have a VM process associated - do not kill normal API clients
3359 * with an open session */
3360 if (!Global::IsOnline(mData->mMachineState))
3361 return setError(VBOX_E_INVALID_OBJECT_STATE,
3362 tr("The machine '%s' does not have a VM process"),
3363 mUserData->s.strName.c_str());
3364
3365 /* forcibly terminate the VM process */
3366 if (mData->mSession.mPID != NIL_RTPROCESS)
3367 RTProcTerminate(mData->mSession.mPID);
3368
3369 /* signal the client watcher thread, as most likely the client has
3370 * been terminated */
3371 mParent->i_updateClientWatcher();
3372 }
3373
3374 return rc;
3375}
3376
3377HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3378{
3379 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3380 return setError(E_INVALIDARG,
3381 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3382 aPosition, SchemaDefs::MaxBootPosition);
3383
3384 if (aDevice == DeviceType_USB)
3385 return setError(E_NOTIMPL,
3386 tr("Booting from USB device is currently not supported"));
3387
3388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3389
3390 HRESULT rc = i_checkStateDependency(MutableStateDep);
3391 if (FAILED(rc)) return rc;
3392
3393 i_setModified(IsModified_MachineData);
3394 mHWData.backup();
3395 mHWData->mBootOrder[aPosition - 1] = aDevice;
3396
3397 return S_OK;
3398}
3399
3400HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3401{
3402 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3403 return setError(E_INVALIDARG,
3404 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3405 aPosition, SchemaDefs::MaxBootPosition);
3406
3407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3408
3409 *aDevice = mHWData->mBootOrder[aPosition - 1];
3410
3411 return S_OK;
3412}
3413
3414HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3415 LONG aControllerPort,
3416 LONG aDevice,
3417 DeviceType_T aType,
3418 const ComPtr<IMedium> &aMedium)
3419{
3420 IMedium *aM = aMedium;
3421 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3422 aName.c_str(), aControllerPort, aDevice, aType, aM));
3423
3424 // request the host lock first, since might be calling Host methods for getting host drives;
3425 // next, protect the media tree all the while we're in here, as well as our member variables
3426 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3427 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3428
3429 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3430 if (FAILED(rc)) return rc;
3431
3432 /// @todo NEWMEDIA implicit machine registration
3433 if (!mData->mRegistered)
3434 return setError(VBOX_E_INVALID_OBJECT_STATE,
3435 tr("Cannot attach storage devices to an unregistered machine"));
3436
3437 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3438
3439 /* Check for an existing controller. */
3440 ComObjPtr<StorageController> ctl;
3441 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3442 if (FAILED(rc)) return rc;
3443
3444 StorageControllerType_T ctrlType;
3445 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3446 if (FAILED(rc))
3447 return setError(E_FAIL,
3448 tr("Could not get type of controller '%s'"),
3449 aName.c_str());
3450
3451 bool fSilent = false;
3452 Utf8Str strReconfig;
3453
3454 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3455 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3456 if ( mData->mMachineState == MachineState_Paused
3457 && strReconfig == "1")
3458 fSilent = true;
3459
3460 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3461 bool fHotplug = false;
3462 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3463 fHotplug = true;
3464
3465 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3466 return setError(VBOX_E_INVALID_VM_STATE,
3467 tr("Controller '%s' does not support hotplugging"),
3468 aName.c_str());
3469
3470 // check that the port and device are not out of range
3471 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3472 if (FAILED(rc)) return rc;
3473
3474 /* check if the device slot is already busy */
3475 MediumAttachment *pAttachTemp;
3476 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3477 aName,
3478 aControllerPort,
3479 aDevice)))
3480 {
3481 Medium *pMedium = pAttachTemp->i_getMedium();
3482 if (pMedium)
3483 {
3484 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3485 return setError(VBOX_E_OBJECT_IN_USE,
3486 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3487 pMedium->i_getLocationFull().c_str(),
3488 aControllerPort,
3489 aDevice,
3490 aName.c_str());
3491 }
3492 else
3493 return setError(VBOX_E_OBJECT_IN_USE,
3494 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3495 aControllerPort, aDevice, aName.c_str());
3496 }
3497
3498 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3499 if (aMedium && medium.isNull())
3500 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3501
3502 AutoCaller mediumCaller(medium);
3503 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3504
3505 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3506
3507 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3508 && !medium.isNull()
3509 )
3510 return setError(VBOX_E_OBJECT_IN_USE,
3511 tr("Medium '%s' is already attached to this virtual machine"),
3512 medium->i_getLocationFull().c_str());
3513
3514 if (!medium.isNull())
3515 {
3516 MediumType_T mtype = medium->i_getType();
3517 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3518 // For DVDs it's not written to the config file, so needs no global config
3519 // version bump. For floppies it's a new attribute "type", which is ignored
3520 // by older VirtualBox version, so needs no global config version bump either.
3521 // For hard disks this type is not accepted.
3522 if (mtype == MediumType_MultiAttach)
3523 {
3524 // This type is new with VirtualBox 4.0 and therefore requires settings
3525 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3526 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3527 // two reasons: The medium type is a property of the media registry tree, which
3528 // can reside in the global config file (for pre-4.0 media); we would therefore
3529 // possibly need to bump the global config version. We don't want to do that though
3530 // because that might make downgrading to pre-4.0 impossible.
3531 // As a result, we can only use these two new types if the medium is NOT in the
3532 // global registry:
3533 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3534 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3535 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3536 )
3537 return setError(VBOX_E_INVALID_OBJECT_STATE,
3538 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3539 "to machines that were created with VirtualBox 4.0 or later"),
3540 medium->i_getLocationFull().c_str());
3541 }
3542 }
3543
3544 bool fIndirect = false;
3545 if (!medium.isNull())
3546 fIndirect = medium->i_isReadOnly();
3547 bool associate = true;
3548
3549 do
3550 {
3551 if ( aType == DeviceType_HardDisk
3552 && mMediumAttachments.isBackedUp())
3553 {
3554 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3555
3556 /* check if the medium was attached to the VM before we started
3557 * changing attachments in which case the attachment just needs to
3558 * be restored */
3559 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3560 {
3561 AssertReturn(!fIndirect, E_FAIL);
3562
3563 /* see if it's the same bus/channel/device */
3564 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3565 {
3566 /* the simplest case: restore the whole attachment
3567 * and return, nothing else to do */
3568 mMediumAttachments->push_back(pAttachTemp);
3569
3570 /* Reattach the medium to the VM. */
3571 if (fHotplug || fSilent)
3572 {
3573 mediumLock.release();
3574 treeLock.release();
3575 alock.release();
3576
3577 MediumLockList *pMediumLockList(new MediumLockList());
3578
3579 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3580 medium /* pToLockWrite */,
3581 false /* fMediumLockWriteAll */,
3582 NULL,
3583 *pMediumLockList);
3584 alock.acquire();
3585 if (FAILED(rc))
3586 delete pMediumLockList;
3587 else
3588 {
3589 mData->mSession.mLockedMedia.Unlock();
3590 alock.release();
3591 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3592 mData->mSession.mLockedMedia.Lock();
3593 alock.acquire();
3594 }
3595 alock.release();
3596
3597 if (SUCCEEDED(rc))
3598 {
3599 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3600 /* Remove lock list in case of error. */
3601 if (FAILED(rc))
3602 {
3603 mData->mSession.mLockedMedia.Unlock();
3604 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3605 mData->mSession.mLockedMedia.Lock();
3606 }
3607 }
3608 }
3609
3610 return S_OK;
3611 }
3612
3613 /* bus/channel/device differ; we need a new attachment object,
3614 * but don't try to associate it again */
3615 associate = false;
3616 break;
3617 }
3618 }
3619
3620 /* go further only if the attachment is to be indirect */
3621 if (!fIndirect)
3622 break;
3623
3624 /* perform the so called smart attachment logic for indirect
3625 * attachments. Note that smart attachment is only applicable to base
3626 * hard disks. */
3627
3628 if (medium->i_getParent().isNull())
3629 {
3630 /* first, investigate the backup copy of the current hard disk
3631 * attachments to make it possible to re-attach existing diffs to
3632 * another device slot w/o losing their contents */
3633 if (mMediumAttachments.isBackedUp())
3634 {
3635 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3636
3637 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3638 uint32_t foundLevel = 0;
3639
3640 for (MediumAttachmentList::const_iterator
3641 it = oldAtts.begin();
3642 it != oldAtts.end();
3643 ++it)
3644 {
3645 uint32_t level = 0;
3646 MediumAttachment *pAttach = *it;
3647 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3648 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3649 if (pMedium.isNull())
3650 continue;
3651
3652 if (pMedium->i_getBase(&level) == medium)
3653 {
3654 /* skip the hard disk if its currently attached (we
3655 * cannot attach the same hard disk twice) */
3656 if (i_findAttachment(*mMediumAttachments.data(),
3657 pMedium))
3658 continue;
3659
3660 /* matched device, channel and bus (i.e. attached to the
3661 * same place) will win and immediately stop the search;
3662 * otherwise the attachment that has the youngest
3663 * descendant of medium will be used
3664 */
3665 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3666 {
3667 /* the simplest case: restore the whole attachment
3668 * and return, nothing else to do */
3669 mMediumAttachments->push_back(*it);
3670
3671 /* Reattach the medium to the VM. */
3672 if (fHotplug || fSilent)
3673 {
3674 mediumLock.release();
3675 treeLock.release();
3676 alock.release();
3677
3678 MediumLockList *pMediumLockList(new MediumLockList());
3679
3680 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3681 medium /* pToLockWrite */,
3682 false /* fMediumLockWriteAll */,
3683 NULL,
3684 *pMediumLockList);
3685 alock.acquire();
3686 if (FAILED(rc))
3687 delete pMediumLockList;
3688 else
3689 {
3690 mData->mSession.mLockedMedia.Unlock();
3691 alock.release();
3692 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3693 mData->mSession.mLockedMedia.Lock();
3694 alock.acquire();
3695 }
3696 alock.release();
3697
3698 if (SUCCEEDED(rc))
3699 {
3700 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3701 /* Remove lock list in case of error. */
3702 if (FAILED(rc))
3703 {
3704 mData->mSession.mLockedMedia.Unlock();
3705 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3706 mData->mSession.mLockedMedia.Lock();
3707 }
3708 }
3709 }
3710
3711 return S_OK;
3712 }
3713 else if ( foundIt == oldAtts.end()
3714 || level > foundLevel /* prefer younger */
3715 )
3716 {
3717 foundIt = it;
3718 foundLevel = level;
3719 }
3720 }
3721 }
3722
3723 if (foundIt != oldAtts.end())
3724 {
3725 /* use the previously attached hard disk */
3726 medium = (*foundIt)->i_getMedium();
3727 mediumCaller.attach(medium);
3728 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3729 mediumLock.attach(medium);
3730 /* not implicit, doesn't require association with this VM */
3731 fIndirect = false;
3732 associate = false;
3733 /* go right to the MediumAttachment creation */
3734 break;
3735 }
3736 }
3737
3738 /* must give up the medium lock and medium tree lock as below we
3739 * go over snapshots, which needs a lock with higher lock order. */
3740 mediumLock.release();
3741 treeLock.release();
3742
3743 /* then, search through snapshots for the best diff in the given
3744 * hard disk's chain to base the new diff on */
3745
3746 ComObjPtr<Medium> base;
3747 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3748 while (snap)
3749 {
3750 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3751
3752 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3753
3754 MediumAttachment *pAttachFound = NULL;
3755 uint32_t foundLevel = 0;
3756
3757 for (MediumAttachmentList::const_iterator
3758 it = snapAtts.begin();
3759 it != snapAtts.end();
3760 ++it)
3761 {
3762 MediumAttachment *pAttach = *it;
3763 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3764 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3765 if (pMedium.isNull())
3766 continue;
3767
3768 uint32_t level = 0;
3769 if (pMedium->i_getBase(&level) == medium)
3770 {
3771 /* matched device, channel and bus (i.e. attached to the
3772 * same place) will win and immediately stop the search;
3773 * otherwise the attachment that has the youngest
3774 * descendant of medium will be used
3775 */
3776 if ( pAttach->i_getDevice() == aDevice
3777 && pAttach->i_getPort() == aControllerPort
3778 && pAttach->i_getControllerName() == aName
3779 )
3780 {
3781 pAttachFound = pAttach;
3782 break;
3783 }
3784 else if ( !pAttachFound
3785 || level > foundLevel /* prefer younger */
3786 )
3787 {
3788 pAttachFound = pAttach;
3789 foundLevel = level;
3790 }
3791 }
3792 }
3793
3794 if (pAttachFound)
3795 {
3796 base = pAttachFound->i_getMedium();
3797 break;
3798 }
3799
3800 snap = snap->i_getParent();
3801 }
3802
3803 /* re-lock medium tree and the medium, as we need it below */
3804 treeLock.acquire();
3805 mediumLock.acquire();
3806
3807 /* found a suitable diff, use it as a base */
3808 if (!base.isNull())
3809 {
3810 medium = base;
3811 mediumCaller.attach(medium);
3812 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3813 mediumLock.attach(medium);
3814 }
3815 }
3816
3817 Utf8Str strFullSnapshotFolder;
3818 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3819
3820 ComObjPtr<Medium> diff;
3821 diff.createObject();
3822 // store this diff in the same registry as the parent
3823 Guid uuidRegistryParent;
3824 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3825 {
3826 // parent image has no registry: this can happen if we're attaching a new immutable
3827 // image that has not yet been attached (medium then points to the base and we're
3828 // creating the diff image for the immutable, and the parent is not yet registered);
3829 // put the parent in the machine registry then
3830 mediumLock.release();
3831 treeLock.release();
3832 alock.release();
3833 i_addMediumToRegistry(medium);
3834 alock.acquire();
3835 treeLock.acquire();
3836 mediumLock.acquire();
3837 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3838 }
3839 rc = diff->init(mParent,
3840 medium->i_getPreferredDiffFormat(),
3841 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3842 uuidRegistryParent,
3843 DeviceType_HardDisk);
3844 if (FAILED(rc)) return rc;
3845
3846 /* Apply the normal locking logic to the entire chain. */
3847 MediumLockList *pMediumLockList(new MediumLockList());
3848 mediumLock.release();
3849 treeLock.release();
3850 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3851 diff /* pToLockWrite */,
3852 false /* fMediumLockWriteAll */,
3853 medium,
3854 *pMediumLockList);
3855 treeLock.acquire();
3856 mediumLock.acquire();
3857 if (SUCCEEDED(rc))
3858 {
3859 mediumLock.release();
3860 treeLock.release();
3861 rc = pMediumLockList->Lock();
3862 treeLock.acquire();
3863 mediumLock.acquire();
3864 if (FAILED(rc))
3865 setError(rc,
3866 tr("Could not lock medium when creating diff '%s'"),
3867 diff->i_getLocationFull().c_str());
3868 else
3869 {
3870 /* will release the lock before the potentially lengthy
3871 * operation, so protect with the special state */
3872 MachineState_T oldState = mData->mMachineState;
3873 i_setMachineState(MachineState_SettingUp);
3874
3875 mediumLock.release();
3876 treeLock.release();
3877 alock.release();
3878
3879 rc = medium->i_createDiffStorage(diff,
3880 medium->i_getPreferredDiffVariant(),
3881 pMediumLockList,
3882 NULL /* aProgress */,
3883 true /* aWait */,
3884 false /* aNotify */);
3885
3886 alock.acquire();
3887 treeLock.acquire();
3888 mediumLock.acquire();
3889
3890 i_setMachineState(oldState);
3891 }
3892 }
3893
3894 /* Unlock the media and free the associated memory. */
3895 delete pMediumLockList;
3896
3897 if (FAILED(rc)) return rc;
3898
3899 /* use the created diff for the actual attachment */
3900 medium = diff;
3901 mediumCaller.attach(medium);
3902 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3903 mediumLock.attach(medium);
3904 }
3905 while (0);
3906
3907 ComObjPtr<MediumAttachment> attachment;
3908 attachment.createObject();
3909 rc = attachment->init(this,
3910 medium,
3911 aName,
3912 aControllerPort,
3913 aDevice,
3914 aType,
3915 fIndirect,
3916 false /* fPassthrough */,
3917 false /* fTempEject */,
3918 false /* fNonRotational */,
3919 false /* fDiscard */,
3920 fHotplug /* fHotPluggable */,
3921 Utf8Str::Empty);
3922 if (FAILED(rc)) return rc;
3923
3924 if (associate && !medium.isNull())
3925 {
3926 // as the last step, associate the medium to the VM
3927 rc = medium->i_addBackReference(mData->mUuid);
3928 // here we can fail because of Deleting, or being in process of creating a Diff
3929 if (FAILED(rc)) return rc;
3930
3931 mediumLock.release();
3932 treeLock.release();
3933 alock.release();
3934 i_addMediumToRegistry(medium);
3935 alock.acquire();
3936 treeLock.acquire();
3937 mediumLock.acquire();
3938 }
3939
3940 /* success: finally remember the attachment */
3941 i_setModified(IsModified_Storage);
3942 mMediumAttachments.backup();
3943 mMediumAttachments->push_back(attachment);
3944
3945 mediumLock.release();
3946 treeLock.release();
3947 alock.release();
3948
3949 if (fHotplug || fSilent)
3950 {
3951 if (!medium.isNull())
3952 {
3953 MediumLockList *pMediumLockList(new MediumLockList());
3954
3955 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3956 medium /* pToLockWrite */,
3957 false /* fMediumLockWriteAll */,
3958 NULL,
3959 *pMediumLockList);
3960 alock.acquire();
3961 if (FAILED(rc))
3962 delete pMediumLockList;
3963 else
3964 {
3965 mData->mSession.mLockedMedia.Unlock();
3966 alock.release();
3967 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3968 mData->mSession.mLockedMedia.Lock();
3969 alock.acquire();
3970 }
3971 alock.release();
3972 }
3973
3974 if (SUCCEEDED(rc))
3975 {
3976 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3977 /* Remove lock list in case of error. */
3978 if (FAILED(rc))
3979 {
3980 mData->mSession.mLockedMedia.Unlock();
3981 mData->mSession.mLockedMedia.Remove(attachment);
3982 mData->mSession.mLockedMedia.Lock();
3983 }
3984 }
3985 }
3986
3987 /* Save modified registries, but skip this machine as it's the caller's
3988 * job to save its settings like all other settings changes. */
3989 mParent->i_unmarkRegistryModified(i_getId());
3990 mParent->i_saveModifiedRegistries();
3991
3992 if (SUCCEEDED(rc))
3993 {
3994 if (fIndirect && medium != aM)
3995 mParent->i_onMediumConfigChanged(medium);
3996 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3997 }
3998
3999 return rc;
4000}
4001
4002HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4003 LONG aDevice)
4004{
4005 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4006 aName.c_str(), aControllerPort, aDevice));
4007
4008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4009
4010 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4011 if (FAILED(rc)) return rc;
4012
4013 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4014
4015 /* Check for an existing controller. */
4016 ComObjPtr<StorageController> ctl;
4017 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4018 if (FAILED(rc)) return rc;
4019
4020 StorageControllerType_T ctrlType;
4021 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4022 if (FAILED(rc))
4023 return setError(E_FAIL,
4024 tr("Could not get type of controller '%s'"),
4025 aName.c_str());
4026
4027 bool fSilent = false;
4028 Utf8Str strReconfig;
4029
4030 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4031 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4032 if ( mData->mMachineState == MachineState_Paused
4033 && strReconfig == "1")
4034 fSilent = true;
4035
4036 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4037 bool fHotplug = false;
4038 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4039 fHotplug = true;
4040
4041 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4042 return setError(VBOX_E_INVALID_VM_STATE,
4043 tr("Controller '%s' does not support hotplugging"),
4044 aName.c_str());
4045
4046 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4047 aName,
4048 aControllerPort,
4049 aDevice);
4050 if (!pAttach)
4051 return setError(VBOX_E_OBJECT_NOT_FOUND,
4052 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4053 aDevice, aControllerPort, aName.c_str());
4054
4055 if (fHotplug && !pAttach->i_getHotPluggable())
4056 return setError(VBOX_E_NOT_SUPPORTED,
4057 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4058 aDevice, aControllerPort, aName.c_str());
4059
4060 /*
4061 * The VM has to detach the device before we delete any implicit diffs.
4062 * If this fails we can roll back without loosing data.
4063 */
4064 if (fHotplug || fSilent)
4065 {
4066 alock.release();
4067 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4068 alock.acquire();
4069 }
4070 if (FAILED(rc)) return rc;
4071
4072 /* If we are here everything went well and we can delete the implicit now. */
4073 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4074
4075 alock.release();
4076
4077 /* Save modified registries, but skip this machine as it's the caller's
4078 * job to save its settings like all other settings changes. */
4079 mParent->i_unmarkRegistryModified(i_getId());
4080 mParent->i_saveModifiedRegistries();
4081
4082 if (SUCCEEDED(rc))
4083 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4084
4085 return rc;
4086}
4087
4088HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4089 LONG aDevice, BOOL aPassthrough)
4090{
4091 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4092 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4093
4094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4095
4096 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4097 if (FAILED(rc)) return rc;
4098
4099 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4100
4101 /* Check for an existing controller. */
4102 ComObjPtr<StorageController> ctl;
4103 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4104 if (FAILED(rc)) return rc;
4105
4106 StorageControllerType_T ctrlType;
4107 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4108 if (FAILED(rc))
4109 return setError(E_FAIL,
4110 tr("Could not get type of controller '%s'"),
4111 aName.c_str());
4112
4113 bool fSilent = false;
4114 Utf8Str strReconfig;
4115
4116 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4117 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4118 if ( mData->mMachineState == MachineState_Paused
4119 && strReconfig == "1")
4120 fSilent = true;
4121
4122 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4123 bool fHotplug = false;
4124 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4125 fHotplug = true;
4126
4127 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4128 return setError(VBOX_E_INVALID_VM_STATE,
4129 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4130 aName.c_str());
4131
4132 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4133 aName,
4134 aControllerPort,
4135 aDevice);
4136 if (!pAttach)
4137 return setError(VBOX_E_OBJECT_NOT_FOUND,
4138 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4139 aDevice, aControllerPort, aName.c_str());
4140
4141
4142 i_setModified(IsModified_Storage);
4143 mMediumAttachments.backup();
4144
4145 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4146
4147 if (pAttach->i_getType() != DeviceType_DVD)
4148 return setError(E_INVALIDARG,
4149 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4150 aDevice, aControllerPort, aName.c_str());
4151
4152 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4153
4154 pAttach->i_updatePassthrough(!!aPassthrough);
4155
4156 attLock.release();
4157 alock.release();
4158 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4159 if (SUCCEEDED(rc) && fValueChanged)
4160 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4161
4162 return rc;
4163}
4164
4165HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4166 LONG aDevice, BOOL aTemporaryEject)
4167{
4168
4169 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4170 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4171
4172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4173
4174 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4175 if (FAILED(rc)) return rc;
4176
4177 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4178 aName,
4179 aControllerPort,
4180 aDevice);
4181 if (!pAttach)
4182 return setError(VBOX_E_OBJECT_NOT_FOUND,
4183 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4184 aDevice, aControllerPort, aName.c_str());
4185
4186
4187 i_setModified(IsModified_Storage);
4188 mMediumAttachments.backup();
4189
4190 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4191
4192 if (pAttach->i_getType() != DeviceType_DVD)
4193 return setError(E_INVALIDARG,
4194 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4195 aDevice, aControllerPort, aName.c_str());
4196 pAttach->i_updateTempEject(!!aTemporaryEject);
4197
4198 return S_OK;
4199}
4200
4201HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4202 LONG aDevice, BOOL aNonRotational)
4203{
4204
4205 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4206 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4207
4208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4209
4210 HRESULT rc = i_checkStateDependency(MutableStateDep);
4211 if (FAILED(rc)) return rc;
4212
4213 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4214
4215 if (Global::IsOnlineOrTransient(mData->mMachineState))
4216 return setError(VBOX_E_INVALID_VM_STATE,
4217 tr("Invalid machine state: %s"),
4218 Global::stringifyMachineState(mData->mMachineState));
4219
4220 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4221 aName,
4222 aControllerPort,
4223 aDevice);
4224 if (!pAttach)
4225 return setError(VBOX_E_OBJECT_NOT_FOUND,
4226 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4227 aDevice, aControllerPort, aName.c_str());
4228
4229
4230 i_setModified(IsModified_Storage);
4231 mMediumAttachments.backup();
4232
4233 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4234
4235 if (pAttach->i_getType() != DeviceType_HardDisk)
4236 return setError(E_INVALIDARG,
4237 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"),
4238 aDevice, aControllerPort, aName.c_str());
4239 pAttach->i_updateNonRotational(!!aNonRotational);
4240
4241 return S_OK;
4242}
4243
4244HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4245 LONG aDevice, BOOL aDiscard)
4246{
4247
4248 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4249 aName.c_str(), aControllerPort, aDevice, aDiscard));
4250
4251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4252
4253 HRESULT rc = i_checkStateDependency(MutableStateDep);
4254 if (FAILED(rc)) return rc;
4255
4256 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4257
4258 if (Global::IsOnlineOrTransient(mData->mMachineState))
4259 return setError(VBOX_E_INVALID_VM_STATE,
4260 tr("Invalid machine state: %s"),
4261 Global::stringifyMachineState(mData->mMachineState));
4262
4263 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4264 aName,
4265 aControllerPort,
4266 aDevice);
4267 if (!pAttach)
4268 return setError(VBOX_E_OBJECT_NOT_FOUND,
4269 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4270 aDevice, aControllerPort, aName.c_str());
4271
4272
4273 i_setModified(IsModified_Storage);
4274 mMediumAttachments.backup();
4275
4276 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4277
4278 if (pAttach->i_getType() != DeviceType_HardDisk)
4279 return setError(E_INVALIDARG,
4280 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"),
4281 aDevice, aControllerPort, aName.c_str());
4282 pAttach->i_updateDiscard(!!aDiscard);
4283
4284 return S_OK;
4285}
4286
4287HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4288 LONG aDevice, BOOL aHotPluggable)
4289{
4290 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4291 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4292
4293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4294
4295 HRESULT rc = i_checkStateDependency(MutableStateDep);
4296 if (FAILED(rc)) return rc;
4297
4298 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4299
4300 if (Global::IsOnlineOrTransient(mData->mMachineState))
4301 return setError(VBOX_E_INVALID_VM_STATE,
4302 tr("Invalid machine state: %s"),
4303 Global::stringifyMachineState(mData->mMachineState));
4304
4305 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4306 aName,
4307 aControllerPort,
4308 aDevice);
4309 if (!pAttach)
4310 return setError(VBOX_E_OBJECT_NOT_FOUND,
4311 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4312 aDevice, aControllerPort, aName.c_str());
4313
4314 /* Check for an existing controller. */
4315 ComObjPtr<StorageController> ctl;
4316 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4317 if (FAILED(rc)) return rc;
4318
4319 StorageControllerType_T ctrlType;
4320 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4321 if (FAILED(rc))
4322 return setError(E_FAIL,
4323 tr("Could not get type of controller '%s'"),
4324 aName.c_str());
4325
4326 if (!i_isControllerHotplugCapable(ctrlType))
4327 return setError(VBOX_E_NOT_SUPPORTED,
4328 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4329 aName.c_str());
4330
4331 i_setModified(IsModified_Storage);
4332 mMediumAttachments.backup();
4333
4334 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4335
4336 if (pAttach->i_getType() == DeviceType_Floppy)
4337 return setError(E_INVALIDARG,
4338 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"),
4339 aDevice, aControllerPort, aName.c_str());
4340 pAttach->i_updateHotPluggable(!!aHotPluggable);
4341
4342 return S_OK;
4343}
4344
4345HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4346 LONG aDevice)
4347{
4348 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4349 aName.c_str(), aControllerPort, aDevice));
4350
4351 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4352}
4353
4354HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4355 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4356{
4357 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4358 aName.c_str(), aControllerPort, aDevice));
4359
4360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4361
4362 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4363 if (FAILED(rc)) return rc;
4364
4365 if (Global::IsOnlineOrTransient(mData->mMachineState))
4366 return setError(VBOX_E_INVALID_VM_STATE,
4367 tr("Invalid machine state: %s"),
4368 Global::stringifyMachineState(mData->mMachineState));
4369
4370 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4371 aName,
4372 aControllerPort,
4373 aDevice);
4374 if (!pAttach)
4375 return setError(VBOX_E_OBJECT_NOT_FOUND,
4376 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4377 aDevice, aControllerPort, aName.c_str());
4378
4379
4380 i_setModified(IsModified_Storage);
4381 mMediumAttachments.backup();
4382
4383 IBandwidthGroup *iB = aBandwidthGroup;
4384 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4385 if (aBandwidthGroup && group.isNull())
4386 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4387
4388 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4389
4390 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4391 if (strBandwidthGroupOld.isNotEmpty())
4392 {
4393 /* Get the bandwidth group object and release it - this must not fail. */
4394 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4395 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4396 Assert(SUCCEEDED(rc));
4397
4398 pBandwidthGroupOld->i_release();
4399 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4400 }
4401
4402 if (!group.isNull())
4403 {
4404 group->i_reference();
4405 pAttach->i_updateBandwidthGroup(group->i_getName());
4406 }
4407
4408 return S_OK;
4409}
4410
4411HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4412 LONG aControllerPort,
4413 LONG aDevice,
4414 DeviceType_T aType)
4415{
4416 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4417 aName.c_str(), aControllerPort, aDevice, aType));
4418
4419 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4420}
4421
4422
4423HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4424 LONG aControllerPort,
4425 LONG aDevice,
4426 BOOL aForce)
4427{
4428 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4429 aName.c_str(), aControllerPort, aForce));
4430
4431 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4432}
4433
4434HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4435 LONG aControllerPort,
4436 LONG aDevice,
4437 const ComPtr<IMedium> &aMedium,
4438 BOOL aForce)
4439{
4440 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4441 aName.c_str(), aControllerPort, aDevice, aForce));
4442
4443 // request the host lock first, since might be calling Host methods for getting host drives;
4444 // next, protect the media tree all the while we're in here, as well as our member variables
4445 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4446 this->lockHandle(),
4447 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4448
4449 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4450 aName,
4451 aControllerPort,
4452 aDevice);
4453 if (pAttach.isNull())
4454 return setError(VBOX_E_OBJECT_NOT_FOUND,
4455 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4456 aDevice, aControllerPort, aName.c_str());
4457
4458 /* Remember previously mounted medium. The medium before taking the
4459 * backup is not necessarily the same thing. */
4460 ComObjPtr<Medium> oldmedium;
4461 oldmedium = pAttach->i_getMedium();
4462
4463 IMedium *iM = aMedium;
4464 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4465 if (aMedium && pMedium.isNull())
4466 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4467
4468 AutoCaller mediumCaller(pMedium);
4469 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4470
4471 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4472 if (pMedium)
4473 {
4474 DeviceType_T mediumType = pAttach->i_getType();
4475 switch (mediumType)
4476 {
4477 case DeviceType_DVD:
4478 case DeviceType_Floppy:
4479 break;
4480
4481 default:
4482 return setError(VBOX_E_INVALID_OBJECT_STATE,
4483 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4484 aControllerPort,
4485 aDevice,
4486 aName.c_str());
4487 }
4488 }
4489
4490 i_setModified(IsModified_Storage);
4491 mMediumAttachments.backup();
4492
4493 {
4494 // The backup operation makes the pAttach reference point to the
4495 // old settings. Re-get the correct reference.
4496 pAttach = i_findAttachment(*mMediumAttachments.data(),
4497 aName,
4498 aControllerPort,
4499 aDevice);
4500 if (!oldmedium.isNull())
4501 oldmedium->i_removeBackReference(mData->mUuid);
4502 if (!pMedium.isNull())
4503 {
4504 pMedium->i_addBackReference(mData->mUuid);
4505
4506 mediumLock.release();
4507 multiLock.release();
4508 i_addMediumToRegistry(pMedium);
4509 multiLock.acquire();
4510 mediumLock.acquire();
4511 }
4512
4513 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4514 pAttach->i_updateMedium(pMedium);
4515 }
4516
4517 i_setModified(IsModified_Storage);
4518
4519 mediumLock.release();
4520 multiLock.release();
4521 HRESULT rc = i_onMediumChange(pAttach, aForce);
4522 multiLock.acquire();
4523 mediumLock.acquire();
4524
4525 /* On error roll back this change only. */
4526 if (FAILED(rc))
4527 {
4528 if (!pMedium.isNull())
4529 pMedium->i_removeBackReference(mData->mUuid);
4530 pAttach = i_findAttachment(*mMediumAttachments.data(),
4531 aName,
4532 aControllerPort,
4533 aDevice);
4534 /* If the attachment is gone in the meantime, bail out. */
4535 if (pAttach.isNull())
4536 return rc;
4537 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4538 if (!oldmedium.isNull())
4539 oldmedium->i_addBackReference(mData->mUuid);
4540 pAttach->i_updateMedium(oldmedium);
4541 }
4542
4543 mediumLock.release();
4544 multiLock.release();
4545
4546 /* Save modified registries, but skip this machine as it's the caller's
4547 * job to save its settings like all other settings changes. */
4548 mParent->i_unmarkRegistryModified(i_getId());
4549 mParent->i_saveModifiedRegistries();
4550
4551 return rc;
4552}
4553HRESULT Machine::getMedium(const com::Utf8Str &aName,
4554 LONG aControllerPort,
4555 LONG aDevice,
4556 ComPtr<IMedium> &aMedium)
4557{
4558 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4559 aName.c_str(), aControllerPort, aDevice));
4560
4561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4562
4563 aMedium = NULL;
4564
4565 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4566 aName,
4567 aControllerPort,
4568 aDevice);
4569 if (pAttach.isNull())
4570 return setError(VBOX_E_OBJECT_NOT_FOUND,
4571 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4572 aDevice, aControllerPort, aName.c_str());
4573
4574 aMedium = pAttach->i_getMedium();
4575
4576 return S_OK;
4577}
4578
4579HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4580{
4581
4582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4583
4584 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4585
4586 return S_OK;
4587}
4588
4589HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4590{
4591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4592
4593 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4594
4595 return S_OK;
4596}
4597
4598HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4599{
4600 /* Do not assert if slot is out of range, just return the advertised
4601 status. testdriver/vbox.py triggers this in logVmInfo. */
4602 if (aSlot >= mNetworkAdapters.size())
4603 return setError(E_INVALIDARG,
4604 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4605 aSlot, mNetworkAdapters.size());
4606
4607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4608
4609 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4610
4611 return S_OK;
4612}
4613
4614HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4615{
4616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4617
4618 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4619 size_t i = 0;
4620 for (settings::StringsMap::const_iterator
4621 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4622 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4623 ++it, ++i)
4624 aKeys[i] = it->first;
4625
4626 return S_OK;
4627}
4628
4629 /**
4630 * @note Locks this object for reading.
4631 */
4632HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4633 com::Utf8Str &aValue)
4634{
4635 /* start with nothing found */
4636 aValue = "";
4637
4638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4639
4640 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4641 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4642 // found:
4643 aValue = it->second; // source is a Utf8Str
4644
4645 /* return the result to caller (may be empty) */
4646 return S_OK;
4647}
4648
4649 /**
4650 * @note Locks mParent for writing + this object for writing.
4651 */
4652HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4653{
4654 /* Because control characters in aKey have caused problems in the settings
4655 * they are rejected unless the key should be deleted. */
4656 if (!aValue.isEmpty())
4657 {
4658 for (size_t i = 0; i < aKey.length(); ++i)
4659 {
4660 char ch = aKey[i];
4661 if (RTLocCIsCntrl(ch))
4662 return E_INVALIDARG;
4663 }
4664 }
4665
4666 Utf8Str strOldValue; // empty
4667
4668 // locking note: we only hold the read lock briefly to look up the old value,
4669 // then release it and call the onExtraCanChange callbacks. There is a small
4670 // chance of a race insofar as the callback might be called twice if two callers
4671 // change the same key at the same time, but that's a much better solution
4672 // than the deadlock we had here before. The actual changing of the extradata
4673 // is then performed under the write lock and race-free.
4674
4675 // look up the old value first; if nothing has changed then we need not do anything
4676 {
4677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4678
4679 // For snapshots don't even think about allowing changes, extradata
4680 // is global for a machine, so there is nothing snapshot specific.
4681 if (i_isSnapshotMachine())
4682 return setError(VBOX_E_INVALID_VM_STATE,
4683 tr("Cannot set extradata for a snapshot"));
4684
4685 // check if the right IMachine instance is used
4686 if (mData->mRegistered && !i_isSessionMachine())
4687 return setError(VBOX_E_INVALID_VM_STATE,
4688 tr("Cannot set extradata for an immutable machine"));
4689
4690 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4691 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4692 strOldValue = it->second;
4693 }
4694
4695 bool fChanged;
4696 if ((fChanged = (strOldValue != aValue)))
4697 {
4698 // ask for permission from all listeners outside the locks;
4699 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4700 // lock to copy the list of callbacks to invoke
4701 Bstr error;
4702 Bstr bstrValue(aValue);
4703
4704 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4705 {
4706 const char *sep = error.isEmpty() ? "" : ": ";
4707 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4708 return setError(E_ACCESSDENIED,
4709 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4710 aKey.c_str(),
4711 aValue.c_str(),
4712 sep,
4713 error.raw());
4714 }
4715
4716 // data is changing and change not vetoed: then write it out under the lock
4717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4718
4719 if (aValue.isEmpty())
4720 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4721 else
4722 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4723 // creates a new key if needed
4724
4725 bool fNeedsGlobalSaveSettings = false;
4726 // This saving of settings is tricky: there is no "old state" for the
4727 // extradata items at all (unlike all other settings), so the old/new
4728 // settings comparison would give a wrong result!
4729 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4730
4731 if (fNeedsGlobalSaveSettings)
4732 {
4733 // save the global settings; for that we should hold only the VirtualBox lock
4734 alock.release();
4735 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4736 mParent->i_saveSettings();
4737 }
4738 }
4739
4740 // fire notification outside the lock
4741 if (fChanged)
4742 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4743
4744 return S_OK;
4745}
4746
4747HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4748{
4749 aProgress = NULL;
4750 NOREF(aSettingsFilePath);
4751 ReturnComNotImplemented();
4752}
4753
4754HRESULT Machine::saveSettings()
4755{
4756 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4757
4758 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4759 if (FAILED(rc)) return rc;
4760
4761 /* the settings file path may never be null */
4762 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4763
4764 /* save all VM data excluding snapshots */
4765 bool fNeedsGlobalSaveSettings = false;
4766 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4767 mlock.release();
4768
4769 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4770 {
4771 // save the global settings; for that we should hold only the VirtualBox lock
4772 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4773 rc = mParent->i_saveSettings();
4774 }
4775
4776 return rc;
4777}
4778
4779
4780HRESULT Machine::discardSettings()
4781{
4782 /*
4783 * We need to take the machine list lock here as well as the machine one
4784 * or we'll get into trouble should any media stuff require rolling back.
4785 *
4786 * Details:
4787 *
4788 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4789 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4790 * 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]
4791 * 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
4792 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4793 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4794 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4795 * 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
4796 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4797 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4798 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4799 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4800 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4801 * 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]
4802 * 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] (*)
4803 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4804 * 0:005> k
4805 * # Child-SP RetAddr Call Site
4806 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4807 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4808 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4809 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4810 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4811 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4812 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4813 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4814 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4815 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4816 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4817 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4818 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4819 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4820 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4821 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4822 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4823 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4824 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4825 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4826 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4827 *
4828 */
4829 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4831
4832 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4833 if (FAILED(rc)) return rc;
4834
4835 /*
4836 * during this rollback, the session will be notified if data has
4837 * been actually changed
4838 */
4839 i_rollback(true /* aNotify */);
4840
4841 return S_OK;
4842}
4843
4844/** @note Locks objects! */
4845HRESULT Machine::unregister(AutoCaller &autoCaller,
4846 CleanupMode_T aCleanupMode,
4847 std::vector<ComPtr<IMedium> > &aMedia)
4848{
4849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4850
4851 Guid id(i_getId());
4852
4853 if (mData->mSession.mState != SessionState_Unlocked)
4854 return setError(VBOX_E_INVALID_OBJECT_STATE,
4855 tr("Cannot unregister the machine '%s' while it is locked"),
4856 mUserData->s.strName.c_str());
4857
4858 // wait for state dependents to drop to zero
4859 i_ensureNoStateDependencies();
4860
4861 if (!mData->mAccessible)
4862 {
4863 // inaccessible machines can only be unregistered; uninitialize ourselves
4864 // here because currently there may be no unregistered that are inaccessible
4865 // (this state combination is not supported). Note releasing the caller and
4866 // leaving the lock before calling uninit()
4867 alock.release();
4868 autoCaller.release();
4869
4870 uninit();
4871
4872 mParent->i_unregisterMachine(this, id);
4873 // calls VirtualBox::i_saveSettings()
4874
4875 return S_OK;
4876 }
4877
4878 HRESULT rc = S_OK;
4879 mData->llFilesToDelete.clear();
4880
4881 if (!mSSData->strStateFilePath.isEmpty())
4882 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4883
4884 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4885 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4886 mData->llFilesToDelete.push_back(strNVRAMFile);
4887
4888 // This list collects the medium objects from all medium attachments
4889 // which we will detach from the machine and its snapshots, in a specific
4890 // order which allows for closing all media without getting "media in use"
4891 // errors, simply by going through the list from the front to the back:
4892 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4893 // and must be closed before the parent media from the snapshots, or closing the parents
4894 // will fail because they still have children);
4895 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4896 // the root ("first") snapshot of the machine.
4897 MediaList llMedia;
4898
4899 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4900 && mMediumAttachments->size()
4901 )
4902 {
4903 // we have media attachments: detach them all and add the Medium objects to our list
4904 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4905 }
4906
4907 if (mData->mFirstSnapshot)
4908 {
4909 // add the media from the medium attachments of the snapshots to llMedia
4910 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4911 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4912 // into the children first
4913
4914 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4915 MachineState_T oldState = mData->mMachineState;
4916 mData->mMachineState = MachineState_DeletingSnapshot;
4917
4918 // make a copy of the first snapshot reference so the refcount does not
4919 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4920 // (would hang due to the AutoCaller voodoo)
4921 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4922
4923 // GO!
4924 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4925
4926 mData->mMachineState = oldState;
4927 }
4928
4929 if (FAILED(rc))
4930 {
4931 i_rollbackMedia();
4932 return rc;
4933 }
4934
4935 // commit all the media changes made above
4936 i_commitMedia();
4937
4938 mData->mRegistered = false;
4939
4940 // machine lock no longer needed
4941 alock.release();
4942
4943 /* Make sure that the settings of the current VM are not saved, because
4944 * they are rather crippled at this point to meet the cleanup expectations
4945 * and there's no point destroying the VM config on disk just because. */
4946 mParent->i_unmarkRegistryModified(id);
4947
4948 // return media to caller
4949 aMedia.resize(llMedia.size());
4950 size_t i = 0;
4951 for (MediaList::const_iterator
4952 it = llMedia.begin();
4953 it != llMedia.end();
4954 ++it, ++i)
4955 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4956
4957 mParent->i_unregisterMachine(this, id);
4958 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4959
4960 return S_OK;
4961}
4962
4963/**
4964 * Task record for deleting a machine config.
4965 */
4966class Machine::DeleteConfigTask
4967 : public Machine::Task
4968{
4969public:
4970 DeleteConfigTask(Machine *m,
4971 Progress *p,
4972 const Utf8Str &t,
4973 const RTCList<ComPtr<IMedium> > &llMediums,
4974 const StringsList &llFilesToDelete)
4975 : Task(m, p, t),
4976 m_llMediums(llMediums),
4977 m_llFilesToDelete(llFilesToDelete)
4978 {}
4979
4980private:
4981 void handler()
4982 {
4983 try
4984 {
4985 m_pMachine->i_deleteConfigHandler(*this);
4986 }
4987 catch (...)
4988 {
4989 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4990 }
4991 }
4992
4993 RTCList<ComPtr<IMedium> > m_llMediums;
4994 StringsList m_llFilesToDelete;
4995
4996 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4997};
4998
4999/**
5000 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5001 * SessionMachine::taskHandler().
5002 *
5003 * @note Locks this object for writing.
5004 *
5005 * @param task
5006 * @return
5007 */
5008void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5009{
5010 LogFlowThisFuncEnter();
5011
5012 AutoCaller autoCaller(this);
5013 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5014 if (FAILED(autoCaller.rc()))
5015 {
5016 /* we might have been uninitialized because the session was accidentally
5017 * closed by the client, so don't assert */
5018 HRESULT rc = setError(E_FAIL,
5019 tr("The session has been accidentally closed"));
5020 task.m_pProgress->i_notifyComplete(rc);
5021 LogFlowThisFuncLeave();
5022 return;
5023 }
5024
5025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5026
5027 HRESULT rc = S_OK;
5028
5029 try
5030 {
5031 ULONG uLogHistoryCount = 3;
5032 ComPtr<ISystemProperties> systemProperties;
5033 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5034 if (FAILED(rc)) throw rc;
5035
5036 if (!systemProperties.isNull())
5037 {
5038 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5039 if (FAILED(rc)) throw rc;
5040 }
5041
5042 MachineState_T oldState = mData->mMachineState;
5043 i_setMachineState(MachineState_SettingUp);
5044 alock.release();
5045 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5046 {
5047 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5048 {
5049 AutoCaller mac(pMedium);
5050 if (FAILED(mac.rc())) throw mac.rc();
5051 Utf8Str strLocation = pMedium->i_getLocationFull();
5052 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5053 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5054 if (FAILED(rc)) throw rc;
5055 }
5056 if (pMedium->i_isMediumFormatFile())
5057 {
5058 ComPtr<IProgress> pProgress2;
5059 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5060 if (FAILED(rc)) throw rc;
5061 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5062 if (FAILED(rc)) throw rc;
5063 }
5064
5065 /* Close the medium, deliberately without checking the return
5066 * code, and without leaving any trace in the error info, as
5067 * a failure here is a very minor issue, which shouldn't happen
5068 * as above we even managed to delete the medium. */
5069 {
5070 ErrorInfoKeeper eik;
5071 pMedium->Close();
5072 }
5073 }
5074 i_setMachineState(oldState);
5075 alock.acquire();
5076
5077 // delete the files pushed on the task list by Machine::Delete()
5078 // (this includes saved states of the machine and snapshots and
5079 // medium storage files from the IMedium list passed in, and the
5080 // machine XML file)
5081 for (StringsList::const_iterator
5082 it = task.m_llFilesToDelete.begin();
5083 it != task.m_llFilesToDelete.end();
5084 ++it)
5085 {
5086 const Utf8Str &strFile = *it;
5087 LogFunc(("Deleting file %s\n", strFile.c_str()));
5088 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5089 if (FAILED(rc)) throw rc;
5090
5091 int vrc = RTFileDelete(strFile.c_str());
5092 if (RT_FAILURE(vrc))
5093 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5094 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5095 }
5096
5097 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5098 if (FAILED(rc)) throw rc;
5099
5100 /* delete the settings only when the file actually exists */
5101 if (mData->pMachineConfigFile->fileExists())
5102 {
5103 /* Delete any backup or uncommitted XML files. Ignore failures.
5104 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5105 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5106 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5107 RTFileDelete(otherXml.c_str());
5108 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5109 RTFileDelete(otherXml.c_str());
5110
5111 /* delete the Logs folder, nothing important should be left
5112 * there (we don't check for errors because the user might have
5113 * some private files there that we don't want to delete) */
5114 Utf8Str logFolder;
5115 getLogFolder(logFolder);
5116 Assert(logFolder.length());
5117 if (RTDirExists(logFolder.c_str()))
5118 {
5119 /* Delete all VBox.log[.N] files from the Logs folder
5120 * (this must be in sync with the rotation logic in
5121 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5122 * files that may have been created by the GUI. */
5123 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5124 RTFileDelete(log.c_str());
5125 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5126 RTFileDelete(log.c_str());
5127 for (ULONG i = uLogHistoryCount; i > 0; i--)
5128 {
5129 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5130 RTFileDelete(log.c_str());
5131 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5132 RTFileDelete(log.c_str());
5133 }
5134#if defined(RT_OS_WINDOWS)
5135 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5136 RTFileDelete(log.c_str());
5137 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5138 RTFileDelete(log.c_str());
5139#endif
5140
5141 RTDirRemove(logFolder.c_str());
5142 }
5143
5144 /* delete the Snapshots folder, nothing important should be left
5145 * there (we don't check for errors because the user might have
5146 * some private files there that we don't want to delete) */
5147 Utf8Str strFullSnapshotFolder;
5148 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5149 Assert(!strFullSnapshotFolder.isEmpty());
5150 if (RTDirExists(strFullSnapshotFolder.c_str()))
5151 RTDirRemove(strFullSnapshotFolder.c_str());
5152
5153 // delete the directory that contains the settings file, but only
5154 // if it matches the VM name
5155 Utf8Str settingsDir;
5156 if (i_isInOwnDir(&settingsDir))
5157 RTDirRemove(settingsDir.c_str());
5158 }
5159
5160 alock.release();
5161
5162 mParent->i_saveModifiedRegistries();
5163 }
5164 catch (HRESULT aRC) { rc = aRC; }
5165
5166 task.m_pProgress->i_notifyComplete(rc);
5167
5168 LogFlowThisFuncLeave();
5169}
5170
5171HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5172{
5173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5174
5175 HRESULT rc = i_checkStateDependency(MutableStateDep);
5176 if (FAILED(rc)) return rc;
5177
5178 if (mData->mRegistered)
5179 return setError(VBOX_E_INVALID_VM_STATE,
5180 tr("Cannot delete settings of a registered machine"));
5181
5182 // collect files to delete
5183 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5184 // machine config file
5185 if (mData->pMachineConfigFile->fileExists())
5186 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5187 // backup of machine config file
5188 Utf8Str strTmp(mData->m_strConfigFileFull);
5189 strTmp.append("-prev");
5190 if (RTFileExists(strTmp.c_str()))
5191 llFilesToDelete.push_back(strTmp);
5192
5193 RTCList<ComPtr<IMedium> > llMediums;
5194 for (size_t i = 0; i < aMedia.size(); ++i)
5195 {
5196 IMedium *pIMedium(aMedia[i]);
5197 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5198 if (pMedium.isNull())
5199 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5200 SafeArray<BSTR> ids;
5201 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5202 if (FAILED(rc)) return rc;
5203 /* At this point the medium should not have any back references
5204 * anymore. If it has it is attached to another VM and *must* not
5205 * deleted. */
5206 if (ids.size() < 1)
5207 llMediums.append(pMedium);
5208 }
5209
5210 ComObjPtr<Progress> pProgress;
5211 pProgress.createObject();
5212 rc = pProgress->init(i_getVirtualBox(),
5213 static_cast<IMachine*>(this) /* aInitiator */,
5214 tr("Deleting files"),
5215 true /* fCancellable */,
5216 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5217 tr("Collecting file inventory"));
5218 if (FAILED(rc))
5219 return rc;
5220
5221 /* create and start the task on a separate thread (note that it will not
5222 * start working until we release alock) */
5223 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5224 rc = pTask->createThread();
5225 pTask = NULL;
5226 if (FAILED(rc))
5227 return rc;
5228
5229 pProgress.queryInterfaceTo(aProgress.asOutParam());
5230
5231 LogFlowFuncLeave();
5232
5233 return S_OK;
5234}
5235
5236HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5237{
5238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5239
5240 ComObjPtr<Snapshot> pSnapshot;
5241 HRESULT rc;
5242
5243 if (aNameOrId.isEmpty())
5244 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5245 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5246 else
5247 {
5248 Guid uuid(aNameOrId);
5249 if (uuid.isValid())
5250 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5251 else
5252 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5253 }
5254 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5255
5256 return rc;
5257}
5258
5259HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5260 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5261{
5262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5263
5264 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5265 if (FAILED(rc)) return rc;
5266
5267 ComObjPtr<SharedFolder> sharedFolder;
5268 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5269 if (SUCCEEDED(rc))
5270 return setError(VBOX_E_OBJECT_IN_USE,
5271 tr("Shared folder named '%s' already exists"),
5272 aName.c_str());
5273
5274 sharedFolder.createObject();
5275 rc = sharedFolder->init(i_getMachine(),
5276 aName,
5277 aHostPath,
5278 !!aWritable,
5279 !!aAutomount,
5280 aAutoMountPoint,
5281 true /* fFailOnError */);
5282 if (FAILED(rc)) return rc;
5283
5284 i_setModified(IsModified_SharedFolders);
5285 mHWData.backup();
5286 mHWData->mSharedFolders.push_back(sharedFolder);
5287
5288 /* inform the direct session if any */
5289 alock.release();
5290 i_onSharedFolderChange();
5291
5292 return S_OK;
5293}
5294
5295HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5296{
5297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5298
5299 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5300 if (FAILED(rc)) return rc;
5301
5302 ComObjPtr<SharedFolder> sharedFolder;
5303 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5304 if (FAILED(rc)) return rc;
5305
5306 i_setModified(IsModified_SharedFolders);
5307 mHWData.backup();
5308 mHWData->mSharedFolders.remove(sharedFolder);
5309
5310 /* inform the direct session if any */
5311 alock.release();
5312 i_onSharedFolderChange();
5313
5314 return S_OK;
5315}
5316
5317HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5318{
5319 /* start with No */
5320 *aCanShow = FALSE;
5321
5322 ComPtr<IInternalSessionControl> directControl;
5323 {
5324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5325
5326 if (mData->mSession.mState != SessionState_Locked)
5327 return setError(VBOX_E_INVALID_VM_STATE,
5328 tr("Machine is not locked for session (session state: %s)"),
5329 Global::stringifySessionState(mData->mSession.mState));
5330
5331 if (mData->mSession.mLockType == LockType_VM)
5332 directControl = mData->mSession.mDirectControl;
5333 }
5334
5335 /* ignore calls made after #OnSessionEnd() is called */
5336 if (!directControl)
5337 return S_OK;
5338
5339 LONG64 dummy;
5340 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5341}
5342
5343HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5344{
5345 ComPtr<IInternalSessionControl> directControl;
5346 {
5347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5348
5349 if (mData->mSession.mState != SessionState_Locked)
5350 return setError(E_FAIL,
5351 tr("Machine is not locked for session (session state: %s)"),
5352 Global::stringifySessionState(mData->mSession.mState));
5353
5354 if (mData->mSession.mLockType == LockType_VM)
5355 directControl = mData->mSession.mDirectControl;
5356 }
5357
5358 /* ignore calls made after #OnSessionEnd() is called */
5359 if (!directControl)
5360 return S_OK;
5361
5362 BOOL dummy;
5363 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5364}
5365
5366#ifdef VBOX_WITH_GUEST_PROPS
5367/**
5368 * Look up a guest property in VBoxSVC's internal structures.
5369 */
5370HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5371 com::Utf8Str &aValue,
5372 LONG64 *aTimestamp,
5373 com::Utf8Str &aFlags) const
5374{
5375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5376
5377 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5378 if (it != mHWData->mGuestProperties.end())
5379 {
5380 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5381 aValue = it->second.strValue;
5382 *aTimestamp = it->second.mTimestamp;
5383 GuestPropWriteFlags(it->second.mFlags, szFlags);
5384 aFlags = Utf8Str(szFlags);
5385 }
5386
5387 return S_OK;
5388}
5389
5390/**
5391 * Query the VM that a guest property belongs to for the property.
5392 * @returns E_ACCESSDENIED if the VM process is not available or not
5393 * currently handling queries and the lookup should then be done in
5394 * VBoxSVC.
5395 */
5396HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5397 com::Utf8Str &aValue,
5398 LONG64 *aTimestamp,
5399 com::Utf8Str &aFlags) const
5400{
5401 HRESULT rc = S_OK;
5402 Bstr bstrValue;
5403 Bstr bstrFlags;
5404
5405 ComPtr<IInternalSessionControl> directControl;
5406 {
5407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5408 if (mData->mSession.mLockType == LockType_VM)
5409 directControl = mData->mSession.mDirectControl;
5410 }
5411
5412 /* ignore calls made after #OnSessionEnd() is called */
5413 if (!directControl)
5414 rc = E_ACCESSDENIED;
5415 else
5416 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5417 0 /* accessMode */,
5418 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5419
5420 aValue = bstrValue;
5421 aFlags = bstrFlags;
5422
5423 return rc;
5424}
5425#endif // VBOX_WITH_GUEST_PROPS
5426
5427HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5428 com::Utf8Str &aValue,
5429 LONG64 *aTimestamp,
5430 com::Utf8Str &aFlags)
5431{
5432#ifndef VBOX_WITH_GUEST_PROPS
5433 ReturnComNotImplemented();
5434#else // VBOX_WITH_GUEST_PROPS
5435
5436 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5437
5438 if (rc == E_ACCESSDENIED)
5439 /* The VM is not running or the service is not (yet) accessible */
5440 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5441 return rc;
5442#endif // VBOX_WITH_GUEST_PROPS
5443}
5444
5445HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5446{
5447 LONG64 dummyTimestamp;
5448 com::Utf8Str dummyFlags;
5449 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5450 return rc;
5451
5452}
5453HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5454{
5455 com::Utf8Str dummyFlags;
5456 com::Utf8Str dummyValue;
5457 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5458 return rc;
5459}
5460
5461#ifdef VBOX_WITH_GUEST_PROPS
5462/**
5463 * Set a guest property in VBoxSVC's internal structures.
5464 */
5465HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5466 const com::Utf8Str &aFlags, bool fDelete)
5467{
5468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5469 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5470 if (FAILED(rc)) return rc;
5471
5472 try
5473 {
5474 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5475 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5476 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5477
5478 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5479 if (it == mHWData->mGuestProperties.end())
5480 {
5481 if (!fDelete)
5482 {
5483 i_setModified(IsModified_MachineData);
5484 mHWData.backupEx();
5485
5486 RTTIMESPEC time;
5487 HWData::GuestProperty prop;
5488 prop.strValue = Bstr(aValue).raw();
5489 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5490 prop.mFlags = fFlags;
5491 mHWData->mGuestProperties[aName] = prop;
5492 }
5493 }
5494 else
5495 {
5496 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5497 {
5498 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5499 }
5500 else
5501 {
5502 i_setModified(IsModified_MachineData);
5503 mHWData.backupEx();
5504
5505 /* The backupEx() operation invalidates our iterator,
5506 * so get a new one. */
5507 it = mHWData->mGuestProperties.find(aName);
5508 Assert(it != mHWData->mGuestProperties.end());
5509
5510 if (!fDelete)
5511 {
5512 RTTIMESPEC time;
5513 it->second.strValue = aValue;
5514 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5515 it->second.mFlags = fFlags;
5516 }
5517 else
5518 mHWData->mGuestProperties.erase(it);
5519 }
5520 }
5521
5522 if (SUCCEEDED(rc))
5523 {
5524 alock.release();
5525
5526 mParent->i_onGuestPropertyChange(mData->mUuid,
5527 Bstr(aName).raw(),
5528 Bstr(aValue).raw(),
5529 Bstr(aFlags).raw());
5530 }
5531 }
5532 catch (std::bad_alloc &)
5533 {
5534 rc = E_OUTOFMEMORY;
5535 }
5536
5537 return rc;
5538}
5539
5540/**
5541 * Set a property on the VM that that property belongs to.
5542 * @returns E_ACCESSDENIED if the VM process is not available or not
5543 * currently handling queries and the setting should then be done in
5544 * VBoxSVC.
5545 */
5546HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5547 const com::Utf8Str &aFlags, bool fDelete)
5548{
5549 HRESULT rc;
5550
5551 try
5552 {
5553 ComPtr<IInternalSessionControl> directControl;
5554 {
5555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5556 if (mData->mSession.mLockType == LockType_VM)
5557 directControl = mData->mSession.mDirectControl;
5558 }
5559
5560 Bstr dummy1; /* will not be changed (setter) */
5561 Bstr dummy2; /* will not be changed (setter) */
5562 LONG64 dummy64;
5563 if (!directControl)
5564 rc = E_ACCESSDENIED;
5565 else
5566 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5567 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5568 fDelete ? 2 : 1 /* accessMode */,
5569 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5570 }
5571 catch (std::bad_alloc &)
5572 {
5573 rc = E_OUTOFMEMORY;
5574 }
5575
5576 return rc;
5577}
5578#endif // VBOX_WITH_GUEST_PROPS
5579
5580HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5581 const com::Utf8Str &aFlags)
5582{
5583#ifndef VBOX_WITH_GUEST_PROPS
5584 ReturnComNotImplemented();
5585#else // VBOX_WITH_GUEST_PROPS
5586 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5587 if (rc == E_ACCESSDENIED)
5588 /* The VM is not running or the service is not (yet) accessible */
5589 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5590 return rc;
5591#endif // VBOX_WITH_GUEST_PROPS
5592}
5593
5594HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5595{
5596 return setGuestProperty(aProperty, aValue, "");
5597}
5598
5599HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5600{
5601#ifndef VBOX_WITH_GUEST_PROPS
5602 ReturnComNotImplemented();
5603#else // VBOX_WITH_GUEST_PROPS
5604 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5605 if (rc == E_ACCESSDENIED)
5606 /* The VM is not running or the service is not (yet) accessible */
5607 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5608 return rc;
5609#endif // VBOX_WITH_GUEST_PROPS
5610}
5611
5612#ifdef VBOX_WITH_GUEST_PROPS
5613/**
5614 * Enumerate the guest properties in VBoxSVC's internal structures.
5615 */
5616HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5617 std::vector<com::Utf8Str> &aNames,
5618 std::vector<com::Utf8Str> &aValues,
5619 std::vector<LONG64> &aTimestamps,
5620 std::vector<com::Utf8Str> &aFlags)
5621{
5622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5623 Utf8Str strPatterns(aPatterns);
5624
5625 /*
5626 * Look for matching patterns and build up a list.
5627 */
5628 HWData::GuestPropertyMap propMap;
5629 for (HWData::GuestPropertyMap::const_iterator
5630 it = mHWData->mGuestProperties.begin();
5631 it != mHWData->mGuestProperties.end();
5632 ++it)
5633 {
5634 if ( strPatterns.isEmpty()
5635 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5636 RTSTR_MAX,
5637 it->first.c_str(),
5638 RTSTR_MAX,
5639 NULL)
5640 )
5641 propMap.insert(*it);
5642 }
5643
5644 alock.release();
5645
5646 /*
5647 * And build up the arrays for returning the property information.
5648 */
5649 size_t cEntries = propMap.size();
5650
5651 aNames.resize(cEntries);
5652 aValues.resize(cEntries);
5653 aTimestamps.resize(cEntries);
5654 aFlags.resize(cEntries);
5655
5656 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5657 size_t i = 0;
5658 for (HWData::GuestPropertyMap::const_iterator
5659 it = propMap.begin();
5660 it != propMap.end();
5661 ++it, ++i)
5662 {
5663 aNames[i] = it->first;
5664 aValues[i] = it->second.strValue;
5665 aTimestamps[i] = it->second.mTimestamp;
5666 GuestPropWriteFlags(it->second.mFlags, szFlags);
5667 aFlags[i] = Utf8Str(szFlags);
5668 }
5669
5670 return S_OK;
5671}
5672
5673/**
5674 * Enumerate the properties managed by a VM.
5675 * @returns E_ACCESSDENIED if the VM process is not available or not
5676 * currently handling queries and the setting should then be done in
5677 * VBoxSVC.
5678 */
5679HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5680 std::vector<com::Utf8Str> &aNames,
5681 std::vector<com::Utf8Str> &aValues,
5682 std::vector<LONG64> &aTimestamps,
5683 std::vector<com::Utf8Str> &aFlags)
5684{
5685 HRESULT rc;
5686 ComPtr<IInternalSessionControl> directControl;
5687 {
5688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5689 if (mData->mSession.mLockType == LockType_VM)
5690 directControl = mData->mSession.mDirectControl;
5691 }
5692
5693 com::SafeArray<BSTR> bNames;
5694 com::SafeArray<BSTR> bValues;
5695 com::SafeArray<LONG64> bTimestamps;
5696 com::SafeArray<BSTR> bFlags;
5697
5698 if (!directControl)
5699 rc = E_ACCESSDENIED;
5700 else
5701 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5702 ComSafeArrayAsOutParam(bNames),
5703 ComSafeArrayAsOutParam(bValues),
5704 ComSafeArrayAsOutParam(bTimestamps),
5705 ComSafeArrayAsOutParam(bFlags));
5706 size_t i;
5707 aNames.resize(bNames.size());
5708 for (i = 0; i < bNames.size(); ++i)
5709 aNames[i] = Utf8Str(bNames[i]);
5710 aValues.resize(bValues.size());
5711 for (i = 0; i < bValues.size(); ++i)
5712 aValues[i] = Utf8Str(bValues[i]);
5713 aTimestamps.resize(bTimestamps.size());
5714 for (i = 0; i < bTimestamps.size(); ++i)
5715 aTimestamps[i] = bTimestamps[i];
5716 aFlags.resize(bFlags.size());
5717 for (i = 0; i < bFlags.size(); ++i)
5718 aFlags[i] = Utf8Str(bFlags[i]);
5719
5720 return rc;
5721}
5722#endif // VBOX_WITH_GUEST_PROPS
5723HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5724 std::vector<com::Utf8Str> &aNames,
5725 std::vector<com::Utf8Str> &aValues,
5726 std::vector<LONG64> &aTimestamps,
5727 std::vector<com::Utf8Str> &aFlags)
5728{
5729#ifndef VBOX_WITH_GUEST_PROPS
5730 ReturnComNotImplemented();
5731#else // VBOX_WITH_GUEST_PROPS
5732
5733 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5734
5735 if (rc == E_ACCESSDENIED)
5736 /* The VM is not running or the service is not (yet) accessible */
5737 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5738 return rc;
5739#endif // VBOX_WITH_GUEST_PROPS
5740}
5741
5742HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5743 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5744{
5745 MediumAttachmentList atts;
5746
5747 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5748 if (FAILED(rc)) return rc;
5749
5750 aMediumAttachments.resize(atts.size());
5751 size_t i = 0;
5752 for (MediumAttachmentList::const_iterator
5753 it = atts.begin();
5754 it != atts.end();
5755 ++it, ++i)
5756 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5757
5758 return S_OK;
5759}
5760
5761HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5762 LONG aControllerPort,
5763 LONG aDevice,
5764 ComPtr<IMediumAttachment> &aAttachment)
5765{
5766 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5767 aName.c_str(), aControllerPort, aDevice));
5768
5769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5770
5771 aAttachment = NULL;
5772
5773 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5774 aName,
5775 aControllerPort,
5776 aDevice);
5777 if (pAttach.isNull())
5778 return setError(VBOX_E_OBJECT_NOT_FOUND,
5779 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5780 aDevice, aControllerPort, aName.c_str());
5781
5782 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5783
5784 return S_OK;
5785}
5786
5787
5788HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5789 StorageBus_T aConnectionType,
5790 ComPtr<IStorageController> &aController)
5791{
5792 if ( (aConnectionType <= StorageBus_Null)
5793 || (aConnectionType > StorageBus_VirtioSCSI))
5794 return setError(E_INVALIDARG,
5795 tr("Invalid connection type: %d"),
5796 aConnectionType);
5797
5798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5799
5800 HRESULT rc = i_checkStateDependency(MutableStateDep);
5801 if (FAILED(rc)) return rc;
5802
5803 /* try to find one with the name first. */
5804 ComObjPtr<StorageController> ctrl;
5805
5806 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5807 if (SUCCEEDED(rc))
5808 return setError(VBOX_E_OBJECT_IN_USE,
5809 tr("Storage controller named '%s' already exists"),
5810 aName.c_str());
5811
5812 ctrl.createObject();
5813
5814 /* get a new instance number for the storage controller */
5815 ULONG ulInstance = 0;
5816 bool fBootable = true;
5817 for (StorageControllerList::const_iterator
5818 it = mStorageControllers->begin();
5819 it != mStorageControllers->end();
5820 ++it)
5821 {
5822 if ((*it)->i_getStorageBus() == aConnectionType)
5823 {
5824 ULONG ulCurInst = (*it)->i_getInstance();
5825
5826 if (ulCurInst >= ulInstance)
5827 ulInstance = ulCurInst + 1;
5828
5829 /* Only one controller of each type can be marked as bootable. */
5830 if ((*it)->i_getBootable())
5831 fBootable = false;
5832 }
5833 }
5834
5835 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5836 if (FAILED(rc)) return rc;
5837
5838 i_setModified(IsModified_Storage);
5839 mStorageControllers.backup();
5840 mStorageControllers->push_back(ctrl);
5841
5842 ctrl.queryInterfaceTo(aController.asOutParam());
5843
5844 /* inform the direct session if any */
5845 alock.release();
5846 i_onStorageControllerChange(i_getId(), aName);
5847
5848 return S_OK;
5849}
5850
5851HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5852 ComPtr<IStorageController> &aStorageController)
5853{
5854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5855
5856 ComObjPtr<StorageController> ctrl;
5857
5858 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5859 if (SUCCEEDED(rc))
5860 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5861
5862 return rc;
5863}
5864
5865HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5866 ULONG aInstance,
5867 ComPtr<IStorageController> &aStorageController)
5868{
5869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5870
5871 for (StorageControllerList::const_iterator
5872 it = mStorageControllers->begin();
5873 it != mStorageControllers->end();
5874 ++it)
5875 {
5876 if ( (*it)->i_getStorageBus() == aConnectionType
5877 && (*it)->i_getInstance() == aInstance)
5878 {
5879 (*it).queryInterfaceTo(aStorageController.asOutParam());
5880 return S_OK;
5881 }
5882 }
5883
5884 return setError(VBOX_E_OBJECT_NOT_FOUND,
5885 tr("Could not find a storage controller with instance number '%lu'"),
5886 aInstance);
5887}
5888
5889HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5890{
5891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5892
5893 HRESULT rc = i_checkStateDependency(MutableStateDep);
5894 if (FAILED(rc)) return rc;
5895
5896 ComObjPtr<StorageController> ctrl;
5897
5898 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5899 if (SUCCEEDED(rc))
5900 {
5901 /* Ensure that only one controller of each type is marked as bootable. */
5902 if (aBootable == TRUE)
5903 {
5904 for (StorageControllerList::const_iterator
5905 it = mStorageControllers->begin();
5906 it != mStorageControllers->end();
5907 ++it)
5908 {
5909 ComObjPtr<StorageController> aCtrl = (*it);
5910
5911 if ( (aCtrl->i_getName() != aName)
5912 && aCtrl->i_getBootable() == TRUE
5913 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5914 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5915 {
5916 aCtrl->i_setBootable(FALSE);
5917 break;
5918 }
5919 }
5920 }
5921
5922 if (SUCCEEDED(rc))
5923 {
5924 ctrl->i_setBootable(aBootable);
5925 i_setModified(IsModified_Storage);
5926 }
5927 }
5928
5929 if (SUCCEEDED(rc))
5930 {
5931 /* inform the direct session if any */
5932 alock.release();
5933 i_onStorageControllerChange(i_getId(), aName);
5934 }
5935
5936 return rc;
5937}
5938
5939HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5940{
5941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5942
5943 HRESULT rc = i_checkStateDependency(MutableStateDep);
5944 if (FAILED(rc)) return rc;
5945
5946 ComObjPtr<StorageController> ctrl;
5947 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5948 if (FAILED(rc)) return rc;
5949
5950 MediumAttachmentList llDetachedAttachments;
5951 {
5952 /* find all attached devices to the appropriate storage controller and detach them all */
5953 // make a temporary list because detachDevice invalidates iterators into
5954 // mMediumAttachments
5955 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5956
5957 for (MediumAttachmentList::const_iterator
5958 it = llAttachments2.begin();
5959 it != llAttachments2.end();
5960 ++it)
5961 {
5962 MediumAttachment *pAttachTemp = *it;
5963
5964 AutoCaller localAutoCaller(pAttachTemp);
5965 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5966
5967 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5968
5969 if (pAttachTemp->i_getControllerName() == aName)
5970 {
5971 llDetachedAttachments.push_back(pAttachTemp);
5972 rc = i_detachDevice(pAttachTemp, alock, NULL);
5973 if (FAILED(rc)) return rc;
5974 }
5975 }
5976 }
5977
5978 /* send event about detached devices before removing parent controller */
5979 for (MediumAttachmentList::const_iterator
5980 it = llDetachedAttachments.begin();
5981 it != llDetachedAttachments.end();
5982 ++it)
5983 {
5984 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5985 }
5986
5987 /* We can remove it now. */
5988 i_setModified(IsModified_Storage);
5989 mStorageControllers.backup();
5990
5991 ctrl->i_unshare();
5992
5993 mStorageControllers->remove(ctrl);
5994
5995 /* inform the direct session if any */
5996 alock.release();
5997 i_onStorageControllerChange(i_getId(), aName);
5998
5999 return S_OK;
6000}
6001
6002HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6003 ComPtr<IUSBController> &aController)
6004{
6005 if ( (aType <= USBControllerType_Null)
6006 || (aType >= USBControllerType_Last))
6007 return setError(E_INVALIDARG,
6008 tr("Invalid USB controller type: %d"),
6009 aType);
6010
6011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6012
6013 HRESULT rc = i_checkStateDependency(MutableStateDep);
6014 if (FAILED(rc)) return rc;
6015
6016 /* try to find one with the same type first. */
6017 ComObjPtr<USBController> ctrl;
6018
6019 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6020 if (SUCCEEDED(rc))
6021 return setError(VBOX_E_OBJECT_IN_USE,
6022 tr("USB controller named '%s' already exists"),
6023 aName.c_str());
6024
6025 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6026 ULONG maxInstances;
6027 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6028 if (FAILED(rc))
6029 return rc;
6030
6031 ULONG cInstances = i_getUSBControllerCountByType(aType);
6032 if (cInstances >= maxInstances)
6033 return setError(E_INVALIDARG,
6034 tr("Too many USB controllers of this type"));
6035
6036 ctrl.createObject();
6037
6038 rc = ctrl->init(this, aName, aType);
6039 if (FAILED(rc)) return rc;
6040
6041 i_setModified(IsModified_USB);
6042 mUSBControllers.backup();
6043 mUSBControllers->push_back(ctrl);
6044
6045 ctrl.queryInterfaceTo(aController.asOutParam());
6046
6047 /* inform the direct session if any */
6048 alock.release();
6049 i_onUSBControllerChange();
6050
6051 return S_OK;
6052}
6053
6054HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6055{
6056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6057
6058 ComObjPtr<USBController> ctrl;
6059
6060 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6061 if (SUCCEEDED(rc))
6062 ctrl.queryInterfaceTo(aController.asOutParam());
6063
6064 return rc;
6065}
6066
6067HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6068 ULONG *aControllers)
6069{
6070 if ( (aType <= USBControllerType_Null)
6071 || (aType >= USBControllerType_Last))
6072 return setError(E_INVALIDARG,
6073 tr("Invalid USB controller type: %d"),
6074 aType);
6075
6076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6077
6078 ComObjPtr<USBController> ctrl;
6079
6080 *aControllers = i_getUSBControllerCountByType(aType);
6081
6082 return S_OK;
6083}
6084
6085HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6086{
6087
6088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6089
6090 HRESULT rc = i_checkStateDependency(MutableStateDep);
6091 if (FAILED(rc)) return rc;
6092
6093 ComObjPtr<USBController> ctrl;
6094 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6095 if (FAILED(rc)) return rc;
6096
6097 i_setModified(IsModified_USB);
6098 mUSBControllers.backup();
6099
6100 ctrl->i_unshare();
6101
6102 mUSBControllers->remove(ctrl);
6103
6104 /* inform the direct session if any */
6105 alock.release();
6106 i_onUSBControllerChange();
6107
6108 return S_OK;
6109}
6110
6111HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6112 ULONG *aOriginX,
6113 ULONG *aOriginY,
6114 ULONG *aWidth,
6115 ULONG *aHeight,
6116 BOOL *aEnabled)
6117{
6118 uint32_t u32OriginX= 0;
6119 uint32_t u32OriginY= 0;
6120 uint32_t u32Width = 0;
6121 uint32_t u32Height = 0;
6122 uint16_t u16Flags = 0;
6123
6124 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6125 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6126 if (RT_FAILURE(vrc))
6127 {
6128#ifdef RT_OS_WINDOWS
6129 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6130 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6131 * So just assign fEnable to TRUE again.
6132 * The right fix would be to change GUI API wrappers to make sure that parameters
6133 * are changed only if API succeeds.
6134 */
6135 *aEnabled = TRUE;
6136#endif
6137 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6138 tr("Saved guest size is not available (%Rrc)"),
6139 vrc);
6140 }
6141
6142 *aOriginX = u32OriginX;
6143 *aOriginY = u32OriginY;
6144 *aWidth = u32Width;
6145 *aHeight = u32Height;
6146 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6147
6148 return S_OK;
6149}
6150
6151HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6152 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6153{
6154 if (aScreenId != 0)
6155 return E_NOTIMPL;
6156
6157 if ( aBitmapFormat != BitmapFormat_BGR0
6158 && aBitmapFormat != BitmapFormat_BGRA
6159 && aBitmapFormat != BitmapFormat_RGBA
6160 && aBitmapFormat != BitmapFormat_PNG)
6161 return setError(E_NOTIMPL,
6162 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6163
6164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6165
6166 uint8_t *pu8Data = NULL;
6167 uint32_t cbData = 0;
6168 uint32_t u32Width = 0;
6169 uint32_t u32Height = 0;
6170
6171 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6172
6173 if (RT_FAILURE(vrc))
6174 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6175 tr("Saved thumbnail data is not available (%Rrc)"),
6176 vrc);
6177
6178 HRESULT hr = S_OK;
6179
6180 *aWidth = u32Width;
6181 *aHeight = u32Height;
6182
6183 if (cbData > 0)
6184 {
6185 /* Convert pixels to the format expected by the API caller. */
6186 if (aBitmapFormat == BitmapFormat_BGR0)
6187 {
6188 /* [0] B, [1] G, [2] R, [3] 0. */
6189 aData.resize(cbData);
6190 memcpy(&aData.front(), pu8Data, cbData);
6191 }
6192 else if (aBitmapFormat == BitmapFormat_BGRA)
6193 {
6194 /* [0] B, [1] G, [2] R, [3] A. */
6195 aData.resize(cbData);
6196 for (uint32_t i = 0; i < cbData; i += 4)
6197 {
6198 aData[i] = pu8Data[i];
6199 aData[i + 1] = pu8Data[i + 1];
6200 aData[i + 2] = pu8Data[i + 2];
6201 aData[i + 3] = 0xff;
6202 }
6203 }
6204 else if (aBitmapFormat == BitmapFormat_RGBA)
6205 {
6206 /* [0] R, [1] G, [2] B, [3] A. */
6207 aData.resize(cbData);
6208 for (uint32_t i = 0; i < cbData; i += 4)
6209 {
6210 aData[i] = pu8Data[i + 2];
6211 aData[i + 1] = pu8Data[i + 1];
6212 aData[i + 2] = pu8Data[i];
6213 aData[i + 3] = 0xff;
6214 }
6215 }
6216 else if (aBitmapFormat == BitmapFormat_PNG)
6217 {
6218 uint8_t *pu8PNG = NULL;
6219 uint32_t cbPNG = 0;
6220 uint32_t cxPNG = 0;
6221 uint32_t cyPNG = 0;
6222
6223 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6224
6225 if (RT_SUCCESS(vrc))
6226 {
6227 aData.resize(cbPNG);
6228 if (cbPNG)
6229 memcpy(&aData.front(), pu8PNG, cbPNG);
6230 }
6231 else
6232 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6233 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6234 vrc);
6235
6236 RTMemFree(pu8PNG);
6237 }
6238 }
6239
6240 freeSavedDisplayScreenshot(pu8Data);
6241
6242 return hr;
6243}
6244
6245HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6246 ULONG *aWidth,
6247 ULONG *aHeight,
6248 std::vector<BitmapFormat_T> &aBitmapFormats)
6249{
6250 if (aScreenId != 0)
6251 return E_NOTIMPL;
6252
6253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6254
6255 uint8_t *pu8Data = NULL;
6256 uint32_t cbData = 0;
6257 uint32_t u32Width = 0;
6258 uint32_t u32Height = 0;
6259
6260 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6261
6262 if (RT_FAILURE(vrc))
6263 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6264 tr("Saved screenshot data is not available (%Rrc)"),
6265 vrc);
6266
6267 *aWidth = u32Width;
6268 *aHeight = u32Height;
6269 aBitmapFormats.resize(1);
6270 aBitmapFormats[0] = BitmapFormat_PNG;
6271
6272 freeSavedDisplayScreenshot(pu8Data);
6273
6274 return S_OK;
6275}
6276
6277HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6278 BitmapFormat_T aBitmapFormat,
6279 ULONG *aWidth,
6280 ULONG *aHeight,
6281 std::vector<BYTE> &aData)
6282{
6283 if (aScreenId != 0)
6284 return E_NOTIMPL;
6285
6286 if (aBitmapFormat != BitmapFormat_PNG)
6287 return E_NOTIMPL;
6288
6289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6290
6291 uint8_t *pu8Data = NULL;
6292 uint32_t cbData = 0;
6293 uint32_t u32Width = 0;
6294 uint32_t u32Height = 0;
6295
6296 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6297
6298 if (RT_FAILURE(vrc))
6299 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6300 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6301 vrc);
6302
6303 *aWidth = u32Width;
6304 *aHeight = u32Height;
6305
6306 aData.resize(cbData);
6307 if (cbData)
6308 memcpy(&aData.front(), pu8Data, cbData);
6309
6310 freeSavedDisplayScreenshot(pu8Data);
6311
6312 return S_OK;
6313}
6314
6315HRESULT Machine::hotPlugCPU(ULONG aCpu)
6316{
6317 HRESULT rc = S_OK;
6318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6319
6320 if (!mHWData->mCPUHotPlugEnabled)
6321 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6322
6323 if (aCpu >= mHWData->mCPUCount)
6324 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6325
6326 if (mHWData->mCPUAttached[aCpu])
6327 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6328
6329 alock.release();
6330 rc = i_onCPUChange(aCpu, false);
6331 alock.acquire();
6332 if (FAILED(rc)) return rc;
6333
6334 i_setModified(IsModified_MachineData);
6335 mHWData.backup();
6336 mHWData->mCPUAttached[aCpu] = true;
6337
6338 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6339 if (Global::IsOnline(mData->mMachineState))
6340 i_saveSettings(NULL);
6341
6342 return S_OK;
6343}
6344
6345HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6346{
6347 HRESULT rc = S_OK;
6348
6349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6350
6351 if (!mHWData->mCPUHotPlugEnabled)
6352 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6353
6354 if (aCpu >= SchemaDefs::MaxCPUCount)
6355 return setError(E_INVALIDARG,
6356 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6357 SchemaDefs::MaxCPUCount);
6358
6359 if (!mHWData->mCPUAttached[aCpu])
6360 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6361
6362 /* CPU 0 can't be detached */
6363 if (aCpu == 0)
6364 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6365
6366 alock.release();
6367 rc = i_onCPUChange(aCpu, true);
6368 alock.acquire();
6369 if (FAILED(rc)) return rc;
6370
6371 i_setModified(IsModified_MachineData);
6372 mHWData.backup();
6373 mHWData->mCPUAttached[aCpu] = false;
6374
6375 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6376 if (Global::IsOnline(mData->mMachineState))
6377 i_saveSettings(NULL);
6378
6379 return S_OK;
6380}
6381
6382HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6383{
6384 *aAttached = false;
6385
6386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6387
6388 /* If hotplug is enabled the CPU is always enabled. */
6389 if (!mHWData->mCPUHotPlugEnabled)
6390 {
6391 if (aCpu < mHWData->mCPUCount)
6392 *aAttached = true;
6393 }
6394 else
6395 {
6396 if (aCpu < SchemaDefs::MaxCPUCount)
6397 *aAttached = mHWData->mCPUAttached[aCpu];
6398 }
6399
6400 return S_OK;
6401}
6402
6403HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6404{
6405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6406
6407 Utf8Str log = i_getLogFilename(aIdx);
6408 if (!RTFileExists(log.c_str()))
6409 log.setNull();
6410 aFilename = log;
6411
6412 return S_OK;
6413}
6414
6415HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6416{
6417 if (aSize < 0)
6418 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6419
6420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6421
6422 HRESULT rc = S_OK;
6423 Utf8Str log = i_getLogFilename(aIdx);
6424
6425 /* do not unnecessarily hold the lock while doing something which does
6426 * not need the lock and potentially takes a long time. */
6427 alock.release();
6428
6429 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6430 * keeps the SOAP reply size under 1M for the webservice (we're using
6431 * base64 encoded strings for binary data for years now, avoiding the
6432 * expansion of each byte array element to approx. 25 bytes of XML. */
6433 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6434 aData.resize(cbData);
6435
6436 RTFILE LogFile;
6437 int vrc = RTFileOpen(&LogFile, log.c_str(),
6438 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6439 if (RT_SUCCESS(vrc))
6440 {
6441 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6442 if (RT_SUCCESS(vrc))
6443 aData.resize(cbData);
6444 else
6445 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6446 tr("Could not read log file '%s' (%Rrc)"),
6447 log.c_str(), vrc);
6448 RTFileClose(LogFile);
6449 }
6450 else
6451 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6452 tr("Could not open log file '%s' (%Rrc)"),
6453 log.c_str(), vrc);
6454
6455 if (FAILED(rc))
6456 aData.resize(0);
6457
6458 return rc;
6459}
6460
6461
6462/**
6463 * Currently this method doesn't attach device to the running VM,
6464 * just makes sure it's plugged on next VM start.
6465 */
6466HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6467{
6468 // lock scope
6469 {
6470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6471
6472 HRESULT rc = i_checkStateDependency(MutableStateDep);
6473 if (FAILED(rc)) return rc;
6474
6475 ChipsetType_T aChipset = ChipsetType_PIIX3;
6476 COMGETTER(ChipsetType)(&aChipset);
6477
6478 if (aChipset != ChipsetType_ICH9)
6479 {
6480 return setError(E_INVALIDARG,
6481 tr("Host PCI attachment only supported with ICH9 chipset"));
6482 }
6483
6484 // check if device with this host PCI address already attached
6485 for (HWData::PCIDeviceAssignmentList::const_iterator
6486 it = mHWData->mPCIDeviceAssignments.begin();
6487 it != mHWData->mPCIDeviceAssignments.end();
6488 ++it)
6489 {
6490 LONG iHostAddress = -1;
6491 ComPtr<PCIDeviceAttachment> pAttach;
6492 pAttach = *it;
6493 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6494 if (iHostAddress == aHostAddress)
6495 return setError(E_INVALIDARG,
6496 tr("Device with host PCI address already attached to this VM"));
6497 }
6498
6499 ComObjPtr<PCIDeviceAttachment> pda;
6500 char name[32];
6501
6502 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6503 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6504 pda.createObject();
6505 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6506 i_setModified(IsModified_MachineData);
6507 mHWData.backup();
6508 mHWData->mPCIDeviceAssignments.push_back(pda);
6509 }
6510
6511 return S_OK;
6512}
6513
6514/**
6515 * Currently this method doesn't detach device from the running VM,
6516 * just makes sure it's not plugged on next VM start.
6517 */
6518HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6519{
6520 ComObjPtr<PCIDeviceAttachment> pAttach;
6521 bool fRemoved = false;
6522 HRESULT rc;
6523
6524 // lock scope
6525 {
6526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6527
6528 rc = i_checkStateDependency(MutableStateDep);
6529 if (FAILED(rc)) return rc;
6530
6531 for (HWData::PCIDeviceAssignmentList::const_iterator
6532 it = mHWData->mPCIDeviceAssignments.begin();
6533 it != mHWData->mPCIDeviceAssignments.end();
6534 ++it)
6535 {
6536 LONG iHostAddress = -1;
6537 pAttach = *it;
6538 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6539 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6540 {
6541 i_setModified(IsModified_MachineData);
6542 mHWData.backup();
6543 mHWData->mPCIDeviceAssignments.remove(pAttach);
6544 fRemoved = true;
6545 break;
6546 }
6547 }
6548 }
6549
6550
6551 /* Fire event outside of the lock */
6552 if (fRemoved)
6553 {
6554 Assert(!pAttach.isNull());
6555 ComPtr<IEventSource> es;
6556 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6557 Assert(SUCCEEDED(rc));
6558 Bstr mid;
6559 rc = this->COMGETTER(Id)(mid.asOutParam());
6560 Assert(SUCCEEDED(rc));
6561 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6562 }
6563
6564 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6565 tr("No host PCI device %08x attached"),
6566 aHostAddress
6567 );
6568}
6569
6570HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6571{
6572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6573
6574 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6575 size_t i = 0;
6576 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6577 it = mHWData->mPCIDeviceAssignments.begin();
6578 it != mHWData->mPCIDeviceAssignments.end();
6579 ++it, ++i)
6580 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6581
6582 return S_OK;
6583}
6584
6585HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6586{
6587 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6588
6589 return S_OK;
6590}
6591
6592HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6593{
6594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6595
6596 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6597
6598 return S_OK;
6599}
6600
6601HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6602{
6603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6604 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6605 if (SUCCEEDED(hrc))
6606 {
6607 hrc = mHWData.backupEx();
6608 if (SUCCEEDED(hrc))
6609 {
6610 i_setModified(IsModified_MachineData);
6611 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6612 }
6613 }
6614 return hrc;
6615}
6616
6617HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6618{
6619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6620 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6621 return S_OK;
6622}
6623
6624HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6625{
6626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6627 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6628 if (SUCCEEDED(hrc))
6629 {
6630 hrc = mHWData.backupEx();
6631 if (SUCCEEDED(hrc))
6632 {
6633 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6634 if (SUCCEEDED(hrc))
6635 i_setModified(IsModified_MachineData);
6636 }
6637 }
6638 return hrc;
6639}
6640
6641HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6642{
6643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6644
6645 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6646
6647 return S_OK;
6648}
6649
6650HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6651{
6652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6653 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6654 if (SUCCEEDED(hrc))
6655 {
6656 hrc = mHWData.backupEx();
6657 if (SUCCEEDED(hrc))
6658 {
6659 i_setModified(IsModified_MachineData);
6660 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6661 }
6662 }
6663 return hrc;
6664}
6665
6666HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6667{
6668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6669
6670 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6671
6672 return S_OK;
6673}
6674
6675HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6676{
6677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6678
6679 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6680 if ( SUCCEEDED(hrc)
6681 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6682 {
6683 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6684 int vrc;
6685
6686 if (aAutostartEnabled)
6687 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6688 else
6689 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6690
6691 if (RT_SUCCESS(vrc))
6692 {
6693 hrc = mHWData.backupEx();
6694 if (SUCCEEDED(hrc))
6695 {
6696 i_setModified(IsModified_MachineData);
6697 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6698 }
6699 }
6700 else if (vrc == VERR_NOT_SUPPORTED)
6701 hrc = setError(VBOX_E_NOT_SUPPORTED,
6702 tr("The VM autostart feature is not supported on this platform"));
6703 else if (vrc == VERR_PATH_NOT_FOUND)
6704 hrc = setError(E_FAIL,
6705 tr("The path to the autostart database is not set"));
6706 else
6707 hrc = setError(E_UNEXPECTED,
6708 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6709 aAutostartEnabled ? "Adding" : "Removing",
6710 mUserData->s.strName.c_str(), vrc);
6711 }
6712 return hrc;
6713}
6714
6715HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6716{
6717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6718
6719 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6720
6721 return S_OK;
6722}
6723
6724HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6725{
6726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6727 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6728 if (SUCCEEDED(hrc))
6729 {
6730 hrc = mHWData.backupEx();
6731 if (SUCCEEDED(hrc))
6732 {
6733 i_setModified(IsModified_MachineData);
6734 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6735 }
6736 }
6737 return hrc;
6738}
6739
6740HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6741{
6742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6743
6744 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6745
6746 return S_OK;
6747}
6748
6749HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6750{
6751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6752 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6753 if ( SUCCEEDED(hrc)
6754 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6755 {
6756 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6757 int vrc;
6758
6759 if (aAutostopType != AutostopType_Disabled)
6760 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6761 else
6762 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6763
6764 if (RT_SUCCESS(vrc))
6765 {
6766 hrc = mHWData.backupEx();
6767 if (SUCCEEDED(hrc))
6768 {
6769 i_setModified(IsModified_MachineData);
6770 mHWData->mAutostart.enmAutostopType = aAutostopType;
6771 }
6772 }
6773 else if (vrc == VERR_NOT_SUPPORTED)
6774 hrc = setError(VBOX_E_NOT_SUPPORTED,
6775 tr("The VM autostop feature is not supported on this platform"));
6776 else if (vrc == VERR_PATH_NOT_FOUND)
6777 hrc = setError(E_FAIL,
6778 tr("The path to the autostart database is not set"));
6779 else
6780 hrc = setError(E_UNEXPECTED,
6781 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6782 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6783 mUserData->s.strName.c_str(), vrc);
6784 }
6785 return hrc;
6786}
6787
6788HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6789{
6790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6791
6792 aDefaultFrontend = mHWData->mDefaultFrontend;
6793
6794 return S_OK;
6795}
6796
6797HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6798{
6799 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6800 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6801 if (SUCCEEDED(hrc))
6802 {
6803 hrc = mHWData.backupEx();
6804 if (SUCCEEDED(hrc))
6805 {
6806 i_setModified(IsModified_MachineData);
6807 mHWData->mDefaultFrontend = aDefaultFrontend;
6808 }
6809 }
6810 return hrc;
6811}
6812
6813HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6814{
6815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6816 size_t cbIcon = mUserData->s.ovIcon.size();
6817 aIcon.resize(cbIcon);
6818 if (cbIcon)
6819 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6820 return S_OK;
6821}
6822
6823HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6824{
6825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6826 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6827 if (SUCCEEDED(hrc))
6828 {
6829 i_setModified(IsModified_MachineData);
6830 mUserData.backup();
6831 size_t cbIcon = aIcon.size();
6832 mUserData->s.ovIcon.resize(cbIcon);
6833 if (cbIcon)
6834 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6835 }
6836 return hrc;
6837}
6838
6839HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6840{
6841#ifdef VBOX_WITH_USB
6842 *aUSBProxyAvailable = true;
6843#else
6844 *aUSBProxyAvailable = false;
6845#endif
6846 return S_OK;
6847}
6848
6849HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6850{
6851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6852
6853 *aVMProcessPriority = mUserData->s.enmVMPriority;
6854
6855 return S_OK;
6856}
6857
6858HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6859{
6860 RT_NOREF(aVMProcessPriority);
6861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6862 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6863 if (SUCCEEDED(hrc))
6864 {
6865 hrc = mUserData.backupEx();
6866 if (SUCCEEDED(hrc))
6867 {
6868 i_setModified(IsModified_MachineData);
6869 mUserData->s.enmVMPriority = aVMProcessPriority;
6870 }
6871 }
6872 alock.release();
6873 if (SUCCEEDED(hrc))
6874 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6875 return hrc;
6876}
6877
6878HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6879 ComPtr<IProgress> &aProgress)
6880{
6881 ComObjPtr<Progress> pP;
6882 Progress *ppP = pP;
6883 IProgress *iP = static_cast<IProgress *>(ppP);
6884 IProgress **pProgress = &iP;
6885
6886 IMachine *pTarget = aTarget;
6887
6888 /* Convert the options. */
6889 RTCList<CloneOptions_T> optList;
6890 if (aOptions.size())
6891 for (size_t i = 0; i < aOptions.size(); ++i)
6892 optList.append(aOptions[i]);
6893
6894 if (optList.contains(CloneOptions_Link))
6895 {
6896 if (!i_isSnapshotMachine())
6897 return setError(E_INVALIDARG,
6898 tr("Linked clone can only be created from a snapshot"));
6899 if (aMode != CloneMode_MachineState)
6900 return setError(E_INVALIDARG,
6901 tr("Linked clone can only be created for a single machine state"));
6902 }
6903 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6904
6905 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6906
6907 HRESULT rc = pWorker->start(pProgress);
6908
6909 pP = static_cast<Progress *>(*pProgress);
6910 pP.queryInterfaceTo(aProgress.asOutParam());
6911
6912 return rc;
6913
6914}
6915
6916HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6917 const com::Utf8Str &aType,
6918 ComPtr<IProgress> &aProgress)
6919{
6920 LogFlowThisFuncEnter();
6921
6922 ComObjPtr<Progress> ptrProgress;
6923 HRESULT hrc = ptrProgress.createObject();
6924 if (SUCCEEDED(hrc))
6925 {
6926 /* Initialize our worker task */
6927 MachineMoveVM *pTask = NULL;
6928 try
6929 {
6930 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6931 }
6932 catch (std::bad_alloc &)
6933 {
6934 return E_OUTOFMEMORY;
6935 }
6936
6937 hrc = pTask->init();//no exceptions are thrown
6938
6939 if (SUCCEEDED(hrc))
6940 {
6941 hrc = pTask->createThread();
6942 pTask = NULL; /* Consumed by createThread(). */
6943 if (SUCCEEDED(hrc))
6944 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6945 else
6946 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6947 }
6948 else
6949 delete pTask;
6950 }
6951
6952 LogFlowThisFuncLeave();
6953 return hrc;
6954
6955}
6956
6957HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6958{
6959 NOREF(aProgress);
6960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6961
6962 // This check should always fail.
6963 HRESULT rc = i_checkStateDependency(MutableStateDep);
6964 if (FAILED(rc)) return rc;
6965
6966 AssertFailedReturn(E_NOTIMPL);
6967}
6968
6969HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6970{
6971 NOREF(aSavedStateFile);
6972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6973
6974 // This check should always fail.
6975 HRESULT rc = i_checkStateDependency(MutableStateDep);
6976 if (FAILED(rc)) return rc;
6977
6978 AssertFailedReturn(E_NOTIMPL);
6979}
6980
6981HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6982{
6983 NOREF(aFRemoveFile);
6984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6985
6986 // This check should always fail.
6987 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
6988 if (FAILED(rc)) return rc;
6989
6990 AssertFailedReturn(E_NOTIMPL);
6991}
6992
6993// public methods for internal purposes
6994/////////////////////////////////////////////////////////////////////////////
6995
6996/**
6997 * Adds the given IsModified_* flag to the dirty flags of the machine.
6998 * This must be called either during i_loadSettings or under the machine write lock.
6999 * @param fl Flag
7000 * @param fAllowStateModification If state modifications are allowed.
7001 */
7002void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7003{
7004 mData->flModifications |= fl;
7005 if (fAllowStateModification && i_isStateModificationAllowed())
7006 mData->mCurrentStateModified = true;
7007}
7008
7009/**
7010 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7011 * care of the write locking.
7012 *
7013 * @param fModification The flag to add.
7014 * @param fAllowStateModification If state modifications are allowed.
7015 */
7016void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7017{
7018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7019 i_setModified(fModification, fAllowStateModification);
7020}
7021
7022/**
7023 * Saves the registry entry of this machine to the given configuration node.
7024 *
7025 * @param data Machine registry data.
7026 *
7027 * @note locks this object for reading.
7028 */
7029HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7030{
7031 AutoLimitedCaller autoCaller(this);
7032 AssertComRCReturnRC(autoCaller.rc());
7033
7034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7035
7036 data.uuid = mData->mUuid;
7037 data.strSettingsFile = mData->m_strConfigFile;
7038
7039 return S_OK;
7040}
7041
7042/**
7043 * Calculates the absolute path of the given path taking the directory of the
7044 * machine settings file as the current directory.
7045 *
7046 * @param strPath Path to calculate the absolute path for.
7047 * @param aResult Where to put the result (used only on success, can be the
7048 * same Utf8Str instance as passed in @a aPath).
7049 * @return IPRT result.
7050 *
7051 * @note Locks this object for reading.
7052 */
7053int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7054{
7055 AutoCaller autoCaller(this);
7056 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7057
7058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7059
7060 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7061
7062 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7063
7064 strSettingsDir.stripFilename();
7065 char szFolder[RTPATH_MAX];
7066 size_t cbFolder = sizeof(szFolder);
7067 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7068 if (RT_SUCCESS(vrc))
7069 aResult = szFolder;
7070
7071 return vrc;
7072}
7073
7074/**
7075 * Copies strSource to strTarget, making it relative to the machine folder
7076 * if it is a subdirectory thereof, or simply copying it otherwise.
7077 *
7078 * @param strSource Path to evaluate and copy.
7079 * @param strTarget Buffer to receive target path.
7080 *
7081 * @note Locks this object for reading.
7082 */
7083void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7084 Utf8Str &strTarget)
7085{
7086 AutoCaller autoCaller(this);
7087 AssertComRCReturn(autoCaller.rc(), (void)0);
7088
7089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7090
7091 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7092 // use strTarget as a temporary buffer to hold the machine settings dir
7093 strTarget = mData->m_strConfigFileFull;
7094 strTarget.stripFilename();
7095 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7096 {
7097 // is relative: then append what's left
7098 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7099 // for empty paths (only possible for subdirs) use "." to avoid
7100 // triggering default settings for not present config attributes.
7101 if (strTarget.isEmpty())
7102 strTarget = ".";
7103 }
7104 else
7105 // is not relative: then overwrite
7106 strTarget = strSource;
7107}
7108
7109/**
7110 * Returns the full path to the machine's log folder in the
7111 * \a aLogFolder argument.
7112 */
7113void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7114{
7115 AutoCaller autoCaller(this);
7116 AssertComRCReturnVoid(autoCaller.rc());
7117
7118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7119
7120 char szTmp[RTPATH_MAX];
7121 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7122 if (RT_SUCCESS(vrc))
7123 {
7124 if (szTmp[0] && !mUserData.isNull())
7125 {
7126 char szTmp2[RTPATH_MAX];
7127 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7128 if (RT_SUCCESS(vrc))
7129 aLogFolder.printf("%s%c%s",
7130 szTmp2,
7131 RTPATH_DELIMITER,
7132 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7133 }
7134 else
7135 vrc = VERR_PATH_IS_RELATIVE;
7136 }
7137
7138 if (RT_FAILURE(vrc))
7139 {
7140 // fallback if VBOX_USER_LOGHOME is not set or invalid
7141 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7142 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7143 aLogFolder.append(RTPATH_DELIMITER);
7144 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7145 }
7146}
7147
7148/**
7149 * Returns the full path to the machine's log file for an given index.
7150 */
7151Utf8Str Machine::i_getLogFilename(ULONG idx)
7152{
7153 Utf8Str logFolder;
7154 getLogFolder(logFolder);
7155 Assert(logFolder.length());
7156
7157 Utf8Str log;
7158 if (idx == 0)
7159 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7160#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7161 else if (idx == 1)
7162 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7163 else
7164 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7165#else
7166 else
7167 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7168#endif
7169 return log;
7170}
7171
7172/**
7173 * Returns the full path to the machine's hardened log file.
7174 */
7175Utf8Str Machine::i_getHardeningLogFilename(void)
7176{
7177 Utf8Str strFilename;
7178 getLogFolder(strFilename);
7179 Assert(strFilename.length());
7180 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7181 return strFilename;
7182}
7183
7184/**
7185 * Returns the default NVRAM filename based on the location of the VM config.
7186 * Note that this is a relative path.
7187 */
7188Utf8Str Machine::i_getDefaultNVRAMFilename()
7189{
7190 AutoCaller autoCaller(this);
7191 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7192
7193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7194
7195 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7196 || i_isSnapshotMachine())
7197 return Utf8Str::Empty;
7198
7199 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7200 strNVRAMFilePath.stripPath();
7201 strNVRAMFilePath.stripSuffix();
7202 strNVRAMFilePath += ".nvram";
7203
7204 return strNVRAMFilePath;
7205}
7206
7207/**
7208 * Returns the NVRAM filename for a new snapshot. This intentionally works
7209 * similarly to the saved state file naming. Note that this is usually
7210 * a relative path, unless the snapshot folder is absolute.
7211 */
7212Utf8Str Machine::i_getSnapshotNVRAMFilename()
7213{
7214 AutoCaller autoCaller(this);
7215 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7216
7217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7218
7219 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7220 return Utf8Str::Empty;
7221
7222 RTTIMESPEC ts;
7223 RTTimeNow(&ts);
7224 RTTIME time;
7225 RTTimeExplode(&time, &ts);
7226
7227 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7228 strNVRAMFilePath += RTPATH_DELIMITER;
7229 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7230 time.i32Year, time.u8Month, time.u8MonthDay,
7231 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7232
7233 return strNVRAMFilePath;
7234}
7235
7236/**
7237 * Composes a unique saved state filename based on the current system time. The filename is
7238 * granular to the second so this will work so long as no more than one snapshot is taken on
7239 * a machine per second.
7240 *
7241 * Before version 4.1, we used this formula for saved state files:
7242 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7243 * which no longer works because saved state files can now be shared between the saved state of the
7244 * "saved" machine and an online snapshot, and the following would cause problems:
7245 * 1) save machine
7246 * 2) create online snapshot from that machine state --> reusing saved state file
7247 * 3) save machine again --> filename would be reused, breaking the online snapshot
7248 *
7249 * So instead we now use a timestamp.
7250 *
7251 * @param strStateFilePath
7252 */
7253
7254void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7255{
7256 AutoCaller autoCaller(this);
7257 AssertComRCReturnVoid(autoCaller.rc());
7258
7259 {
7260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7261 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7262 }
7263
7264 RTTIMESPEC ts;
7265 RTTimeNow(&ts);
7266 RTTIME time;
7267 RTTimeExplode(&time, &ts);
7268
7269 strStateFilePath += RTPATH_DELIMITER;
7270 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7271 time.i32Year, time.u8Month, time.u8MonthDay,
7272 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7273}
7274
7275/**
7276 * Returns whether at least one USB controller is present for the VM.
7277 */
7278bool Machine::i_isUSBControllerPresent()
7279{
7280 AutoCaller autoCaller(this);
7281 AssertComRCReturn(autoCaller.rc(), false);
7282
7283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7284
7285 return (mUSBControllers->size() > 0);
7286}
7287
7288
7289/**
7290 * @note Locks this object for writing, calls the client process
7291 * (inside the lock).
7292 */
7293HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7294 const Utf8Str &strFrontend,
7295 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7296 ProgressProxy *aProgress)
7297{
7298 LogFlowThisFuncEnter();
7299
7300 AssertReturn(aControl, E_FAIL);
7301 AssertReturn(aProgress, E_FAIL);
7302 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7303
7304 AutoCaller autoCaller(this);
7305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7306
7307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7308
7309 if (!mData->mRegistered)
7310 return setError(E_UNEXPECTED,
7311 tr("The machine '%s' is not registered"),
7312 mUserData->s.strName.c_str());
7313
7314 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7315
7316 /* The process started when launching a VM with separate UI/VM processes is always
7317 * the UI process, i.e. needs special handling as it won't claim the session. */
7318 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7319
7320 if (fSeparate)
7321 {
7322 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7323 return setError(VBOX_E_INVALID_OBJECT_STATE,
7324 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7325 mUserData->s.strName.c_str());
7326 }
7327 else
7328 {
7329 if ( mData->mSession.mState == SessionState_Locked
7330 || mData->mSession.mState == SessionState_Spawning
7331 || mData->mSession.mState == SessionState_Unlocking)
7332 return setError(VBOX_E_INVALID_OBJECT_STATE,
7333 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7334 mUserData->s.strName.c_str());
7335
7336 /* may not be busy */
7337 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7338 }
7339
7340 /* Hardening logging */
7341#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7342 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7343 {
7344 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7345 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7346 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7347 {
7348 Utf8Str strStartupLogDir = strHardeningLogFile;
7349 strStartupLogDir.stripFilename();
7350 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7351 file without stripping the file. */
7352 }
7353 strSupHardeningLogArg.append(strHardeningLogFile);
7354
7355 /* Remove legacy log filename to avoid confusion. */
7356 Utf8Str strOldStartupLogFile;
7357 getLogFolder(strOldStartupLogFile);
7358 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7359 RTFileDelete(strOldStartupLogFile.c_str());
7360 }
7361#else
7362 Utf8Str strSupHardeningLogArg;
7363#endif
7364
7365 Utf8Str strAppOverride;
7366#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7367 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7368#endif
7369
7370 bool fUseVBoxSDS = false;
7371 Utf8Str strCanonicalName;
7372 if (false)
7373 { }
7374#ifdef VBOX_WITH_QTGUI
7375 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7376 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7377 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7378 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7379 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7380 {
7381 strCanonicalName = "GUI/Qt";
7382 fUseVBoxSDS = true;
7383 }
7384#endif
7385#ifdef VBOX_WITH_VBOXSDL
7386 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7387 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7388 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7389 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7390 {
7391 strCanonicalName = "GUI/SDL";
7392 fUseVBoxSDS = true;
7393 }
7394#endif
7395#ifdef VBOX_WITH_HEADLESS
7396 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7397 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7398 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7399 {
7400 strCanonicalName = "headless";
7401 }
7402#endif
7403 else
7404 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7405
7406 Utf8Str idStr = mData->mUuid.toString();
7407 Utf8Str const &strMachineName = mUserData->s.strName;
7408 RTPROCESS pid = NIL_RTPROCESS;
7409
7410#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7411 RT_NOREF(fUseVBoxSDS);
7412#else
7413 DWORD idCallerSession = ~(DWORD)0;
7414 if (fUseVBoxSDS)
7415 {
7416 /*
7417 * The VBoxSDS should be used for process launching the VM with
7418 * GUI only if the caller and the VBoxSDS are in different Windows
7419 * sessions and the caller in the interactive one.
7420 */
7421 fUseVBoxSDS = false;
7422
7423 /* Get windows session of the current process. The process token used
7424 due to several reasons:
7425 1. The token is absent for the current thread except someone set it
7426 for us.
7427 2. Needs to get the id of the session where the process is started.
7428 We only need to do this once, though. */
7429 static DWORD s_idCurrentSession = ~(DWORD)0;
7430 DWORD idCurrentSession = s_idCurrentSession;
7431 if (idCurrentSession == ~(DWORD)0)
7432 {
7433 HANDLE hCurrentProcessToken = NULL;
7434 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7435 {
7436 DWORD cbIgn = 0;
7437 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7438 s_idCurrentSession = idCurrentSession;
7439 else
7440 {
7441 idCurrentSession = ~(DWORD)0;
7442 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7443 }
7444 CloseHandle(hCurrentProcessToken);
7445 }
7446 else
7447 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7448 }
7449
7450 /* get the caller's session */
7451 HRESULT hrc = CoImpersonateClient();
7452 if (SUCCEEDED(hrc))
7453 {
7454 HANDLE hCallerThreadToken;
7455 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7456 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7457 &hCallerThreadToken))
7458 {
7459 SetLastError(NO_ERROR);
7460 DWORD cbIgn = 0;
7461 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7462 {
7463 /* Only need to use SDS if the session ID differs: */
7464 if (idCurrentSession != idCallerSession)
7465 {
7466 fUseVBoxSDS = false;
7467
7468 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7469 DWORD cbTokenGroups = 0;
7470 PTOKEN_GROUPS pTokenGroups = NULL;
7471 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7472 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7473 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7474 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7475 {
7476 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7477 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7478 PSID pInteractiveSid = NULL;
7479 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7480 {
7481 /* Iterate over the groups looking for the interactive SID: */
7482 fUseVBoxSDS = false;
7483 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7484 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7485 {
7486 fUseVBoxSDS = true;
7487 break;
7488 }
7489 FreeSid(pInteractiveSid);
7490 }
7491 }
7492 else
7493 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7494 RTMemTmpFree(pTokenGroups);
7495 }
7496 }
7497 else
7498 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7499 CloseHandle(hCallerThreadToken);
7500 }
7501 else
7502 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7503 CoRevertToSelf();
7504 }
7505 else
7506 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7507 }
7508 if (fUseVBoxSDS)
7509 {
7510 /* connect to VBoxSDS */
7511 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7512 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7513 if (FAILED(rc))
7514 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7515 strMachineName.c_str());
7516
7517 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7518 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7519 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7520 service to access the files. */
7521 rc = CoSetProxyBlanket(pVBoxSDS,
7522 RPC_C_AUTHN_DEFAULT,
7523 RPC_C_AUTHZ_DEFAULT,
7524 COLE_DEFAULT_PRINCIPAL,
7525 RPC_C_AUTHN_LEVEL_DEFAULT,
7526 RPC_C_IMP_LEVEL_IMPERSONATE,
7527 NULL,
7528 EOAC_DEFAULT);
7529 if (FAILED(rc))
7530 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7531
7532 size_t const cEnvVars = aEnvironmentChanges.size();
7533 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7534 for (size_t i = 0; i < cEnvVars; i++)
7535 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7536
7537 ULONG uPid = 0;
7538 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7539 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7540 idCallerSession, &uPid);
7541 if (FAILED(rc))
7542 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7543 pid = (RTPROCESS)uPid;
7544 }
7545 else
7546#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7547 {
7548 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7549 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7550 if (RT_FAILURE(vrc))
7551 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7552 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7553 }
7554
7555 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7556 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7557
7558 if (!fSeparate)
7559 {
7560 /*
7561 * Note that we don't release the lock here before calling the client,
7562 * because it doesn't need to call us back if called with a NULL argument.
7563 * Releasing the lock here is dangerous because we didn't prepare the
7564 * launch data yet, but the client we've just started may happen to be
7565 * too fast and call LockMachine() that will fail (because of PID, etc.),
7566 * so that the Machine will never get out of the Spawning session state.
7567 */
7568
7569 /* inform the session that it will be a remote one */
7570 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7571#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7572 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7573#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7574 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7575#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7576 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7577
7578 if (FAILED(rc))
7579 {
7580 /* restore the session state */
7581 mData->mSession.mState = SessionState_Unlocked;
7582 alock.release();
7583 mParent->i_addProcessToReap(pid);
7584 /* The failure may occur w/o any error info (from RPC), so provide one */
7585 return setError(VBOX_E_VM_ERROR,
7586 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7587 }
7588
7589 /* attach launch data to the machine */
7590 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7591 mData->mSession.mRemoteControls.push_back(aControl);
7592 mData->mSession.mProgress = aProgress;
7593 mData->mSession.mPID = pid;
7594 mData->mSession.mState = SessionState_Spawning;
7595 Assert(strCanonicalName.isNotEmpty());
7596 mData->mSession.mName = strCanonicalName;
7597 }
7598 else
7599 {
7600 /* For separate UI process we declare the launch as completed instantly, as the
7601 * actual headless VM start may or may not come. No point in remembering anything
7602 * yet, as what matters for us is when the headless VM gets started. */
7603 aProgress->i_notifyComplete(S_OK);
7604 }
7605
7606 alock.release();
7607 mParent->i_addProcessToReap(pid);
7608
7609 LogFlowThisFuncLeave();
7610 return S_OK;
7611}
7612
7613/**
7614 * Returns @c true if the given session machine instance has an open direct
7615 * session (and optionally also for direct sessions which are closing) and
7616 * returns the session control machine instance if so.
7617 *
7618 * Note that when the method returns @c false, the arguments remain unchanged.
7619 *
7620 * @param aMachine Session machine object.
7621 * @param aControl Direct session control object (optional).
7622 * @param aRequireVM If true then only allow VM sessions.
7623 * @param aAllowClosing If true then additionally a session which is currently
7624 * being closed will also be allowed.
7625 *
7626 * @note locks this object for reading.
7627 */
7628bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7629 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7630 bool aRequireVM /*= false*/,
7631 bool aAllowClosing /*= false*/)
7632{
7633 AutoLimitedCaller autoCaller(this);
7634 AssertComRCReturn(autoCaller.rc(), false);
7635
7636 /* just return false for inaccessible machines */
7637 if (getObjectState().getState() != ObjectState::Ready)
7638 return false;
7639
7640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7641
7642 if ( ( mData->mSession.mState == SessionState_Locked
7643 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7644 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7645 )
7646 {
7647 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7648
7649 aMachine = mData->mSession.mMachine;
7650
7651 if (aControl != NULL)
7652 *aControl = mData->mSession.mDirectControl;
7653
7654 return true;
7655 }
7656
7657 return false;
7658}
7659
7660/**
7661 * Returns @c true if the given machine has an spawning direct session.
7662 *
7663 * @note locks this object for reading.
7664 */
7665bool Machine::i_isSessionSpawning()
7666{
7667 AutoLimitedCaller autoCaller(this);
7668 AssertComRCReturn(autoCaller.rc(), false);
7669
7670 /* just return false for inaccessible machines */
7671 if (getObjectState().getState() != ObjectState::Ready)
7672 return false;
7673
7674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7675
7676 if (mData->mSession.mState == SessionState_Spawning)
7677 return true;
7678
7679 return false;
7680}
7681
7682/**
7683 * Called from the client watcher thread to check for unexpected client process
7684 * death during Session_Spawning state (e.g. before it successfully opened a
7685 * direct session).
7686 *
7687 * On Win32 and on OS/2, this method is called only when we've got the
7688 * direct client's process termination notification, so it always returns @c
7689 * true.
7690 *
7691 * On other platforms, this method returns @c true if the client process is
7692 * terminated and @c false if it's still alive.
7693 *
7694 * @note Locks this object for writing.
7695 */
7696bool Machine::i_checkForSpawnFailure()
7697{
7698 AutoCaller autoCaller(this);
7699 if (!autoCaller.isOk())
7700 {
7701 /* nothing to do */
7702 LogFlowThisFunc(("Already uninitialized!\n"));
7703 return true;
7704 }
7705
7706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7707
7708 if (mData->mSession.mState != SessionState_Spawning)
7709 {
7710 /* nothing to do */
7711 LogFlowThisFunc(("Not spawning any more!\n"));
7712 return true;
7713 }
7714
7715 HRESULT rc = S_OK;
7716
7717 /* PID not yet initialized, skip check. */
7718 if (mData->mSession.mPID == NIL_RTPROCESS)
7719 return false;
7720
7721 RTPROCSTATUS status;
7722 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7723
7724 if (vrc != VERR_PROCESS_RUNNING)
7725 {
7726 Utf8Str strExtraInfo;
7727
7728#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7729 /* If the startup logfile exists and is of non-zero length, tell the
7730 user to look there for more details to encourage them to attach it
7731 when reporting startup issues. */
7732 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7733 uint64_t cbStartupLogFile = 0;
7734 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7735 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7736 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7737#endif
7738
7739 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7740 rc = setError(E_FAIL,
7741 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7742 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7743 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7744 rc = setError(E_FAIL,
7745 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7746 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7747 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7748 rc = setError(E_FAIL,
7749 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7750 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7751 else
7752 rc = setErrorBoth(E_FAIL, vrc,
7753 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7754 i_getName().c_str(), vrc, strExtraInfo.c_str());
7755 }
7756
7757 if (FAILED(rc))
7758 {
7759 /* Close the remote session, remove the remote control from the list
7760 * and reset session state to Closed (@note keep the code in sync with
7761 * the relevant part in LockMachine()). */
7762
7763 Assert(mData->mSession.mRemoteControls.size() == 1);
7764 if (mData->mSession.mRemoteControls.size() == 1)
7765 {
7766 ErrorInfoKeeper eik;
7767 mData->mSession.mRemoteControls.front()->Uninitialize();
7768 }
7769
7770 mData->mSession.mRemoteControls.clear();
7771 mData->mSession.mState = SessionState_Unlocked;
7772
7773 /* finalize the progress after setting the state */
7774 if (!mData->mSession.mProgress.isNull())
7775 {
7776 mData->mSession.mProgress->notifyComplete(rc);
7777 mData->mSession.mProgress.setNull();
7778 }
7779
7780 mData->mSession.mPID = NIL_RTPROCESS;
7781
7782 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7783 return true;
7784 }
7785
7786 return false;
7787}
7788
7789/**
7790 * Checks whether the machine can be registered. If so, commits and saves
7791 * all settings.
7792 *
7793 * @note Must be called from mParent's write lock. Locks this object and
7794 * children for writing.
7795 */
7796HRESULT Machine::i_prepareRegister()
7797{
7798 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7799
7800 AutoLimitedCaller autoCaller(this);
7801 AssertComRCReturnRC(autoCaller.rc());
7802
7803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7804
7805 /* wait for state dependents to drop to zero */
7806 i_ensureNoStateDependencies();
7807
7808 if (!mData->mAccessible)
7809 return setError(VBOX_E_INVALID_OBJECT_STATE,
7810 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7811 mUserData->s.strName.c_str(),
7812 mData->mUuid.toString().c_str());
7813
7814 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7815
7816 if (mData->mRegistered)
7817 return setError(VBOX_E_INVALID_OBJECT_STATE,
7818 tr("The machine '%s' with UUID {%s} is already registered"),
7819 mUserData->s.strName.c_str(),
7820 mData->mUuid.toString().c_str());
7821
7822 HRESULT rc = S_OK;
7823
7824 // Ensure the settings are saved. If we are going to be registered and
7825 // no config file exists yet, create it by calling i_saveSettings() too.
7826 if ( (mData->flModifications)
7827 || (!mData->pMachineConfigFile->fileExists())
7828 )
7829 {
7830 rc = i_saveSettings(NULL);
7831 // no need to check whether VirtualBox.xml needs saving too since
7832 // we can't have a machine XML file rename pending
7833 if (FAILED(rc)) return rc;
7834 }
7835
7836 /* more config checking goes here */
7837
7838 if (SUCCEEDED(rc))
7839 {
7840 /* we may have had implicit modifications we want to fix on success */
7841 i_commit();
7842
7843 mData->mRegistered = true;
7844 }
7845 else
7846 {
7847 /* we may have had implicit modifications we want to cancel on failure*/
7848 i_rollback(false /* aNotify */);
7849 }
7850
7851 return rc;
7852}
7853
7854/**
7855 * Increases the number of objects dependent on the machine state or on the
7856 * registered state. Guarantees that these two states will not change at least
7857 * until #i_releaseStateDependency() is called.
7858 *
7859 * Depending on the @a aDepType value, additional state checks may be made.
7860 * These checks will set extended error info on failure. See
7861 * #i_checkStateDependency() for more info.
7862 *
7863 * If this method returns a failure, the dependency is not added and the caller
7864 * is not allowed to rely on any particular machine state or registration state
7865 * value and may return the failed result code to the upper level.
7866 *
7867 * @param aDepType Dependency type to add.
7868 * @param aState Current machine state (NULL if not interested).
7869 * @param aRegistered Current registered state (NULL if not interested).
7870 *
7871 * @note Locks this object for writing.
7872 */
7873HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7874 MachineState_T *aState /* = NULL */,
7875 BOOL *aRegistered /* = NULL */)
7876{
7877 AutoCaller autoCaller(this);
7878 AssertComRCReturnRC(autoCaller.rc());
7879
7880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7881
7882 HRESULT rc = i_checkStateDependency(aDepType);
7883 if (FAILED(rc)) return rc;
7884
7885 {
7886 if (mData->mMachineStateChangePending != 0)
7887 {
7888 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7889 * drop to zero so don't add more. It may make sense to wait a bit
7890 * and retry before reporting an error (since the pending state
7891 * transition should be really quick) but let's just assert for
7892 * now to see if it ever happens on practice. */
7893
7894 AssertFailed();
7895
7896 return setError(E_ACCESSDENIED,
7897 tr("Machine state change is in progress. Please retry the operation later."));
7898 }
7899
7900 ++mData->mMachineStateDeps;
7901 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7902 }
7903
7904 if (aState)
7905 *aState = mData->mMachineState;
7906 if (aRegistered)
7907 *aRegistered = mData->mRegistered;
7908
7909 return S_OK;
7910}
7911
7912/**
7913 * Decreases the number of objects dependent on the machine state.
7914 * Must always complete the #i_addStateDependency() call after the state
7915 * dependency is no more necessary.
7916 */
7917void Machine::i_releaseStateDependency()
7918{
7919 AutoCaller autoCaller(this);
7920 AssertComRCReturnVoid(autoCaller.rc());
7921
7922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7923
7924 /* releaseStateDependency() w/o addStateDependency()? */
7925 AssertReturnVoid(mData->mMachineStateDeps != 0);
7926 -- mData->mMachineStateDeps;
7927
7928 if (mData->mMachineStateDeps == 0)
7929 {
7930 /* inform i_ensureNoStateDependencies() that there are no more deps */
7931 if (mData->mMachineStateChangePending != 0)
7932 {
7933 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7934 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7935 }
7936 }
7937}
7938
7939Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7940{
7941 /* start with nothing found */
7942 Utf8Str strResult("");
7943
7944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7945
7946 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7947 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7948 // found:
7949 strResult = it->second; // source is a Utf8Str
7950
7951 return strResult;
7952}
7953
7954// protected methods
7955/////////////////////////////////////////////////////////////////////////////
7956
7957/**
7958 * Performs machine state checks based on the @a aDepType value. If a check
7959 * fails, this method will set extended error info, otherwise it will return
7960 * S_OK. It is supposed, that on failure, the caller will immediately return
7961 * the return value of this method to the upper level.
7962 *
7963 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7964 *
7965 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7966 * current state of this machine object allows to change settings of the
7967 * machine (i.e. the machine is not registered, or registered but not running
7968 * and not saved). It is useful to call this method from Machine setters
7969 * before performing any change.
7970 *
7971 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7972 * as for MutableStateDep except that if the machine is saved, S_OK is also
7973 * returned. This is useful in setters which allow changing machine
7974 * properties when it is in the saved state.
7975 *
7976 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7977 * if the current state of this machine object allows to change runtime
7978 * changeable settings of the machine (i.e. the machine is not registered, or
7979 * registered but either running or not running and not saved). It is useful
7980 * to call this method from Machine setters before performing any changes to
7981 * runtime changeable settings.
7982 *
7983 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7984 * the same as for MutableOrRunningStateDep except that if the machine is
7985 * saved, S_OK is also returned. This is useful in setters which allow
7986 * changing runtime and saved state changeable machine properties.
7987 *
7988 * @param aDepType Dependency type to check.
7989 *
7990 * @note Non Machine based classes should use #i_addStateDependency() and
7991 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7992 * template.
7993 *
7994 * @note This method must be called from under this object's read or write
7995 * lock.
7996 */
7997HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7998{
7999 switch (aDepType)
8000 {
8001 case AnyStateDep:
8002 {
8003 break;
8004 }
8005 case MutableStateDep:
8006 {
8007 if ( mData->mRegistered
8008 && ( !i_isSessionMachine()
8009 || ( mData->mMachineState != MachineState_Aborted
8010 && mData->mMachineState != MachineState_Teleported
8011 && mData->mMachineState != MachineState_PoweredOff
8012 )
8013 )
8014 )
8015 return setError(VBOX_E_INVALID_VM_STATE,
8016 tr("The machine is not mutable (state is %s)"),
8017 Global::stringifyMachineState(mData->mMachineState));
8018 break;
8019 }
8020 case MutableOrSavedStateDep:
8021 {
8022 if ( mData->mRegistered
8023 && ( !i_isSessionMachine()
8024 || ( mData->mMachineState != MachineState_Aborted
8025 && mData->mMachineState != MachineState_Teleported
8026 && mData->mMachineState != MachineState_Saved
8027 && mData->mMachineState != MachineState_PoweredOff
8028 )
8029 )
8030 )
8031 return setError(VBOX_E_INVALID_VM_STATE,
8032 tr("The machine is not mutable or saved (state is %s)"),
8033 Global::stringifyMachineState(mData->mMachineState));
8034 break;
8035 }
8036 case MutableOrRunningStateDep:
8037 {
8038 if ( mData->mRegistered
8039 && ( !i_isSessionMachine()
8040 || ( mData->mMachineState != MachineState_Aborted
8041 && mData->mMachineState != MachineState_Teleported
8042 && mData->mMachineState != MachineState_PoweredOff
8043 && !Global::IsOnline(mData->mMachineState)
8044 )
8045 )
8046 )
8047 return setError(VBOX_E_INVALID_VM_STATE,
8048 tr("The machine is not mutable or running (state is %s)"),
8049 Global::stringifyMachineState(mData->mMachineState));
8050 break;
8051 }
8052 case MutableOrSavedOrRunningStateDep:
8053 {
8054 if ( mData->mRegistered
8055 && ( !i_isSessionMachine()
8056 || ( mData->mMachineState != MachineState_Aborted
8057 && mData->mMachineState != MachineState_Teleported
8058 && mData->mMachineState != MachineState_Saved
8059 && mData->mMachineState != MachineState_PoweredOff
8060 && !Global::IsOnline(mData->mMachineState)
8061 )
8062 )
8063 )
8064 return setError(VBOX_E_INVALID_VM_STATE,
8065 tr("The machine is not mutable, saved or running (state is %s)"),
8066 Global::stringifyMachineState(mData->mMachineState));
8067 break;
8068 }
8069 }
8070
8071 return S_OK;
8072}
8073
8074/**
8075 * Helper to initialize all associated child objects and allocate data
8076 * structures.
8077 *
8078 * This method must be called as a part of the object's initialization procedure
8079 * (usually done in the #init() method).
8080 *
8081 * @note Must be called only from #init() or from #i_registeredInit().
8082 */
8083HRESULT Machine::initDataAndChildObjects()
8084{
8085 AutoCaller autoCaller(this);
8086 AssertComRCReturnRC(autoCaller.rc());
8087 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8088 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8089
8090 AssertReturn(!mData->mAccessible, E_FAIL);
8091
8092 /* allocate data structures */
8093 mSSData.allocate();
8094 mUserData.allocate();
8095 mHWData.allocate();
8096 mMediumAttachments.allocate();
8097 mStorageControllers.allocate();
8098 mUSBControllers.allocate();
8099
8100 /* initialize mOSTypeId */
8101 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8102
8103/** @todo r=bird: init() methods never fails, right? Why don't we make them
8104 * return void then! */
8105
8106 /* create associated BIOS settings object */
8107 unconst(mBIOSSettings).createObject();
8108 mBIOSSettings->init(this);
8109
8110 /* create associated record settings object */
8111 unconst(mRecordingSettings).createObject();
8112 mRecordingSettings->init(this);
8113
8114 /* create the graphics adapter object (always present) */
8115 unconst(mGraphicsAdapter).createObject();
8116 mGraphicsAdapter->init(this);
8117
8118 /* create an associated VRDE object (default is disabled) */
8119 unconst(mVRDEServer).createObject();
8120 mVRDEServer->init(this);
8121
8122 /* create associated serial port objects */
8123 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8124 {
8125 unconst(mSerialPorts[slot]).createObject();
8126 mSerialPorts[slot]->init(this, slot);
8127 }
8128
8129 /* create associated parallel port objects */
8130 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8131 {
8132 unconst(mParallelPorts[slot]).createObject();
8133 mParallelPorts[slot]->init(this, slot);
8134 }
8135
8136 /* create the audio adapter object (always present, default is disabled) */
8137 unconst(mAudioAdapter).createObject();
8138 mAudioAdapter->init(this);
8139
8140 /* create the USB device filters object (always present) */
8141 unconst(mUSBDeviceFilters).createObject();
8142 mUSBDeviceFilters->init(this);
8143
8144 /* create associated network adapter objects */
8145 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8146 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8147 {
8148 unconst(mNetworkAdapters[slot]).createObject();
8149 mNetworkAdapters[slot]->init(this, slot);
8150 }
8151
8152 /* create the bandwidth control */
8153 unconst(mBandwidthControl).createObject();
8154 mBandwidthControl->init(this);
8155
8156 return S_OK;
8157}
8158
8159/**
8160 * Helper to uninitialize all associated child objects and to free all data
8161 * structures.
8162 *
8163 * This method must be called as a part of the object's uninitialization
8164 * procedure (usually done in the #uninit() method).
8165 *
8166 * @note Must be called only from #uninit() or from #i_registeredInit().
8167 */
8168void Machine::uninitDataAndChildObjects()
8169{
8170 AutoCaller autoCaller(this);
8171 AssertComRCReturnVoid(autoCaller.rc());
8172 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8173 || getObjectState().getState() == ObjectState::Limited);
8174
8175 /* tell all our other child objects we've been uninitialized */
8176 if (mBandwidthControl)
8177 {
8178 mBandwidthControl->uninit();
8179 unconst(mBandwidthControl).setNull();
8180 }
8181
8182 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8183 {
8184 if (mNetworkAdapters[slot])
8185 {
8186 mNetworkAdapters[slot]->uninit();
8187 unconst(mNetworkAdapters[slot]).setNull();
8188 }
8189 }
8190
8191 if (mUSBDeviceFilters)
8192 {
8193 mUSBDeviceFilters->uninit();
8194 unconst(mUSBDeviceFilters).setNull();
8195 }
8196
8197 if (mAudioAdapter)
8198 {
8199 mAudioAdapter->uninit();
8200 unconst(mAudioAdapter).setNull();
8201 }
8202
8203 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8204 {
8205 if (mParallelPorts[slot])
8206 {
8207 mParallelPorts[slot]->uninit();
8208 unconst(mParallelPorts[slot]).setNull();
8209 }
8210 }
8211
8212 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8213 {
8214 if (mSerialPorts[slot])
8215 {
8216 mSerialPorts[slot]->uninit();
8217 unconst(mSerialPorts[slot]).setNull();
8218 }
8219 }
8220
8221 if (mVRDEServer)
8222 {
8223 mVRDEServer->uninit();
8224 unconst(mVRDEServer).setNull();
8225 }
8226
8227 if (mGraphicsAdapter)
8228 {
8229 mGraphicsAdapter->uninit();
8230 unconst(mGraphicsAdapter).setNull();
8231 }
8232
8233 if (mBIOSSettings)
8234 {
8235 mBIOSSettings->uninit();
8236 unconst(mBIOSSettings).setNull();
8237 }
8238
8239 if (mRecordingSettings)
8240 {
8241 mRecordingSettings->uninit();
8242 unconst(mRecordingSettings).setNull();
8243 }
8244
8245 /* Deassociate media (only when a real Machine or a SnapshotMachine
8246 * instance is uninitialized; SessionMachine instances refer to real
8247 * Machine media). This is necessary for a clean re-initialization of
8248 * the VM after successfully re-checking the accessibility state. Note
8249 * that in case of normal Machine or SnapshotMachine uninitialization (as
8250 * a result of unregistering or deleting the snapshot), outdated media
8251 * attachments will already be uninitialized and deleted, so this
8252 * code will not affect them. */
8253 if ( !mMediumAttachments.isNull()
8254 && !i_isSessionMachine()
8255 )
8256 {
8257 for (MediumAttachmentList::const_iterator
8258 it = mMediumAttachments->begin();
8259 it != mMediumAttachments->end();
8260 ++it)
8261 {
8262 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8263 if (pMedium.isNull())
8264 continue;
8265 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8266 AssertComRC(rc);
8267 }
8268 }
8269
8270 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8271 {
8272 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8273 if (mData->mFirstSnapshot)
8274 {
8275 // snapshots tree is protected by machine write lock; strictly
8276 // this isn't necessary here since we're deleting the entire
8277 // machine, but otherwise we assert in Snapshot::uninit()
8278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8279 mData->mFirstSnapshot->uninit();
8280 mData->mFirstSnapshot.setNull();
8281 }
8282
8283 mData->mCurrentSnapshot.setNull();
8284 }
8285
8286 /* free data structures (the essential mData structure is not freed here
8287 * since it may be still in use) */
8288 mMediumAttachments.free();
8289 mStorageControllers.free();
8290 mUSBControllers.free();
8291 mHWData.free();
8292 mUserData.free();
8293 mSSData.free();
8294}
8295
8296/**
8297 * Returns a pointer to the Machine object for this machine that acts like a
8298 * parent for complex machine data objects such as shared folders, etc.
8299 *
8300 * For primary Machine objects and for SnapshotMachine objects, returns this
8301 * object's pointer itself. For SessionMachine objects, returns the peer
8302 * (primary) machine pointer.
8303 */
8304Machine *Machine::i_getMachine()
8305{
8306 if (i_isSessionMachine())
8307 return (Machine*)mPeer;
8308 return this;
8309}
8310
8311/**
8312 * Makes sure that there are no machine state dependents. If necessary, waits
8313 * for the number of dependents to drop to zero.
8314 *
8315 * Make sure this method is called from under this object's write lock to
8316 * guarantee that no new dependents may be added when this method returns
8317 * control to the caller.
8318 *
8319 * @note Locks this object for writing. The lock will be released while waiting
8320 * (if necessary).
8321 *
8322 * @warning To be used only in methods that change the machine state!
8323 */
8324void Machine::i_ensureNoStateDependencies()
8325{
8326 AssertReturnVoid(isWriteLockOnCurrentThread());
8327
8328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8329
8330 /* Wait for all state dependents if necessary */
8331 if (mData->mMachineStateDeps != 0)
8332 {
8333 /* lazy semaphore creation */
8334 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8335 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8336
8337 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8338 mData->mMachineStateDeps));
8339
8340 ++mData->mMachineStateChangePending;
8341
8342 /* reset the semaphore before waiting, the last dependent will signal
8343 * it */
8344 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8345
8346 alock.release();
8347
8348 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8349
8350 alock.acquire();
8351
8352 -- mData->mMachineStateChangePending;
8353 }
8354}
8355
8356/**
8357 * Changes the machine state and informs callbacks.
8358 *
8359 * This method is not intended to fail so it either returns S_OK or asserts (and
8360 * returns a failure).
8361 *
8362 * @note Locks this object for writing.
8363 */
8364HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8365{
8366 LogFlowThisFuncEnter();
8367 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8368 Assert(aMachineState != MachineState_Null);
8369
8370 AutoCaller autoCaller(this);
8371 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8372
8373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8374
8375 /* wait for state dependents to drop to zero */
8376 i_ensureNoStateDependencies();
8377
8378 MachineState_T const enmOldState = mData->mMachineState;
8379 if (enmOldState != aMachineState)
8380 {
8381 mData->mMachineState = aMachineState;
8382 RTTimeNow(&mData->mLastStateChange);
8383
8384#ifdef VBOX_WITH_DTRACE_R3_MAIN
8385 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8386#endif
8387 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8388 }
8389
8390 LogFlowThisFuncLeave();
8391 return S_OK;
8392}
8393
8394/**
8395 * Searches for a shared folder with the given logical name
8396 * in the collection of shared folders.
8397 *
8398 * @param aName logical name of the shared folder
8399 * @param aSharedFolder where to return the found object
8400 * @param aSetError whether to set the error info if the folder is
8401 * not found
8402 * @return
8403 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8404 *
8405 * @note
8406 * must be called from under the object's lock!
8407 */
8408HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8409 ComObjPtr<SharedFolder> &aSharedFolder,
8410 bool aSetError /* = false */)
8411{
8412 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8413 for (HWData::SharedFolderList::const_iterator
8414 it = mHWData->mSharedFolders.begin();
8415 it != mHWData->mSharedFolders.end();
8416 ++it)
8417 {
8418 SharedFolder *pSF = *it;
8419 AutoCaller autoCaller(pSF);
8420 if (pSF->i_getName() == aName)
8421 {
8422 aSharedFolder = pSF;
8423 rc = S_OK;
8424 break;
8425 }
8426 }
8427
8428 if (aSetError && FAILED(rc))
8429 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8430
8431 return rc;
8432}
8433
8434/**
8435 * Initializes all machine instance data from the given settings structures
8436 * from XML. The exception is the machine UUID which needs special handling
8437 * depending on the caller's use case, so the caller needs to set that herself.
8438 *
8439 * This gets called in several contexts during machine initialization:
8440 *
8441 * -- When machine XML exists on disk already and needs to be loaded into memory,
8442 * for example, from #i_registeredInit() to load all registered machines on
8443 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8444 * attached to the machine should be part of some media registry already.
8445 *
8446 * -- During OVF import, when a machine config has been constructed from an
8447 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8448 * ensure that the media listed as attachments in the config (which have
8449 * been imported from the OVF) receive the correct registry ID.
8450 *
8451 * -- During VM cloning.
8452 *
8453 * @param config Machine settings from XML.
8454 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8455 * for each attached medium in the config.
8456 * @return
8457 */
8458HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8459 const Guid *puuidRegistry)
8460{
8461 // copy name, description, OS type, teleporter, UTC etc.
8462 mUserData->s = config.machineUserData;
8463
8464 // look up the object by Id to check it is valid
8465 ComObjPtr<GuestOSType> pGuestOSType;
8466 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8467 if (!pGuestOSType.isNull())
8468 mUserData->s.strOsType = pGuestOSType->i_id();
8469
8470 // stateFile (optional)
8471 if (config.strStateFile.isEmpty())
8472 mSSData->strStateFilePath.setNull();
8473 else
8474 {
8475 Utf8Str stateFilePathFull(config.strStateFile);
8476 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8477 if (RT_FAILURE(vrc))
8478 return setErrorBoth(E_FAIL, vrc,
8479 tr("Invalid saved state file path '%s' (%Rrc)"),
8480 config.strStateFile.c_str(),
8481 vrc);
8482 mSSData->strStateFilePath = stateFilePathFull;
8483 }
8484
8485 // snapshot folder needs special processing so set it again
8486 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8487 if (FAILED(rc)) return rc;
8488
8489 /* Copy the extra data items (config may or may not be the same as
8490 * mData->pMachineConfigFile) if necessary. When loading the XML files
8491 * from disk they are the same, but not for OVF import. */
8492 if (mData->pMachineConfigFile != &config)
8493 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8494
8495 /* currentStateModified (optional, default is true) */
8496 mData->mCurrentStateModified = config.fCurrentStateModified;
8497
8498 mData->mLastStateChange = config.timeLastStateChange;
8499
8500 /*
8501 * note: all mUserData members must be assigned prior this point because
8502 * we need to commit changes in order to let mUserData be shared by all
8503 * snapshot machine instances.
8504 */
8505 mUserData.commitCopy();
8506
8507 // machine registry, if present (must be loaded before snapshots)
8508 if (config.canHaveOwnMediaRegistry())
8509 {
8510 // determine machine folder
8511 Utf8Str strMachineFolder = i_getSettingsFileFull();
8512 strMachineFolder.stripFilename();
8513 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8514 config.mediaRegistry,
8515 strMachineFolder);
8516 if (FAILED(rc)) return rc;
8517 }
8518
8519 /* Snapshot node (optional) */
8520 size_t cRootSnapshots;
8521 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8522 {
8523 // there must be only one root snapshot
8524 Assert(cRootSnapshots == 1);
8525
8526 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8527
8528 rc = i_loadSnapshot(snap,
8529 config.uuidCurrentSnapshot,
8530 NULL); // no parent == first snapshot
8531 if (FAILED(rc)) return rc;
8532 }
8533
8534 // hardware data
8535 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8536 if (FAILED(rc)) return rc;
8537
8538 /*
8539 * NOTE: the assignment below must be the last thing to do,
8540 * otherwise it will be not possible to change the settings
8541 * somewhere in the code above because all setters will be
8542 * blocked by i_checkStateDependency(MutableStateDep).
8543 */
8544
8545 /* set the machine state to Aborted or Saved when appropriate */
8546 if (config.fAborted)
8547 {
8548 mSSData->strStateFilePath.setNull();
8549
8550 /* no need to use i_setMachineState() during init() */
8551 mData->mMachineState = MachineState_Aborted;
8552 }
8553 else if (!mSSData->strStateFilePath.isEmpty())
8554 {
8555 /* no need to use i_setMachineState() during init() */
8556 mData->mMachineState = MachineState_Saved;
8557 }
8558
8559 // after loading settings, we are no longer different from the XML on disk
8560 mData->flModifications = 0;
8561
8562 return S_OK;
8563}
8564
8565/**
8566 * Recursively loads all snapshots starting from the given.
8567 *
8568 * @param data snapshot settings.
8569 * @param aCurSnapshotId Current snapshot ID from the settings file.
8570 * @param aParentSnapshot Parent snapshot.
8571 */
8572HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8573 const Guid &aCurSnapshotId,
8574 Snapshot *aParentSnapshot)
8575{
8576 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8577 AssertReturn(!i_isSessionMachine(), E_FAIL);
8578
8579 HRESULT rc = S_OK;
8580
8581 Utf8Str strStateFile;
8582 if (!data.strStateFile.isEmpty())
8583 {
8584 /* optional */
8585 strStateFile = data.strStateFile;
8586 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8587 if (RT_FAILURE(vrc))
8588 return setErrorBoth(E_FAIL, vrc,
8589 tr("Invalid saved state file path '%s' (%Rrc)"),
8590 strStateFile.c_str(),
8591 vrc);
8592 }
8593
8594 /* create a snapshot machine object */
8595 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8596 pSnapshotMachine.createObject();
8597 rc = pSnapshotMachine->initFromSettings(this,
8598 data.hardware,
8599 &data.debugging,
8600 &data.autostart,
8601 data.uuid.ref(),
8602 strStateFile);
8603 if (FAILED(rc)) return rc;
8604
8605 /* create a snapshot object */
8606 ComObjPtr<Snapshot> pSnapshot;
8607 pSnapshot.createObject();
8608 /* initialize the snapshot */
8609 rc = pSnapshot->init(mParent, // VirtualBox object
8610 data.uuid,
8611 data.strName,
8612 data.strDescription,
8613 data.timestamp,
8614 pSnapshotMachine,
8615 aParentSnapshot);
8616 if (FAILED(rc)) return rc;
8617
8618 /* memorize the first snapshot if necessary */
8619 if (!mData->mFirstSnapshot)
8620 mData->mFirstSnapshot = pSnapshot;
8621
8622 /* memorize the current snapshot when appropriate */
8623 if ( !mData->mCurrentSnapshot
8624 && pSnapshot->i_getId() == aCurSnapshotId
8625 )
8626 mData->mCurrentSnapshot = pSnapshot;
8627
8628 // now create the children
8629 for (settings::SnapshotsList::const_iterator
8630 it = data.llChildSnapshots.begin();
8631 it != data.llChildSnapshots.end();
8632 ++it)
8633 {
8634 const settings::Snapshot &childData = *it;
8635 // recurse
8636 rc = i_loadSnapshot(childData,
8637 aCurSnapshotId,
8638 pSnapshot); // parent = the one we created above
8639 if (FAILED(rc)) return rc;
8640 }
8641
8642 return rc;
8643}
8644
8645/**
8646 * Loads settings into mHWData.
8647 *
8648 * @param puuidRegistry Registry ID.
8649 * @param puuidSnapshot Snapshot ID
8650 * @param data Reference to the hardware settings.
8651 * @param pDbg Pointer to the debugging settings.
8652 * @param pAutostart Pointer to the autostart settings.
8653 */
8654HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8655 const Guid *puuidSnapshot,
8656 const settings::Hardware &data,
8657 const settings::Debugging *pDbg,
8658 const settings::Autostart *pAutostart)
8659{
8660 AssertReturn(!i_isSessionMachine(), E_FAIL);
8661
8662 HRESULT rc = S_OK;
8663
8664 try
8665 {
8666 ComObjPtr<GuestOSType> pGuestOSType;
8667 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8668
8669 /* The hardware version attribute (optional). */
8670 mHWData->mHWVersion = data.strVersion;
8671 mHWData->mHardwareUUID = data.uuid;
8672
8673 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8674 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8675 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8676 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8677 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8678 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8679 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8680 mHWData->mPAEEnabled = data.fPAE;
8681 mHWData->mLongMode = data.enmLongMode;
8682 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8683 mHWData->mAPIC = data.fAPIC;
8684 mHWData->mX2APIC = data.fX2APIC;
8685 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8686 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8687 mHWData->mSpecCtrl = data.fSpecCtrl;
8688 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8689 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8690 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8691 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8692 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8693 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8694 mHWData->mCPUCount = data.cCPUs;
8695 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8696 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8697 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8698 mHWData->mCpuProfile = data.strCpuProfile;
8699
8700 // cpu
8701 if (mHWData->mCPUHotPlugEnabled)
8702 {
8703 for (settings::CpuList::const_iterator
8704 it = data.llCpus.begin();
8705 it != data.llCpus.end();
8706 ++it)
8707 {
8708 const settings::Cpu &cpu = *it;
8709
8710 mHWData->mCPUAttached[cpu.ulId] = true;
8711 }
8712 }
8713
8714 // cpuid leafs
8715 for (settings::CpuIdLeafsList::const_iterator
8716 it = data.llCpuIdLeafs.begin();
8717 it != data.llCpuIdLeafs.end();
8718 ++it)
8719 {
8720 const settings::CpuIdLeaf &rLeaf= *it;
8721 if ( rLeaf.idx < UINT32_C(0x20)
8722 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8723 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8724 mHWData->mCpuIdLeafList.push_back(rLeaf);
8725 /* else: just ignore */
8726 }
8727
8728 mHWData->mMemorySize = data.ulMemorySizeMB;
8729 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8730
8731 // boot order
8732 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8733 {
8734 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8735 if (it == data.mapBootOrder.end())
8736 mHWData->mBootOrder[i] = DeviceType_Null;
8737 else
8738 mHWData->mBootOrder[i] = it->second;
8739 }
8740
8741 mHWData->mFirmwareType = data.firmwareType;
8742 mHWData->mPointingHIDType = data.pointingHIDType;
8743 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8744 mHWData->mChipsetType = data.chipsetType;
8745 mHWData->mParavirtProvider = data.paravirtProvider;
8746 mHWData->mParavirtDebug = data.strParavirtDebug;
8747 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8748 mHWData->mHPETEnabled = data.fHPETEnabled;
8749
8750 /* GraphicsAdapter */
8751 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8752 if (FAILED(rc)) return rc;
8753
8754 /* VRDEServer */
8755 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8756 if (FAILED(rc)) return rc;
8757
8758 /* BIOS */
8759 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8760 if (FAILED(rc)) return rc;
8761
8762 /* Recording settings */
8763 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8764 if (FAILED(rc)) return rc;
8765
8766 // Bandwidth control (must come before network adapters)
8767 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8768 if (FAILED(rc)) return rc;
8769
8770 /* USB controllers */
8771 for (settings::USBControllerList::const_iterator
8772 it = data.usbSettings.llUSBControllers.begin();
8773 it != data.usbSettings.llUSBControllers.end();
8774 ++it)
8775 {
8776 const settings::USBController &settingsCtrl = *it;
8777 ComObjPtr<USBController> newCtrl;
8778
8779 newCtrl.createObject();
8780 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8781 mUSBControllers->push_back(newCtrl);
8782 }
8783
8784 /* USB device filters */
8785 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8786 if (FAILED(rc)) return rc;
8787
8788 // network adapters (establish array size first and apply defaults, to
8789 // ensure reading the same settings as we saved, since the list skips
8790 // adapters having defaults)
8791 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8792 size_t oldCount = mNetworkAdapters.size();
8793 if (newCount > oldCount)
8794 {
8795 mNetworkAdapters.resize(newCount);
8796 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8797 {
8798 unconst(mNetworkAdapters[slot]).createObject();
8799 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8800 }
8801 }
8802 else if (newCount < oldCount)
8803 mNetworkAdapters.resize(newCount);
8804 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8805 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8806 for (settings::NetworkAdaptersList::const_iterator
8807 it = data.llNetworkAdapters.begin();
8808 it != data.llNetworkAdapters.end();
8809 ++it)
8810 {
8811 const settings::NetworkAdapter &nic = *it;
8812
8813 /* slot uniqueness is guaranteed by XML Schema */
8814 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8815 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8816 if (FAILED(rc)) return rc;
8817 }
8818
8819 // serial ports (establish defaults first, to ensure reading the same
8820 // settings as we saved, since the list skips ports having defaults)
8821 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8822 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8823 for (settings::SerialPortsList::const_iterator
8824 it = data.llSerialPorts.begin();
8825 it != data.llSerialPorts.end();
8826 ++it)
8827 {
8828 const settings::SerialPort &s = *it;
8829
8830 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8831 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8832 if (FAILED(rc)) return rc;
8833 }
8834
8835 // parallel ports (establish defaults first, to ensure reading the same
8836 // settings as we saved, since the list skips ports having defaults)
8837 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8838 mParallelPorts[i]->i_applyDefaults();
8839 for (settings::ParallelPortsList::const_iterator
8840 it = data.llParallelPorts.begin();
8841 it != data.llParallelPorts.end();
8842 ++it)
8843 {
8844 const settings::ParallelPort &p = *it;
8845
8846 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8847 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8848 if (FAILED(rc)) return rc;
8849 }
8850
8851 /* AudioAdapter */
8852 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8853 if (FAILED(rc)) return rc;
8854
8855 /* storage controllers */
8856 rc = i_loadStorageControllers(data.storage,
8857 puuidRegistry,
8858 puuidSnapshot);
8859 if (FAILED(rc)) return rc;
8860
8861 /* Shared folders */
8862 for (settings::SharedFoldersList::const_iterator
8863 it = data.llSharedFolders.begin();
8864 it != data.llSharedFolders.end();
8865 ++it)
8866 {
8867 const settings::SharedFolder &sf = *it;
8868
8869 ComObjPtr<SharedFolder> sharedFolder;
8870 /* Check for double entries. Not allowed! */
8871 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8872 if (SUCCEEDED(rc))
8873 return setError(VBOX_E_OBJECT_IN_USE,
8874 tr("Shared folder named '%s' already exists"),
8875 sf.strName.c_str());
8876
8877 /* Create the new shared folder. Don't break on error. This will be
8878 * reported when the machine starts. */
8879 sharedFolder.createObject();
8880 rc = sharedFolder->init(i_getMachine(),
8881 sf.strName,
8882 sf.strHostPath,
8883 RT_BOOL(sf.fWritable),
8884 RT_BOOL(sf.fAutoMount),
8885 sf.strAutoMountPoint,
8886 false /* fFailOnError */);
8887 if (FAILED(rc)) return rc;
8888 mHWData->mSharedFolders.push_back(sharedFolder);
8889 }
8890
8891 // Clipboard
8892 mHWData->mClipboardMode = data.clipboardMode;
8893 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8894
8895 // drag'n'drop
8896 mHWData->mDnDMode = data.dndMode;
8897
8898 // guest settings
8899 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8900
8901 // IO settings
8902 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8903 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8904
8905 // Host PCI devices
8906 for (settings::HostPCIDeviceAttachmentList::const_iterator
8907 it = data.pciAttachments.begin();
8908 it != data.pciAttachments.end();
8909 ++it)
8910 {
8911 const settings::HostPCIDeviceAttachment &hpda = *it;
8912 ComObjPtr<PCIDeviceAttachment> pda;
8913
8914 pda.createObject();
8915 pda->i_loadSettings(this, hpda);
8916 mHWData->mPCIDeviceAssignments.push_back(pda);
8917 }
8918
8919 /*
8920 * (The following isn't really real hardware, but it lives in HWData
8921 * for reasons of convenience.)
8922 */
8923
8924#ifdef VBOX_WITH_GUEST_PROPS
8925 /* Guest properties (optional) */
8926
8927 /* Only load transient guest properties for configs which have saved
8928 * state, because there shouldn't be any for powered off VMs. The same
8929 * logic applies for snapshots, as offline snapshots shouldn't have
8930 * any such properties. They confuse the code in various places.
8931 * Note: can't rely on the machine state, as it isn't set yet. */
8932 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8933 /* apologies for the hacky unconst() usage, but this needs hacking
8934 * actually inconsistent settings into consistency, otherwise there
8935 * will be some corner cases where the inconsistency survives
8936 * surprisingly long without getting fixed, especially for snapshots
8937 * as there are no config changes. */
8938 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8939 for (settings::GuestPropertiesList::iterator
8940 it = llGuestProperties.begin();
8941 it != llGuestProperties.end();
8942 /*nothing*/)
8943 {
8944 const settings::GuestProperty &prop = *it;
8945 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8946 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8947 if ( fSkipTransientGuestProperties
8948 && ( fFlags & GUEST_PROP_F_TRANSIENT
8949 || fFlags & GUEST_PROP_F_TRANSRESET))
8950 {
8951 it = llGuestProperties.erase(it);
8952 continue;
8953 }
8954 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8955 mHWData->mGuestProperties[prop.strName] = property;
8956 ++it;
8957 }
8958#endif /* VBOX_WITH_GUEST_PROPS defined */
8959
8960 rc = i_loadDebugging(pDbg);
8961 if (FAILED(rc))
8962 return rc;
8963
8964 mHWData->mAutostart = *pAutostart;
8965
8966 /* default frontend */
8967 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8968 }
8969 catch (std::bad_alloc &)
8970 {
8971 return E_OUTOFMEMORY;
8972 }
8973
8974 AssertComRC(rc);
8975 return rc;
8976}
8977
8978/**
8979 * Called from i_loadHardware() to load the debugging settings of the
8980 * machine.
8981 *
8982 * @param pDbg Pointer to the settings.
8983 */
8984HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8985{
8986 mHWData->mDebugging = *pDbg;
8987 /* no more processing currently required, this will probably change. */
8988 return S_OK;
8989}
8990
8991/**
8992 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8993 *
8994 * @param data storage settings.
8995 * @param puuidRegistry media registry ID to set media to or NULL;
8996 * see Machine::i_loadMachineDataFromSettings()
8997 * @param puuidSnapshot snapshot ID
8998 * @return
8999 */
9000HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9001 const Guid *puuidRegistry,
9002 const Guid *puuidSnapshot)
9003{
9004 AssertReturn(!i_isSessionMachine(), E_FAIL);
9005
9006 HRESULT rc = S_OK;
9007
9008 for (settings::StorageControllersList::const_iterator
9009 it = data.llStorageControllers.begin();
9010 it != data.llStorageControllers.end();
9011 ++it)
9012 {
9013 const settings::StorageController &ctlData = *it;
9014
9015 ComObjPtr<StorageController> pCtl;
9016 /* Try to find one with the name first. */
9017 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9018 if (SUCCEEDED(rc))
9019 return setError(VBOX_E_OBJECT_IN_USE,
9020 tr("Storage controller named '%s' already exists"),
9021 ctlData.strName.c_str());
9022
9023 pCtl.createObject();
9024 rc = pCtl->init(this,
9025 ctlData.strName,
9026 ctlData.storageBus,
9027 ctlData.ulInstance,
9028 ctlData.fBootable);
9029 if (FAILED(rc)) return rc;
9030
9031 mStorageControllers->push_back(pCtl);
9032
9033 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9034 if (FAILED(rc)) return rc;
9035
9036 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9037 if (FAILED(rc)) return rc;
9038
9039 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9040 if (FAILED(rc)) return rc;
9041
9042 /* Load the attached devices now. */
9043 rc = i_loadStorageDevices(pCtl,
9044 ctlData,
9045 puuidRegistry,
9046 puuidSnapshot);
9047 if (FAILED(rc)) return rc;
9048 }
9049
9050 return S_OK;
9051}
9052
9053/**
9054 * Called from i_loadStorageControllers for a controller's devices.
9055 *
9056 * @param aStorageController
9057 * @param data
9058 * @param puuidRegistry media registry ID to set media to or NULL; see
9059 * Machine::i_loadMachineDataFromSettings()
9060 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9061 * @return
9062 */
9063HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9064 const settings::StorageController &data,
9065 const Guid *puuidRegistry,
9066 const Guid *puuidSnapshot)
9067{
9068 HRESULT rc = S_OK;
9069
9070 /* paranoia: detect duplicate attachments */
9071 for (settings::AttachedDevicesList::const_iterator
9072 it = data.llAttachedDevices.begin();
9073 it != data.llAttachedDevices.end();
9074 ++it)
9075 {
9076 const settings::AttachedDevice &ad = *it;
9077
9078 for (settings::AttachedDevicesList::const_iterator it2 = it;
9079 it2 != data.llAttachedDevices.end();
9080 ++it2)
9081 {
9082 if (it == it2)
9083 continue;
9084
9085 const settings::AttachedDevice &ad2 = *it2;
9086
9087 if ( ad.lPort == ad2.lPort
9088 && ad.lDevice == ad2.lDevice)
9089 {
9090 return setError(E_FAIL,
9091 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9092 aStorageController->i_getName().c_str(),
9093 ad.lPort,
9094 ad.lDevice,
9095 mUserData->s.strName.c_str());
9096 }
9097 }
9098 }
9099
9100 for (settings::AttachedDevicesList::const_iterator
9101 it = data.llAttachedDevices.begin();
9102 it != data.llAttachedDevices.end();
9103 ++it)
9104 {
9105 const settings::AttachedDevice &dev = *it;
9106 ComObjPtr<Medium> medium;
9107
9108 switch (dev.deviceType)
9109 {
9110 case DeviceType_Floppy:
9111 case DeviceType_DVD:
9112 if (dev.strHostDriveSrc.isNotEmpty())
9113 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9114 false /* fRefresh */, medium);
9115 else
9116 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9117 dev.uuid,
9118 false /* fRefresh */,
9119 false /* aSetError */,
9120 medium);
9121 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9122 // This is not an error. The host drive or UUID might have vanished, so just go
9123 // ahead without this removeable medium attachment
9124 rc = S_OK;
9125 break;
9126
9127 case DeviceType_HardDisk:
9128 {
9129 /* find a hard disk by UUID */
9130 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9131 if (FAILED(rc))
9132 {
9133 if (i_isSnapshotMachine())
9134 {
9135 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9136 // so the user knows that the bad disk is in a snapshot somewhere
9137 com::ErrorInfo info;
9138 return setError(E_FAIL,
9139 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9140 puuidSnapshot->raw(),
9141 info.getText().raw());
9142 }
9143 else
9144 return rc;
9145 }
9146
9147 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9148
9149 if (medium->i_getType() == MediumType_Immutable)
9150 {
9151 if (i_isSnapshotMachine())
9152 return setError(E_FAIL,
9153 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9154 "of the virtual machine '%s' ('%s')"),
9155 medium->i_getLocationFull().c_str(),
9156 dev.uuid.raw(),
9157 puuidSnapshot->raw(),
9158 mUserData->s.strName.c_str(),
9159 mData->m_strConfigFileFull.c_str());
9160
9161 return setError(E_FAIL,
9162 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9163 medium->i_getLocationFull().c_str(),
9164 dev.uuid.raw(),
9165 mUserData->s.strName.c_str(),
9166 mData->m_strConfigFileFull.c_str());
9167 }
9168
9169 if (medium->i_getType() == MediumType_MultiAttach)
9170 {
9171 if (i_isSnapshotMachine())
9172 return setError(E_FAIL,
9173 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9174 "of the virtual machine '%s' ('%s')"),
9175 medium->i_getLocationFull().c_str(),
9176 dev.uuid.raw(),
9177 puuidSnapshot->raw(),
9178 mUserData->s.strName.c_str(),
9179 mData->m_strConfigFileFull.c_str());
9180
9181 return setError(E_FAIL,
9182 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9183 medium->i_getLocationFull().c_str(),
9184 dev.uuid.raw(),
9185 mUserData->s.strName.c_str(),
9186 mData->m_strConfigFileFull.c_str());
9187 }
9188
9189 if ( !i_isSnapshotMachine()
9190 && medium->i_getChildren().size() != 0
9191 )
9192 return setError(E_FAIL,
9193 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9194 "because it has %d differencing child hard disks"),
9195 medium->i_getLocationFull().c_str(),
9196 dev.uuid.raw(),
9197 mUserData->s.strName.c_str(),
9198 mData->m_strConfigFileFull.c_str(),
9199 medium->i_getChildren().size());
9200
9201 if (i_findAttachment(*mMediumAttachments.data(),
9202 medium))
9203 return setError(E_FAIL,
9204 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9205 medium->i_getLocationFull().c_str(),
9206 dev.uuid.raw(),
9207 mUserData->s.strName.c_str(),
9208 mData->m_strConfigFileFull.c_str());
9209
9210 break;
9211 }
9212
9213 default:
9214 return setError(E_FAIL,
9215 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9216 medium->i_getLocationFull().c_str(),
9217 mUserData->s.strName.c_str(),
9218 mData->m_strConfigFileFull.c_str());
9219 }
9220
9221 if (FAILED(rc))
9222 break;
9223
9224 /* Bandwidth groups are loaded at this point. */
9225 ComObjPtr<BandwidthGroup> pBwGroup;
9226
9227 if (!dev.strBwGroup.isEmpty())
9228 {
9229 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9230 if (FAILED(rc))
9231 return setError(E_FAIL,
9232 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9233 medium->i_getLocationFull().c_str(),
9234 dev.strBwGroup.c_str(),
9235 mUserData->s.strName.c_str(),
9236 mData->m_strConfigFileFull.c_str());
9237 pBwGroup->i_reference();
9238 }
9239
9240 const Utf8Str controllerName = aStorageController->i_getName();
9241 ComObjPtr<MediumAttachment> pAttachment;
9242 pAttachment.createObject();
9243 rc = pAttachment->init(this,
9244 medium,
9245 controllerName,
9246 dev.lPort,
9247 dev.lDevice,
9248 dev.deviceType,
9249 false,
9250 dev.fPassThrough,
9251 dev.fTempEject,
9252 dev.fNonRotational,
9253 dev.fDiscard,
9254 dev.fHotPluggable,
9255 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9256 if (FAILED(rc)) break;
9257
9258 /* associate the medium with this machine and snapshot */
9259 if (!medium.isNull())
9260 {
9261 AutoCaller medCaller(medium);
9262 if (FAILED(medCaller.rc())) return medCaller.rc();
9263 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9264
9265 if (i_isSnapshotMachine())
9266 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9267 else
9268 rc = medium->i_addBackReference(mData->mUuid);
9269 /* If the medium->addBackReference fails it sets an appropriate
9270 * error message, so no need to do any guesswork here. */
9271
9272 if (puuidRegistry)
9273 // caller wants registry ID to be set on all attached media (OVF import case)
9274 medium->i_addRegistry(*puuidRegistry);
9275 }
9276
9277 if (FAILED(rc))
9278 break;
9279
9280 /* back up mMediumAttachments to let registeredInit() properly rollback
9281 * on failure (= limited accessibility) */
9282 i_setModified(IsModified_Storage);
9283 mMediumAttachments.backup();
9284 mMediumAttachments->push_back(pAttachment);
9285 }
9286
9287 return rc;
9288}
9289
9290/**
9291 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9292 *
9293 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9294 * @param aSnapshot where to return the found snapshot
9295 * @param aSetError true to set extended error info on failure
9296 */
9297HRESULT Machine::i_findSnapshotById(const Guid &aId,
9298 ComObjPtr<Snapshot> &aSnapshot,
9299 bool aSetError /* = false */)
9300{
9301 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9302
9303 if (!mData->mFirstSnapshot)
9304 {
9305 if (aSetError)
9306 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9307 return E_FAIL;
9308 }
9309
9310 if (aId.isZero())
9311 aSnapshot = mData->mFirstSnapshot;
9312 else
9313 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9314
9315 if (!aSnapshot)
9316 {
9317 if (aSetError)
9318 return setError(E_FAIL,
9319 tr("Could not find a snapshot with UUID {%s}"),
9320 aId.toString().c_str());
9321 return E_FAIL;
9322 }
9323
9324 return S_OK;
9325}
9326
9327/**
9328 * Returns the snapshot with the given name or fails of no such snapshot.
9329 *
9330 * @param strName snapshot name to find
9331 * @param aSnapshot where to return the found snapshot
9332 * @param aSetError true to set extended error info on failure
9333 */
9334HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9335 ComObjPtr<Snapshot> &aSnapshot,
9336 bool aSetError /* = false */)
9337{
9338 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9339
9340 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9341
9342 if (!mData->mFirstSnapshot)
9343 {
9344 if (aSetError)
9345 return setError(VBOX_E_OBJECT_NOT_FOUND,
9346 tr("This machine does not have any snapshots"));
9347 return VBOX_E_OBJECT_NOT_FOUND;
9348 }
9349
9350 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9351
9352 if (!aSnapshot)
9353 {
9354 if (aSetError)
9355 return setError(VBOX_E_OBJECT_NOT_FOUND,
9356 tr("Could not find a snapshot named '%s'"), strName.c_str());
9357 return VBOX_E_OBJECT_NOT_FOUND;
9358 }
9359
9360 return S_OK;
9361}
9362
9363/**
9364 * Returns a storage controller object with the given name.
9365 *
9366 * @param aName storage controller name to find
9367 * @param aStorageController where to return the found storage controller
9368 * @param aSetError true to set extended error info on failure
9369 */
9370HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9371 ComObjPtr<StorageController> &aStorageController,
9372 bool aSetError /* = false */)
9373{
9374 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9375
9376 for (StorageControllerList::const_iterator
9377 it = mStorageControllers->begin();
9378 it != mStorageControllers->end();
9379 ++it)
9380 {
9381 if ((*it)->i_getName() == aName)
9382 {
9383 aStorageController = (*it);
9384 return S_OK;
9385 }
9386 }
9387
9388 if (aSetError)
9389 return setError(VBOX_E_OBJECT_NOT_FOUND,
9390 tr("Could not find a storage controller named '%s'"),
9391 aName.c_str());
9392 return VBOX_E_OBJECT_NOT_FOUND;
9393}
9394
9395/**
9396 * Returns a USB controller object with the given name.
9397 *
9398 * @param aName USB controller name to find
9399 * @param aUSBController where to return the found USB controller
9400 * @param aSetError true to set extended error info on failure
9401 */
9402HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9403 ComObjPtr<USBController> &aUSBController,
9404 bool aSetError /* = false */)
9405{
9406 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9407
9408 for (USBControllerList::const_iterator
9409 it = mUSBControllers->begin();
9410 it != mUSBControllers->end();
9411 ++it)
9412 {
9413 if ((*it)->i_getName() == aName)
9414 {
9415 aUSBController = (*it);
9416 return S_OK;
9417 }
9418 }
9419
9420 if (aSetError)
9421 return setError(VBOX_E_OBJECT_NOT_FOUND,
9422 tr("Could not find a storage controller named '%s'"),
9423 aName.c_str());
9424 return VBOX_E_OBJECT_NOT_FOUND;
9425}
9426
9427/**
9428 * Returns the number of USB controller instance of the given type.
9429 *
9430 * @param enmType USB controller type.
9431 */
9432ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9433{
9434 ULONG cCtrls = 0;
9435
9436 for (USBControllerList::const_iterator
9437 it = mUSBControllers->begin();
9438 it != mUSBControllers->end();
9439 ++it)
9440 {
9441 if ((*it)->i_getControllerType() == enmType)
9442 cCtrls++;
9443 }
9444
9445 return cCtrls;
9446}
9447
9448HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9449 MediumAttachmentList &atts)
9450{
9451 AutoCaller autoCaller(this);
9452 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9453
9454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9455
9456 for (MediumAttachmentList::const_iterator
9457 it = mMediumAttachments->begin();
9458 it != mMediumAttachments->end();
9459 ++it)
9460 {
9461 const ComObjPtr<MediumAttachment> &pAtt = *it;
9462 // should never happen, but deal with NULL pointers in the list.
9463 AssertContinue(!pAtt.isNull());
9464
9465 // getControllerName() needs caller+read lock
9466 AutoCaller autoAttCaller(pAtt);
9467 if (FAILED(autoAttCaller.rc()))
9468 {
9469 atts.clear();
9470 return autoAttCaller.rc();
9471 }
9472 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9473
9474 if (pAtt->i_getControllerName() == aName)
9475 atts.push_back(pAtt);
9476 }
9477
9478 return S_OK;
9479}
9480
9481
9482/**
9483 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9484 * file if the machine name was changed and about creating a new settings file
9485 * if this is a new machine.
9486 *
9487 * @note Must be never called directly but only from #saveSettings().
9488 */
9489HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9490{
9491 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9492
9493 HRESULT rc = S_OK;
9494
9495 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9496
9497 /// @todo need to handle primary group change, too
9498
9499 /* attempt to rename the settings file if machine name is changed */
9500 if ( mUserData->s.fNameSync
9501 && mUserData.isBackedUp()
9502 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9503 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9504 )
9505 {
9506 bool dirRenamed = false;
9507 bool fileRenamed = false;
9508
9509 Utf8Str configFile, newConfigFile;
9510 Utf8Str configFilePrev, newConfigFilePrev;
9511 Utf8Str NVRAMFile, newNVRAMFile;
9512 Utf8Str configDir, newConfigDir;
9513
9514 do
9515 {
9516 int vrc = VINF_SUCCESS;
9517
9518 Utf8Str name = mUserData.backedUpData()->s.strName;
9519 Utf8Str newName = mUserData->s.strName;
9520 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9521 if (group == "/")
9522 group.setNull();
9523 Utf8Str newGroup = mUserData->s.llGroups.front();
9524 if (newGroup == "/")
9525 newGroup.setNull();
9526
9527 configFile = mData->m_strConfigFileFull;
9528
9529 /* first, rename the directory if it matches the group and machine name */
9530 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9531 /** @todo hack, make somehow use of ComposeMachineFilename */
9532 if (mUserData->s.fDirectoryIncludesUUID)
9533 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9534 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9535 /** @todo hack, make somehow use of ComposeMachineFilename */
9536 if (mUserData->s.fDirectoryIncludesUUID)
9537 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9538 configDir = configFile;
9539 configDir.stripFilename();
9540 newConfigDir = configDir;
9541 if ( configDir.length() >= groupPlusName.length()
9542 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9543 groupPlusName.c_str()))
9544 {
9545 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9546 Utf8Str newConfigBaseDir(newConfigDir);
9547 newConfigDir.append(newGroupPlusName);
9548 /* consistency: use \ if appropriate on the platform */
9549 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9550 /* new dir and old dir cannot be equal here because of 'if'
9551 * above and because name != newName */
9552 Assert(configDir != newConfigDir);
9553 if (!fSettingsFileIsNew)
9554 {
9555 /* perform real rename only if the machine is not new */
9556 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9557 if ( vrc == VERR_FILE_NOT_FOUND
9558 || vrc == VERR_PATH_NOT_FOUND)
9559 {
9560 /* create the parent directory, then retry renaming */
9561 Utf8Str parent(newConfigDir);
9562 parent.stripFilename();
9563 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9564 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9565 }
9566 if (RT_FAILURE(vrc))
9567 {
9568 rc = setErrorBoth(E_FAIL, vrc,
9569 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9570 configDir.c_str(),
9571 newConfigDir.c_str(),
9572 vrc);
9573 break;
9574 }
9575 /* delete subdirectories which are no longer needed */
9576 Utf8Str dir(configDir);
9577 dir.stripFilename();
9578 while (dir != newConfigBaseDir && dir != ".")
9579 {
9580 vrc = RTDirRemove(dir.c_str());
9581 if (RT_FAILURE(vrc))
9582 break;
9583 dir.stripFilename();
9584 }
9585 dirRenamed = true;
9586 }
9587 }
9588
9589 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9590
9591 /* then try to rename the settings file itself */
9592 if (newConfigFile != configFile)
9593 {
9594 /* get the path to old settings file in renamed directory */
9595 configFile.printf("%s%c%s",
9596 newConfigDir.c_str(),
9597 RTPATH_DELIMITER,
9598 RTPathFilename(configFile.c_str()));
9599 if (!fSettingsFileIsNew)
9600 {
9601 /* perform real rename only if the machine is not new */
9602 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9603 if (RT_FAILURE(vrc))
9604 {
9605 rc = setErrorBoth(E_FAIL, vrc,
9606 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9607 configFile.c_str(),
9608 newConfigFile.c_str(),
9609 vrc);
9610 break;
9611 }
9612 fileRenamed = true;
9613 configFilePrev = configFile;
9614 configFilePrev += "-prev";
9615 newConfigFilePrev = newConfigFile;
9616 newConfigFilePrev += "-prev";
9617 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9618 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9619 if (NVRAMFile.isNotEmpty())
9620 {
9621 // in the NVRAM file path, replace the old directory with the new directory
9622 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9623 {
9624 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9625 NVRAMFile = newConfigDir + strNVRAMFile;
9626 }
9627 newNVRAMFile = newConfigFile;
9628 newNVRAMFile.stripSuffix();
9629 newNVRAMFile += ".nvram";
9630 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9631 }
9632 }
9633 }
9634
9635 // update m_strConfigFileFull amd mConfigFile
9636 mData->m_strConfigFileFull = newConfigFile;
9637 // compute the relative path too
9638 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9639
9640 // store the old and new so that VirtualBox::i_saveSettings() can update
9641 // the media registry
9642 if ( mData->mRegistered
9643 && (configDir != newConfigDir || configFile != newConfigFile))
9644 {
9645 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9646
9647 if (pfNeedsGlobalSaveSettings)
9648 *pfNeedsGlobalSaveSettings = true;
9649 }
9650
9651 // in the saved state file path, replace the old directory with the new directory
9652 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9653 {
9654 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9655 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9656 }
9657 if (newNVRAMFile.isNotEmpty())
9658 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9659
9660 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9661 if (mData->mFirstSnapshot)
9662 {
9663 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9664 newConfigDir.c_str());
9665 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9666 newConfigDir.c_str());
9667 }
9668 }
9669 while (0);
9670
9671 if (FAILED(rc))
9672 {
9673 /* silently try to rename everything back */
9674 if (fileRenamed)
9675 {
9676 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9677 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9678 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9679 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9680 }
9681 if (dirRenamed)
9682 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9683 }
9684
9685 if (FAILED(rc)) return rc;
9686 }
9687
9688 if (fSettingsFileIsNew)
9689 {
9690 /* create a virgin config file */
9691 int vrc = VINF_SUCCESS;
9692
9693 /* ensure the settings directory exists */
9694 Utf8Str path(mData->m_strConfigFileFull);
9695 path.stripFilename();
9696 if (!RTDirExists(path.c_str()))
9697 {
9698 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9699 if (RT_FAILURE(vrc))
9700 {
9701 return setErrorBoth(E_FAIL, vrc,
9702 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9703 path.c_str(),
9704 vrc);
9705 }
9706 }
9707
9708 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9709 path = Utf8Str(mData->m_strConfigFileFull);
9710 RTFILE f = NIL_RTFILE;
9711 vrc = RTFileOpen(&f, path.c_str(),
9712 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9713 if (RT_FAILURE(vrc))
9714 return setErrorBoth(E_FAIL, vrc,
9715 tr("Could not create the settings file '%s' (%Rrc)"),
9716 path.c_str(),
9717 vrc);
9718 RTFileClose(f);
9719 }
9720
9721 return rc;
9722}
9723
9724/**
9725 * Saves and commits machine data, user data and hardware data.
9726 *
9727 * Note that on failure, the data remains uncommitted.
9728 *
9729 * @a aFlags may combine the following flags:
9730 *
9731 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9732 * Used when saving settings after an operation that makes them 100%
9733 * correspond to the settings from the current snapshot.
9734 * - SaveS_Force: settings will be saved without doing a deep compare of the
9735 * settings structures. This is used when this is called because snapshots
9736 * have changed to avoid the overhead of the deep compare.
9737 *
9738 * @note Must be called from under this object's write lock. Locks children for
9739 * writing.
9740 *
9741 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9742 * initialized to false and that will be set to true by this function if
9743 * the caller must invoke VirtualBox::i_saveSettings() because the global
9744 * settings have changed. This will happen if a machine rename has been
9745 * saved and the global machine and media registries will therefore need
9746 * updating.
9747 * @param aFlags Flags.
9748 */
9749HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9750 int aFlags /*= 0*/)
9751{
9752 LogFlowThisFuncEnter();
9753
9754 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9755
9756 /* make sure child objects are unable to modify the settings while we are
9757 * saving them */
9758 i_ensureNoStateDependencies();
9759
9760 AssertReturn(!i_isSnapshotMachine(),
9761 E_FAIL);
9762
9763 if (!mData->mAccessible)
9764 return setError(VBOX_E_INVALID_VM_STATE,
9765 tr("The machine is not accessible, so cannot save settings"));
9766
9767 HRESULT rc = S_OK;
9768 bool fNeedsWrite = false;
9769
9770 /* First, prepare to save settings. It will care about renaming the
9771 * settings directory and file if the machine name was changed and about
9772 * creating a new settings file if this is a new machine. */
9773 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9774 if (FAILED(rc)) return rc;
9775
9776 // keep a pointer to the current settings structures
9777 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9778 settings::MachineConfigFile *pNewConfig = NULL;
9779
9780 try
9781 {
9782 // make a fresh one to have everyone write stuff into
9783 pNewConfig = new settings::MachineConfigFile(NULL);
9784 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9785
9786 // now go and copy all the settings data from COM to the settings structures
9787 // (this calls i_saveSettings() on all the COM objects in the machine)
9788 i_copyMachineDataToSettings(*pNewConfig);
9789
9790 if (aFlags & SaveS_ResetCurStateModified)
9791 {
9792 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9793 mData->mCurrentStateModified = FALSE;
9794 fNeedsWrite = true; // always, no need to compare
9795 }
9796 else if (aFlags & SaveS_Force)
9797 {
9798 fNeedsWrite = true; // always, no need to compare
9799 }
9800 else
9801 {
9802 if (!mData->mCurrentStateModified)
9803 {
9804 // do a deep compare of the settings that we just saved with the settings
9805 // previously stored in the config file; this invokes MachineConfigFile::operator==
9806 // which does a deep compare of all the settings, which is expensive but less expensive
9807 // than writing out XML in vain
9808 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9809
9810 // could still be modified if any settings changed
9811 mData->mCurrentStateModified = fAnySettingsChanged;
9812
9813 fNeedsWrite = fAnySettingsChanged;
9814 }
9815 else
9816 fNeedsWrite = true;
9817 }
9818
9819 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9820
9821 if (fNeedsWrite)
9822 // now spit it all out!
9823 pNewConfig->write(mData->m_strConfigFileFull);
9824
9825 mData->pMachineConfigFile = pNewConfig;
9826 delete pOldConfig;
9827 i_commit();
9828
9829 // after saving settings, we are no longer different from the XML on disk
9830 mData->flModifications = 0;
9831 }
9832 catch (HRESULT err)
9833 {
9834 // we assume that error info is set by the thrower
9835 rc = err;
9836
9837 // restore old config
9838 delete pNewConfig;
9839 mData->pMachineConfigFile = pOldConfig;
9840 }
9841 catch (...)
9842 {
9843 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9844 }
9845
9846 if (fNeedsWrite)
9847 {
9848 /* Fire the data change event, even on failure (since we've already
9849 * committed all data). This is done only for SessionMachines because
9850 * mutable Machine instances are always not registered (i.e. private
9851 * to the client process that creates them) and thus don't need to
9852 * inform callbacks. */
9853 if (i_isSessionMachine())
9854 mParent->i_onMachineDataChange(mData->mUuid);
9855 }
9856
9857 LogFlowThisFunc(("rc=%08X\n", rc));
9858 LogFlowThisFuncLeave();
9859 return rc;
9860}
9861
9862/**
9863 * Implementation for saving the machine settings into the given
9864 * settings::MachineConfigFile instance. This copies machine extradata
9865 * from the previous machine config file in the instance data, if any.
9866 *
9867 * This gets called from two locations:
9868 *
9869 * -- Machine::i_saveSettings(), during the regular XML writing;
9870 *
9871 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9872 * exported to OVF and we write the VirtualBox proprietary XML
9873 * into a <vbox:Machine> tag.
9874 *
9875 * This routine fills all the fields in there, including snapshots, *except*
9876 * for the following:
9877 *
9878 * -- fCurrentStateModified. There is some special logic associated with that.
9879 *
9880 * The caller can then call MachineConfigFile::write() or do something else
9881 * with it.
9882 *
9883 * Caller must hold the machine lock!
9884 *
9885 * This throws XML errors and HRESULT, so the caller must have a catch block!
9886 */
9887void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9888{
9889 // deep copy extradata, being extra careful with self assignment (the STL
9890 // map assignment on Mac OS X clang based Xcode isn't checking)
9891 if (&config != mData->pMachineConfigFile)
9892 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9893
9894 config.uuid = mData->mUuid;
9895
9896 // copy name, description, OS type, teleport, UTC etc.
9897 config.machineUserData = mUserData->s;
9898
9899 if ( mData->mMachineState == MachineState_Saved
9900 || mData->mMachineState == MachineState_Restoring
9901 // when doing certain snapshot operations we may or may not have
9902 // a saved state in the current state, so keep everything as is
9903 || ( ( mData->mMachineState == MachineState_Snapshotting
9904 || mData->mMachineState == MachineState_DeletingSnapshot
9905 || mData->mMachineState == MachineState_RestoringSnapshot)
9906 && (!mSSData->strStateFilePath.isEmpty())
9907 )
9908 )
9909 {
9910 Assert(!mSSData->strStateFilePath.isEmpty());
9911 /* try to make the file name relative to the settings file dir */
9912 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9913 }
9914 else
9915 {
9916 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9917 config.strStateFile.setNull();
9918 }
9919
9920 if (mData->mCurrentSnapshot)
9921 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9922 else
9923 config.uuidCurrentSnapshot.clear();
9924
9925 config.timeLastStateChange = mData->mLastStateChange;
9926 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9927 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9928
9929 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9930 if (FAILED(rc)) throw rc;
9931
9932 // save machine's media registry if this is VirtualBox 4.0 or later
9933 if (config.canHaveOwnMediaRegistry())
9934 {
9935 // determine machine folder
9936 Utf8Str strMachineFolder = i_getSettingsFileFull();
9937 strMachineFolder.stripFilename();
9938 mParent->i_saveMediaRegistry(config.mediaRegistry,
9939 i_getId(), // only media with registry ID == machine UUID
9940 strMachineFolder);
9941 // this throws HRESULT
9942 }
9943
9944 // save snapshots
9945 rc = i_saveAllSnapshots(config);
9946 if (FAILED(rc)) throw rc;
9947}
9948
9949/**
9950 * Saves all snapshots of the machine into the given machine config file. Called
9951 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9952 * @param config
9953 * @return
9954 */
9955HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9956{
9957 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9958
9959 HRESULT rc = S_OK;
9960
9961 try
9962 {
9963 config.llFirstSnapshot.clear();
9964
9965 if (mData->mFirstSnapshot)
9966 {
9967 // the settings use a list for "the first snapshot"
9968 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9969
9970 // get reference to the snapshot on the list and work on that
9971 // element straight in the list to avoid excessive copying later
9972 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9973 if (FAILED(rc)) throw rc;
9974 }
9975
9976// if (mType == IsSessionMachine)
9977// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9978
9979 }
9980 catch (HRESULT err)
9981 {
9982 /* we assume that error info is set by the thrower */
9983 rc = err;
9984 }
9985 catch (...)
9986 {
9987 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9988 }
9989
9990 return rc;
9991}
9992
9993/**
9994 * Saves the VM hardware configuration. It is assumed that the
9995 * given node is empty.
9996 *
9997 * @param data Reference to the settings object for the hardware config.
9998 * @param pDbg Pointer to the settings object for the debugging config
9999 * which happens to live in mHWData.
10000 * @param pAutostart Pointer to the settings object for the autostart config
10001 * which happens to live in mHWData.
10002 */
10003HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10004 settings::Autostart *pAutostart)
10005{
10006 HRESULT rc = S_OK;
10007
10008 try
10009 {
10010 /* The hardware version attribute (optional).
10011 Automatically upgrade from 1 to current default hardware version
10012 when there is no saved state. (ugly!) */
10013 if ( mHWData->mHWVersion == "1"
10014 && mSSData->strStateFilePath.isEmpty()
10015 )
10016 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10017
10018 data.strVersion = mHWData->mHWVersion;
10019 data.uuid = mHWData->mHardwareUUID;
10020
10021 // CPU
10022 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10023 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10024 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10025 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10026 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10027 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10028 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10029 data.fPAE = !!mHWData->mPAEEnabled;
10030 data.enmLongMode = mHWData->mLongMode;
10031 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10032 data.fAPIC = !!mHWData->mAPIC;
10033 data.fX2APIC = !!mHWData->mX2APIC;
10034 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10035 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10036 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10037 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10038 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10039 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10040 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10041 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10042 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10043 data.cCPUs = mHWData->mCPUCount;
10044 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10045 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10046 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10047 data.strCpuProfile = mHWData->mCpuProfile;
10048
10049 data.llCpus.clear();
10050 if (data.fCpuHotPlug)
10051 {
10052 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10053 {
10054 if (mHWData->mCPUAttached[idx])
10055 {
10056 settings::Cpu cpu;
10057 cpu.ulId = idx;
10058 data.llCpus.push_back(cpu);
10059 }
10060 }
10061 }
10062
10063 /* Standard and Extended CPUID leafs. */
10064 data.llCpuIdLeafs.clear();
10065 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10066
10067 // memory
10068 data.ulMemorySizeMB = mHWData->mMemorySize;
10069 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10070
10071 // firmware
10072 data.firmwareType = mHWData->mFirmwareType;
10073
10074 // HID
10075 data.pointingHIDType = mHWData->mPointingHIDType;
10076 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10077
10078 // chipset
10079 data.chipsetType = mHWData->mChipsetType;
10080
10081 // paravirt
10082 data.paravirtProvider = mHWData->mParavirtProvider;
10083 data.strParavirtDebug = mHWData->mParavirtDebug;
10084
10085 // emulated USB card reader
10086 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10087
10088 // HPET
10089 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10090
10091 // boot order
10092 data.mapBootOrder.clear();
10093 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10094 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10095
10096 /* VRDEServer settings (optional) */
10097 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10098 if (FAILED(rc)) throw rc;
10099
10100 /* BIOS settings (required) */
10101 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10102 if (FAILED(rc)) throw rc;
10103
10104 /* Recording settings (required) */
10105 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10106 if (FAILED(rc)) throw rc;
10107
10108 /* GraphicsAdapter settings (required) */
10109 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10110 if (FAILED(rc)) throw rc;
10111
10112 /* USB Controller (required) */
10113 data.usbSettings.llUSBControllers.clear();
10114 for (USBControllerList::const_iterator
10115 it = mUSBControllers->begin();
10116 it != mUSBControllers->end();
10117 ++it)
10118 {
10119 ComObjPtr<USBController> ctrl = *it;
10120 settings::USBController settingsCtrl;
10121
10122 settingsCtrl.strName = ctrl->i_getName();
10123 settingsCtrl.enmType = ctrl->i_getControllerType();
10124
10125 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10126 }
10127
10128 /* USB device filters (required) */
10129 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10130 if (FAILED(rc)) throw rc;
10131
10132 /* Network adapters (required) */
10133 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10134 data.llNetworkAdapters.clear();
10135 /* Write out only the nominal number of network adapters for this
10136 * chipset type. Since Machine::commit() hasn't been called there
10137 * may be extra NIC settings in the vector. */
10138 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10139 {
10140 settings::NetworkAdapter nic;
10141 nic.ulSlot = (uint32_t)slot;
10142 /* paranoia check... must not be NULL, but must not crash either. */
10143 if (mNetworkAdapters[slot])
10144 {
10145 if (mNetworkAdapters[slot]->i_hasDefaults())
10146 continue;
10147
10148 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10149 if (FAILED(rc)) throw rc;
10150
10151 data.llNetworkAdapters.push_back(nic);
10152 }
10153 }
10154
10155 /* Serial ports */
10156 data.llSerialPorts.clear();
10157 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10158 {
10159 if (mSerialPorts[slot]->i_hasDefaults())
10160 continue;
10161
10162 settings::SerialPort s;
10163 s.ulSlot = slot;
10164 rc = mSerialPorts[slot]->i_saveSettings(s);
10165 if (FAILED(rc)) return rc;
10166
10167 data.llSerialPorts.push_back(s);
10168 }
10169
10170 /* Parallel ports */
10171 data.llParallelPorts.clear();
10172 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10173 {
10174 if (mParallelPorts[slot]->i_hasDefaults())
10175 continue;
10176
10177 settings::ParallelPort p;
10178 p.ulSlot = slot;
10179 rc = mParallelPorts[slot]->i_saveSettings(p);
10180 if (FAILED(rc)) return rc;
10181
10182 data.llParallelPorts.push_back(p);
10183 }
10184
10185 /* Audio adapter */
10186 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10187 if (FAILED(rc)) return rc;
10188
10189 rc = i_saveStorageControllers(data.storage);
10190 if (FAILED(rc)) return rc;
10191
10192 /* Shared folders */
10193 data.llSharedFolders.clear();
10194 for (HWData::SharedFolderList::const_iterator
10195 it = mHWData->mSharedFolders.begin();
10196 it != mHWData->mSharedFolders.end();
10197 ++it)
10198 {
10199 SharedFolder *pSF = *it;
10200 AutoCaller sfCaller(pSF);
10201 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10202 settings::SharedFolder sf;
10203 sf.strName = pSF->i_getName();
10204 sf.strHostPath = pSF->i_getHostPath();
10205 sf.fWritable = !!pSF->i_isWritable();
10206 sf.fAutoMount = !!pSF->i_isAutoMounted();
10207 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10208
10209 data.llSharedFolders.push_back(sf);
10210 }
10211
10212 // clipboard
10213 data.clipboardMode = mHWData->mClipboardMode;
10214 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10215
10216 // drag'n'drop
10217 data.dndMode = mHWData->mDnDMode;
10218
10219 /* Guest */
10220 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10221
10222 // IO settings
10223 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10224 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10225
10226 /* BandwidthControl (required) */
10227 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10228 if (FAILED(rc)) throw rc;
10229
10230 /* Host PCI devices */
10231 data.pciAttachments.clear();
10232 for (HWData::PCIDeviceAssignmentList::const_iterator
10233 it = mHWData->mPCIDeviceAssignments.begin();
10234 it != mHWData->mPCIDeviceAssignments.end();
10235 ++it)
10236 {
10237 ComObjPtr<PCIDeviceAttachment> pda = *it;
10238 settings::HostPCIDeviceAttachment hpda;
10239
10240 rc = pda->i_saveSettings(hpda);
10241 if (FAILED(rc)) throw rc;
10242
10243 data.pciAttachments.push_back(hpda);
10244 }
10245
10246 // guest properties
10247 data.llGuestProperties.clear();
10248#ifdef VBOX_WITH_GUEST_PROPS
10249 for (HWData::GuestPropertyMap::const_iterator
10250 it = mHWData->mGuestProperties.begin();
10251 it != mHWData->mGuestProperties.end();
10252 ++it)
10253 {
10254 HWData::GuestProperty property = it->second;
10255
10256 /* Remove transient guest properties at shutdown unless we
10257 * are saving state. Note that restoring snapshot intentionally
10258 * keeps them, they will be removed if appropriate once the final
10259 * machine state is set (as crashes etc. need to work). */
10260 if ( ( mData->mMachineState == MachineState_PoweredOff
10261 || mData->mMachineState == MachineState_Aborted
10262 || mData->mMachineState == MachineState_Teleported)
10263 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10264 continue;
10265 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10266 prop.strName = it->first;
10267 prop.strValue = property.strValue;
10268 prop.timestamp = property.mTimestamp;
10269 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10270 GuestPropWriteFlags(property.mFlags, szFlags);
10271 prop.strFlags = szFlags;
10272
10273 data.llGuestProperties.push_back(prop);
10274 }
10275
10276 /* I presume this doesn't require a backup(). */
10277 mData->mGuestPropertiesModified = FALSE;
10278#endif /* VBOX_WITH_GUEST_PROPS defined */
10279
10280 *pDbg = mHWData->mDebugging;
10281 *pAutostart = mHWData->mAutostart;
10282
10283 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10284 }
10285 catch (std::bad_alloc &)
10286 {
10287 return E_OUTOFMEMORY;
10288 }
10289
10290 AssertComRC(rc);
10291 return rc;
10292}
10293
10294/**
10295 * Saves the storage controller configuration.
10296 *
10297 * @param data storage settings.
10298 */
10299HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10300{
10301 data.llStorageControllers.clear();
10302
10303 for (StorageControllerList::const_iterator
10304 it = mStorageControllers->begin();
10305 it != mStorageControllers->end();
10306 ++it)
10307 {
10308 HRESULT rc;
10309 ComObjPtr<StorageController> pCtl = *it;
10310
10311 settings::StorageController ctl;
10312 ctl.strName = pCtl->i_getName();
10313 ctl.controllerType = pCtl->i_getControllerType();
10314 ctl.storageBus = pCtl->i_getStorageBus();
10315 ctl.ulInstance = pCtl->i_getInstance();
10316 ctl.fBootable = pCtl->i_getBootable();
10317
10318 /* Save the port count. */
10319 ULONG portCount;
10320 rc = pCtl->COMGETTER(PortCount)(&portCount);
10321 ComAssertComRCRet(rc, rc);
10322 ctl.ulPortCount = portCount;
10323
10324 /* Save fUseHostIOCache */
10325 BOOL fUseHostIOCache;
10326 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10327 ComAssertComRCRet(rc, rc);
10328 ctl.fUseHostIOCache = !!fUseHostIOCache;
10329
10330 /* save the devices now. */
10331 rc = i_saveStorageDevices(pCtl, ctl);
10332 ComAssertComRCRet(rc, rc);
10333
10334 data.llStorageControllers.push_back(ctl);
10335 }
10336
10337 return S_OK;
10338}
10339
10340/**
10341 * Saves the hard disk configuration.
10342 */
10343HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10344 settings::StorageController &data)
10345{
10346 MediumAttachmentList atts;
10347
10348 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10349 if (FAILED(rc)) return rc;
10350
10351 data.llAttachedDevices.clear();
10352 for (MediumAttachmentList::const_iterator
10353 it = atts.begin();
10354 it != atts.end();
10355 ++it)
10356 {
10357 settings::AttachedDevice dev;
10358 IMediumAttachment *iA = *it;
10359 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10360 Medium *pMedium = pAttach->i_getMedium();
10361
10362 dev.deviceType = pAttach->i_getType();
10363 dev.lPort = pAttach->i_getPort();
10364 dev.lDevice = pAttach->i_getDevice();
10365 dev.fPassThrough = pAttach->i_getPassthrough();
10366 dev.fHotPluggable = pAttach->i_getHotPluggable();
10367 if (pMedium)
10368 {
10369 if (pMedium->i_isHostDrive())
10370 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10371 else
10372 dev.uuid = pMedium->i_getId();
10373 dev.fTempEject = pAttach->i_getTempEject();
10374 dev.fNonRotational = pAttach->i_getNonRotational();
10375 dev.fDiscard = pAttach->i_getDiscard();
10376 }
10377
10378 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10379
10380 data.llAttachedDevices.push_back(dev);
10381 }
10382
10383 return S_OK;
10384}
10385
10386/**
10387 * Saves machine state settings as defined by aFlags
10388 * (SaveSTS_* values).
10389 *
10390 * @param aFlags Combination of SaveSTS_* flags.
10391 *
10392 * @note Locks objects for writing.
10393 */
10394HRESULT Machine::i_saveStateSettings(int aFlags)
10395{
10396 if (aFlags == 0)
10397 return S_OK;
10398
10399 AutoCaller autoCaller(this);
10400 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10401
10402 /* This object's write lock is also necessary to serialize file access
10403 * (prevent concurrent reads and writes) */
10404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10405
10406 HRESULT rc = S_OK;
10407
10408 Assert(mData->pMachineConfigFile);
10409
10410 try
10411 {
10412 if (aFlags & SaveSTS_CurStateModified)
10413 mData->pMachineConfigFile->fCurrentStateModified = true;
10414
10415 if (aFlags & SaveSTS_StateFilePath)
10416 {
10417 if (!mSSData->strStateFilePath.isEmpty())
10418 /* try to make the file name relative to the settings file dir */
10419 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10420 else
10421 mData->pMachineConfigFile->strStateFile.setNull();
10422 }
10423
10424 if (aFlags & SaveSTS_StateTimeStamp)
10425 {
10426 Assert( mData->mMachineState != MachineState_Aborted
10427 || mSSData->strStateFilePath.isEmpty());
10428
10429 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10430
10431 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10432/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10433 }
10434
10435 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10436 }
10437 catch (...)
10438 {
10439 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10440 }
10441
10442 return rc;
10443}
10444
10445/**
10446 * Ensures that the given medium is added to a media registry. If this machine
10447 * was created with 4.0 or later, then the machine registry is used. Otherwise
10448 * the global VirtualBox media registry is used.
10449 *
10450 * Caller must NOT hold machine lock, media tree or any medium locks!
10451 *
10452 * @param pMedium
10453 */
10454void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10455{
10456 /* Paranoia checks: do not hold machine or media tree locks. */
10457 AssertReturnVoid(!isWriteLockOnCurrentThread());
10458 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10459
10460 ComObjPtr<Medium> pBase;
10461 {
10462 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10463 pBase = pMedium->i_getBase();
10464 }
10465
10466 /* Paranoia checks: do not hold medium locks. */
10467 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10468 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10469
10470 // decide which medium registry to use now that the medium is attached:
10471 Guid uuid;
10472 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10473 if (fCanHaveOwnMediaRegistry)
10474 // machine XML is VirtualBox 4.0 or higher:
10475 uuid = i_getId(); // machine UUID
10476 else
10477 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10478
10479 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10480 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10481 if (pMedium->i_addRegistry(uuid))
10482 mParent->i_markRegistryModified(uuid);
10483
10484 /* For more complex hard disk structures it can happen that the base
10485 * medium isn't yet associated with any medium registry. Do that now. */
10486 if (pMedium != pBase)
10487 {
10488 /* Tree lock needed by Medium::addRegistry when recursing. */
10489 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10490 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10491 {
10492 treeLock.release();
10493 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10494 treeLock.acquire();
10495 }
10496 if (pBase->i_addRegistryRecursive(uuid))
10497 {
10498 treeLock.release();
10499 mParent->i_markRegistryModified(uuid);
10500 }
10501 }
10502}
10503
10504/**
10505 * Creates differencing hard disks for all normal hard disks attached to this
10506 * machine and a new set of attachments to refer to created disks.
10507 *
10508 * Used when taking a snapshot or when deleting the current state. Gets called
10509 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10510 *
10511 * This method assumes that mMediumAttachments contains the original hard disk
10512 * attachments it needs to create diffs for. On success, these attachments will
10513 * be replaced with the created diffs.
10514 *
10515 * Attachments with non-normal hard disks are left as is.
10516 *
10517 * If @a aOnline is @c false then the original hard disks that require implicit
10518 * diffs will be locked for reading. Otherwise it is assumed that they are
10519 * already locked for writing (when the VM was started). Note that in the latter
10520 * case it is responsibility of the caller to lock the newly created diffs for
10521 * writing if this method succeeds.
10522 *
10523 * @param aProgress Progress object to run (must contain at least as
10524 * many operations left as the number of hard disks
10525 * attached).
10526 * @param aWeight Weight of this operation.
10527 * @param aOnline Whether the VM was online prior to this operation.
10528 *
10529 * @note The progress object is not marked as completed, neither on success nor
10530 * on failure. This is a responsibility of the caller.
10531 *
10532 * @note Locks this object and the media tree for writing.
10533 */
10534HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10535 ULONG aWeight,
10536 bool aOnline)
10537{
10538 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10539
10540 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10541 AssertReturn(!!pProgressControl, E_INVALIDARG);
10542
10543 AutoCaller autoCaller(this);
10544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10545
10546 AutoMultiWriteLock2 alock(this->lockHandle(),
10547 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10548
10549 /* must be in a protective state because we release the lock below */
10550 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10551 || mData->mMachineState == MachineState_OnlineSnapshotting
10552 || mData->mMachineState == MachineState_LiveSnapshotting
10553 || mData->mMachineState == MachineState_RestoringSnapshot
10554 || mData->mMachineState == MachineState_DeletingSnapshot
10555 , E_FAIL);
10556
10557 HRESULT rc = S_OK;
10558
10559 // use appropriate locked media map (online or offline)
10560 MediumLockListMap lockedMediaOffline;
10561 MediumLockListMap *lockedMediaMap;
10562 if (aOnline)
10563 lockedMediaMap = &mData->mSession.mLockedMedia;
10564 else
10565 lockedMediaMap = &lockedMediaOffline;
10566
10567 try
10568 {
10569 if (!aOnline)
10570 {
10571 /* lock all attached hard disks early to detect "in use"
10572 * situations before creating actual diffs */
10573 for (MediumAttachmentList::const_iterator
10574 it = mMediumAttachments->begin();
10575 it != mMediumAttachments->end();
10576 ++it)
10577 {
10578 MediumAttachment *pAtt = *it;
10579 if (pAtt->i_getType() == DeviceType_HardDisk)
10580 {
10581 Medium *pMedium = pAtt->i_getMedium();
10582 Assert(pMedium);
10583
10584 MediumLockList *pMediumLockList(new MediumLockList());
10585 alock.release();
10586 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10587 NULL /* pToLockWrite */,
10588 false /* fMediumLockWriteAll */,
10589 NULL,
10590 *pMediumLockList);
10591 alock.acquire();
10592 if (FAILED(rc))
10593 {
10594 delete pMediumLockList;
10595 throw rc;
10596 }
10597 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10598 if (FAILED(rc))
10599 {
10600 throw setError(rc,
10601 tr("Collecting locking information for all attached media failed"));
10602 }
10603 }
10604 }
10605
10606 /* Now lock all media. If this fails, nothing is locked. */
10607 alock.release();
10608 rc = lockedMediaMap->Lock();
10609 alock.acquire();
10610 if (FAILED(rc))
10611 {
10612 throw setError(rc,
10613 tr("Locking of attached media failed"));
10614 }
10615 }
10616
10617 /* remember the current list (note that we don't use backup() since
10618 * mMediumAttachments may be already backed up) */
10619 MediumAttachmentList atts = *mMediumAttachments.data();
10620
10621 /* start from scratch */
10622 mMediumAttachments->clear();
10623
10624 /* go through remembered attachments and create diffs for normal hard
10625 * disks and attach them */
10626 for (MediumAttachmentList::const_iterator
10627 it = atts.begin();
10628 it != atts.end();
10629 ++it)
10630 {
10631 MediumAttachment *pAtt = *it;
10632
10633 DeviceType_T devType = pAtt->i_getType();
10634 Medium *pMedium = pAtt->i_getMedium();
10635
10636 if ( devType != DeviceType_HardDisk
10637 || pMedium == NULL
10638 || pMedium->i_getType() != MediumType_Normal)
10639 {
10640 /* copy the attachment as is */
10641
10642 /** @todo the progress object created in SessionMachine::TakeSnaphot
10643 * only expects operations for hard disks. Later other
10644 * device types need to show up in the progress as well. */
10645 if (devType == DeviceType_HardDisk)
10646 {
10647 if (pMedium == NULL)
10648 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10649 aWeight); // weight
10650 else
10651 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10652 pMedium->i_getBase()->i_getName().c_str()).raw(),
10653 aWeight); // weight
10654 }
10655
10656 mMediumAttachments->push_back(pAtt);
10657 continue;
10658 }
10659
10660 /* need a diff */
10661 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10662 pMedium->i_getBase()->i_getName().c_str()).raw(),
10663 aWeight); // weight
10664
10665 Utf8Str strFullSnapshotFolder;
10666 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10667
10668 ComObjPtr<Medium> diff;
10669 diff.createObject();
10670 // store the diff in the same registry as the parent
10671 // (this cannot fail here because we can't create implicit diffs for
10672 // unregistered images)
10673 Guid uuidRegistryParent;
10674 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10675 Assert(fInRegistry); NOREF(fInRegistry);
10676 rc = diff->init(mParent,
10677 pMedium->i_getPreferredDiffFormat(),
10678 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10679 uuidRegistryParent,
10680 DeviceType_HardDisk);
10681 if (FAILED(rc)) throw rc;
10682
10683 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10684 * the push_back? Looks like we're going to release medium with the
10685 * wrong kind of lock (general issue with if we fail anywhere at all)
10686 * and an orphaned VDI in the snapshots folder. */
10687
10688 /* update the appropriate lock list */
10689 MediumLockList *pMediumLockList;
10690 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10691 AssertComRCThrowRC(rc);
10692 if (aOnline)
10693 {
10694 alock.release();
10695 /* The currently attached medium will be read-only, change
10696 * the lock type to read. */
10697 rc = pMediumLockList->Update(pMedium, false);
10698 alock.acquire();
10699 AssertComRCThrowRC(rc);
10700 }
10701
10702 /* release the locks before the potentially lengthy operation */
10703 alock.release();
10704 rc = pMedium->i_createDiffStorage(diff,
10705 pMedium->i_getPreferredDiffVariant(),
10706 pMediumLockList,
10707 NULL /* aProgress */,
10708 true /* aWait */,
10709 false /* aNotify */);
10710 alock.acquire();
10711 if (FAILED(rc)) throw rc;
10712
10713 /* actual lock list update is done in Machine::i_commitMedia */
10714
10715 rc = diff->i_addBackReference(mData->mUuid);
10716 AssertComRCThrowRC(rc);
10717
10718 /* add a new attachment */
10719 ComObjPtr<MediumAttachment> attachment;
10720 attachment.createObject();
10721 rc = attachment->init(this,
10722 diff,
10723 pAtt->i_getControllerName(),
10724 pAtt->i_getPort(),
10725 pAtt->i_getDevice(),
10726 DeviceType_HardDisk,
10727 true /* aImplicit */,
10728 false /* aPassthrough */,
10729 false /* aTempEject */,
10730 pAtt->i_getNonRotational(),
10731 pAtt->i_getDiscard(),
10732 pAtt->i_getHotPluggable(),
10733 pAtt->i_getBandwidthGroup());
10734 if (FAILED(rc)) throw rc;
10735
10736 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10737 AssertComRCThrowRC(rc);
10738 mMediumAttachments->push_back(attachment);
10739 }
10740 }
10741 catch (HRESULT aRC) { rc = aRC; }
10742
10743 /* unlock all hard disks we locked when there is no VM */
10744 if (!aOnline)
10745 {
10746 ErrorInfoKeeper eik;
10747
10748 HRESULT rc1 = lockedMediaMap->Clear();
10749 AssertComRC(rc1);
10750 }
10751
10752 return rc;
10753}
10754
10755/**
10756 * Deletes implicit differencing hard disks created either by
10757 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10758 * mMediumAttachments.
10759 *
10760 * Note that to delete hard disks created by #attachDevice() this method is
10761 * called from #i_rollbackMedia() when the changes are rolled back.
10762 *
10763 * @note Locks this object and the media tree for writing.
10764 */
10765HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10766{
10767 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10768
10769 AutoCaller autoCaller(this);
10770 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10771
10772 AutoMultiWriteLock2 alock(this->lockHandle(),
10773 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10774
10775 /* We absolutely must have backed up state. */
10776 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10777
10778 /* Check if there are any implicitly created diff images. */
10779 bool fImplicitDiffs = false;
10780 for (MediumAttachmentList::const_iterator
10781 it = mMediumAttachments->begin();
10782 it != mMediumAttachments->end();
10783 ++it)
10784 {
10785 const ComObjPtr<MediumAttachment> &pAtt = *it;
10786 if (pAtt->i_isImplicit())
10787 {
10788 fImplicitDiffs = true;
10789 break;
10790 }
10791 }
10792 /* If there is nothing to do, leave early. This saves lots of image locking
10793 * effort. It also avoids a MachineStateChanged event without real reason.
10794 * This is important e.g. when loading a VM config, because there should be
10795 * no events. Otherwise API clients can become thoroughly confused for
10796 * inaccessible VMs (the code for loading VM configs uses this method for
10797 * cleanup if the config makes no sense), as they take such events as an
10798 * indication that the VM is alive, and they would force the VM config to
10799 * be reread, leading to an endless loop. */
10800 if (!fImplicitDiffs)
10801 return S_OK;
10802
10803 HRESULT rc = S_OK;
10804 MachineState_T oldState = mData->mMachineState;
10805
10806 /* will release the lock before the potentially lengthy operation,
10807 * so protect with the special state (unless already protected) */
10808 if ( oldState != MachineState_Snapshotting
10809 && oldState != MachineState_OnlineSnapshotting
10810 && oldState != MachineState_LiveSnapshotting
10811 && oldState != MachineState_RestoringSnapshot
10812 && oldState != MachineState_DeletingSnapshot
10813 && oldState != MachineState_DeletingSnapshotOnline
10814 && oldState != MachineState_DeletingSnapshotPaused
10815 )
10816 i_setMachineState(MachineState_SettingUp);
10817
10818 // use appropriate locked media map (online or offline)
10819 MediumLockListMap lockedMediaOffline;
10820 MediumLockListMap *lockedMediaMap;
10821 if (aOnline)
10822 lockedMediaMap = &mData->mSession.mLockedMedia;
10823 else
10824 lockedMediaMap = &lockedMediaOffline;
10825
10826 try
10827 {
10828 if (!aOnline)
10829 {
10830 /* lock all attached hard disks early to detect "in use"
10831 * situations before deleting actual diffs */
10832 for (MediumAttachmentList::const_iterator
10833 it = mMediumAttachments->begin();
10834 it != mMediumAttachments->end();
10835 ++it)
10836 {
10837 MediumAttachment *pAtt = *it;
10838 if (pAtt->i_getType() == DeviceType_HardDisk)
10839 {
10840 Medium *pMedium = pAtt->i_getMedium();
10841 Assert(pMedium);
10842
10843 MediumLockList *pMediumLockList(new MediumLockList());
10844 alock.release();
10845 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10846 NULL /* pToLockWrite */,
10847 false /* fMediumLockWriteAll */,
10848 NULL,
10849 *pMediumLockList);
10850 alock.acquire();
10851
10852 if (FAILED(rc))
10853 {
10854 delete pMediumLockList;
10855 throw rc;
10856 }
10857
10858 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10859 if (FAILED(rc))
10860 throw rc;
10861 }
10862 }
10863
10864 if (FAILED(rc))
10865 throw rc;
10866 } // end of offline
10867
10868 /* Lock lists are now up to date and include implicitly created media */
10869
10870 /* Go through remembered attachments and delete all implicitly created
10871 * diffs and fix up the attachment information */
10872 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10873 MediumAttachmentList implicitAtts;
10874 for (MediumAttachmentList::const_iterator
10875 it = mMediumAttachments->begin();
10876 it != mMediumAttachments->end();
10877 ++it)
10878 {
10879 ComObjPtr<MediumAttachment> pAtt = *it;
10880 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10881 if (pMedium.isNull())
10882 continue;
10883
10884 // Implicit attachments go on the list for deletion and back references are removed.
10885 if (pAtt->i_isImplicit())
10886 {
10887 /* Deassociate and mark for deletion */
10888 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10889 rc = pMedium->i_removeBackReference(mData->mUuid);
10890 if (FAILED(rc))
10891 throw rc;
10892 implicitAtts.push_back(pAtt);
10893 continue;
10894 }
10895
10896 /* Was this medium attached before? */
10897 if (!i_findAttachment(oldAtts, pMedium))
10898 {
10899 /* no: de-associate */
10900 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10901 rc = pMedium->i_removeBackReference(mData->mUuid);
10902 if (FAILED(rc))
10903 throw rc;
10904 continue;
10905 }
10906 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10907 }
10908
10909 /* If there are implicit attachments to delete, throw away the lock
10910 * map contents (which will unlock all media) since the medium
10911 * attachments will be rolled back. Below we need to completely
10912 * recreate the lock map anyway since it is infinitely complex to
10913 * do this incrementally (would need reconstructing each attachment
10914 * change, which would be extremely hairy). */
10915 if (implicitAtts.size() != 0)
10916 {
10917 ErrorInfoKeeper eik;
10918
10919 HRESULT rc1 = lockedMediaMap->Clear();
10920 AssertComRC(rc1);
10921 }
10922
10923 /* rollback hard disk changes */
10924 mMediumAttachments.rollback();
10925
10926 MultiResult mrc(S_OK);
10927
10928 // Delete unused implicit diffs.
10929 if (implicitAtts.size() != 0)
10930 {
10931 alock.release();
10932
10933 for (MediumAttachmentList::const_iterator
10934 it = implicitAtts.begin();
10935 it != implicitAtts.end();
10936 ++it)
10937 {
10938 // Remove medium associated with this attachment.
10939 ComObjPtr<MediumAttachment> pAtt = *it;
10940 Assert(pAtt);
10941 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10942 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10943 Assert(pMedium);
10944
10945 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10946 // continue on delete failure, just collect error messages
10947 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10948 pMedium->i_getLocationFull().c_str() ));
10949 mrc = rc;
10950 }
10951 // Clear the list of deleted implicit attachments now, while not
10952 // holding the lock, as it will ultimately trigger Medium::uninit()
10953 // calls which assume that the media tree lock isn't held.
10954 implicitAtts.clear();
10955
10956 alock.acquire();
10957
10958 /* if there is a VM recreate media lock map as mentioned above,
10959 * otherwise it is a waste of time and we leave things unlocked */
10960 if (aOnline)
10961 {
10962 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10963 /* must never be NULL, but better safe than sorry */
10964 if (!pMachine.isNull())
10965 {
10966 alock.release();
10967 rc = mData->mSession.mMachine->i_lockMedia();
10968 alock.acquire();
10969 if (FAILED(rc))
10970 throw rc;
10971 }
10972 }
10973 }
10974 }
10975 catch (HRESULT aRC) {rc = aRC;}
10976
10977 if (mData->mMachineState == MachineState_SettingUp)
10978 i_setMachineState(oldState);
10979
10980 /* unlock all hard disks we locked when there is no VM */
10981 if (!aOnline)
10982 {
10983 ErrorInfoKeeper eik;
10984
10985 HRESULT rc1 = lockedMediaMap->Clear();
10986 AssertComRC(rc1);
10987 }
10988
10989 return rc;
10990}
10991
10992
10993/**
10994 * Looks through the given list of media attachments for one with the given parameters
10995 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10996 * can be searched as well if needed.
10997 *
10998 * @param ll
10999 * @param aControllerName
11000 * @param aControllerPort
11001 * @param aDevice
11002 * @return
11003 */
11004MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11005 const Utf8Str &aControllerName,
11006 LONG aControllerPort,
11007 LONG aDevice)
11008{
11009 for (MediumAttachmentList::const_iterator
11010 it = ll.begin();
11011 it != ll.end();
11012 ++it)
11013 {
11014 MediumAttachment *pAttach = *it;
11015 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11016 return pAttach;
11017 }
11018
11019 return NULL;
11020}
11021
11022/**
11023 * Looks through the given list of media attachments for one with the given parameters
11024 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11025 * can be searched as well if needed.
11026 *
11027 * @param ll
11028 * @param pMedium
11029 * @return
11030 */
11031MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11032 ComObjPtr<Medium> pMedium)
11033{
11034 for (MediumAttachmentList::const_iterator
11035 it = ll.begin();
11036 it != ll.end();
11037 ++it)
11038 {
11039 MediumAttachment *pAttach = *it;
11040 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11041 if (pMediumThis == pMedium)
11042 return pAttach;
11043 }
11044
11045 return NULL;
11046}
11047
11048/**
11049 * Looks through the given list of media attachments for one with the given parameters
11050 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11051 * can be searched as well if needed.
11052 *
11053 * @param ll
11054 * @param id
11055 * @return
11056 */
11057MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11058 Guid &id)
11059{
11060 for (MediumAttachmentList::const_iterator
11061 it = ll.begin();
11062 it != ll.end();
11063 ++it)
11064 {
11065 MediumAttachment *pAttach = *it;
11066 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11067 if (pMediumThis->i_getId() == id)
11068 return pAttach;
11069 }
11070
11071 return NULL;
11072}
11073
11074/**
11075 * Main implementation for Machine::DetachDevice. This also gets called
11076 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11077 *
11078 * @param pAttach Medium attachment to detach.
11079 * @param writeLock Machine write lock which the caller must have locked once.
11080 * This may be released temporarily in here.
11081 * @param pSnapshot If NULL, then the detachment is for the current machine.
11082 * Otherwise this is for a SnapshotMachine, and this must be
11083 * its snapshot.
11084 * @return
11085 */
11086HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11087 AutoWriteLock &writeLock,
11088 Snapshot *pSnapshot)
11089{
11090 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11091 DeviceType_T mediumType = pAttach->i_getType();
11092
11093 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11094
11095 if (pAttach->i_isImplicit())
11096 {
11097 /* attempt to implicitly delete the implicitly created diff */
11098
11099 /// @todo move the implicit flag from MediumAttachment to Medium
11100 /// and forbid any hard disk operation when it is implicit. Or maybe
11101 /// a special media state for it to make it even more simple.
11102
11103 Assert(mMediumAttachments.isBackedUp());
11104
11105 /* will release the lock before the potentially lengthy operation, so
11106 * protect with the special state */
11107 MachineState_T oldState = mData->mMachineState;
11108 i_setMachineState(MachineState_SettingUp);
11109
11110 writeLock.release();
11111
11112 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11113 true /*aWait*/,
11114 false /*aNotify*/);
11115
11116 writeLock.acquire();
11117
11118 i_setMachineState(oldState);
11119
11120 if (FAILED(rc)) return rc;
11121 }
11122
11123 i_setModified(IsModified_Storage);
11124 mMediumAttachments.backup();
11125 mMediumAttachments->remove(pAttach);
11126
11127 if (!oldmedium.isNull())
11128 {
11129 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11130 if (pSnapshot)
11131 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11132 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11133 else if (mediumType != DeviceType_HardDisk)
11134 oldmedium->i_removeBackReference(mData->mUuid);
11135 }
11136
11137 return S_OK;
11138}
11139
11140/**
11141 * Goes thru all media of the given list and
11142 *
11143 * 1) calls i_detachDevice() on each of them for this machine and
11144 * 2) adds all Medium objects found in the process to the given list,
11145 * depending on cleanupMode.
11146 *
11147 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11148 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11149 * media to the list.
11150 *
11151 * This gets called from Machine::Unregister, both for the actual Machine and
11152 * the SnapshotMachine objects that might be found in the snapshots.
11153 *
11154 * Requires caller and locking. The machine lock must be passed in because it
11155 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11156 *
11157 * @param writeLock Machine lock from top-level caller; this gets passed to
11158 * i_detachDevice.
11159 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11160 * object if called for a SnapshotMachine.
11161 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11162 * added to llMedia; if Full, then all media get added;
11163 * otherwise no media get added.
11164 * @param llMedia Caller's list to receive Medium objects which got detached so
11165 * caller can close() them, depending on cleanupMode.
11166 * @return
11167 */
11168HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11169 Snapshot *pSnapshot,
11170 CleanupMode_T cleanupMode,
11171 MediaList &llMedia)
11172{
11173 Assert(isWriteLockOnCurrentThread());
11174
11175 HRESULT rc;
11176
11177 // make a temporary list because i_detachDevice invalidates iterators into
11178 // mMediumAttachments
11179 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11180
11181 for (MediumAttachmentList::iterator
11182 it = llAttachments2.begin();
11183 it != llAttachments2.end();
11184 ++it)
11185 {
11186 ComObjPtr<MediumAttachment> &pAttach = *it;
11187 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11188
11189 if (!pMedium.isNull())
11190 {
11191 AutoCaller mac(pMedium);
11192 if (FAILED(mac.rc())) return mac.rc();
11193 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11194 DeviceType_T devType = pMedium->i_getDeviceType();
11195 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11196 && devType == DeviceType_HardDisk)
11197 || (cleanupMode == CleanupMode_Full)
11198 )
11199 {
11200 llMedia.push_back(pMedium);
11201 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11202 /* Not allowed to keep this lock as below we need the parent
11203 * medium lock, and the lock order is parent to child. */
11204 lock.release();
11205 /*
11206 * Search for medias which are not attached to any machine, but
11207 * in the chain to an attached disk. Mediums are only consided
11208 * if they are:
11209 * - have only one child
11210 * - no references to any machines
11211 * - are of normal medium type
11212 */
11213 while (!pParent.isNull())
11214 {
11215 AutoCaller mac1(pParent);
11216 if (FAILED(mac1.rc())) return mac1.rc();
11217 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11218 if (pParent->i_getChildren().size() == 1)
11219 {
11220 if ( pParent->i_getMachineBackRefCount() == 0
11221 && pParent->i_getType() == MediumType_Normal
11222 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11223 llMedia.push_back(pParent);
11224 }
11225 else
11226 break;
11227 pParent = pParent->i_getParent();
11228 }
11229 }
11230 }
11231
11232 // real machine: then we need to use the proper method
11233 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11234
11235 if (FAILED(rc))
11236 return rc;
11237 }
11238
11239 return S_OK;
11240}
11241
11242/**
11243 * Perform deferred hard disk detachments.
11244 *
11245 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11246 * changed (not backed up).
11247 *
11248 * If @a aOnline is @c true then this method will also unlock the old hard
11249 * disks for which the new implicit diffs were created and will lock these new
11250 * diffs for writing.
11251 *
11252 * @param aOnline Whether the VM was online prior to this operation.
11253 *
11254 * @note Locks this object for writing!
11255 */
11256void Machine::i_commitMedia(bool aOnline /*= false*/)
11257{
11258 AutoCaller autoCaller(this);
11259 AssertComRCReturnVoid(autoCaller.rc());
11260
11261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11262
11263 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11264
11265 HRESULT rc = S_OK;
11266
11267 /* no attach/detach operations -- nothing to do */
11268 if (!mMediumAttachments.isBackedUp())
11269 return;
11270
11271 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11272 bool fMediaNeedsLocking = false;
11273
11274 /* enumerate new attachments */
11275 for (MediumAttachmentList::const_iterator
11276 it = mMediumAttachments->begin();
11277 it != mMediumAttachments->end();
11278 ++it)
11279 {
11280 MediumAttachment *pAttach = *it;
11281
11282 pAttach->i_commit();
11283
11284 Medium *pMedium = pAttach->i_getMedium();
11285 bool fImplicit = pAttach->i_isImplicit();
11286
11287 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11288 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11289 fImplicit));
11290
11291 /** @todo convert all this Machine-based voodoo to MediumAttachment
11292 * based commit logic. */
11293 if (fImplicit)
11294 {
11295 /* convert implicit attachment to normal */
11296 pAttach->i_setImplicit(false);
11297
11298 if ( aOnline
11299 && pMedium
11300 && pAttach->i_getType() == DeviceType_HardDisk
11301 )
11302 {
11303 /* update the appropriate lock list */
11304 MediumLockList *pMediumLockList;
11305 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11306 AssertComRC(rc);
11307 if (pMediumLockList)
11308 {
11309 /* unlock if there's a need to change the locking */
11310 if (!fMediaNeedsLocking)
11311 {
11312 rc = mData->mSession.mLockedMedia.Unlock();
11313 AssertComRC(rc);
11314 fMediaNeedsLocking = true;
11315 }
11316 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11317 AssertComRC(rc);
11318 rc = pMediumLockList->Append(pMedium, true);
11319 AssertComRC(rc);
11320 }
11321 }
11322
11323 continue;
11324 }
11325
11326 if (pMedium)
11327 {
11328 /* was this medium attached before? */
11329 for (MediumAttachmentList::iterator
11330 oldIt = oldAtts.begin();
11331 oldIt != oldAtts.end();
11332 ++oldIt)
11333 {
11334 MediumAttachment *pOldAttach = *oldIt;
11335 if (pOldAttach->i_getMedium() == pMedium)
11336 {
11337 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11338
11339 /* yes: remove from old to avoid de-association */
11340 oldAtts.erase(oldIt);
11341 break;
11342 }
11343 }
11344 }
11345 }
11346
11347 /* enumerate remaining old attachments and de-associate from the
11348 * current machine state */
11349 for (MediumAttachmentList::const_iterator
11350 it = oldAtts.begin();
11351 it != oldAtts.end();
11352 ++it)
11353 {
11354 MediumAttachment *pAttach = *it;
11355 Medium *pMedium = pAttach->i_getMedium();
11356
11357 /* Detach only hard disks, since DVD/floppy media is detached
11358 * instantly in MountMedium. */
11359 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11360 {
11361 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11362
11363 /* now de-associate from the current machine state */
11364 rc = pMedium->i_removeBackReference(mData->mUuid);
11365 AssertComRC(rc);
11366
11367 if (aOnline)
11368 {
11369 /* unlock since medium is not used anymore */
11370 MediumLockList *pMediumLockList;
11371 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11372 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11373 {
11374 /* this happens for online snapshots, there the attachment
11375 * is changing, but only to a diff image created under
11376 * the old one, so there is no separate lock list */
11377 Assert(!pMediumLockList);
11378 }
11379 else
11380 {
11381 AssertComRC(rc);
11382 if (pMediumLockList)
11383 {
11384 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11385 AssertComRC(rc);
11386 }
11387 }
11388 }
11389 }
11390 }
11391
11392 /* take media locks again so that the locking state is consistent */
11393 if (fMediaNeedsLocking)
11394 {
11395 Assert(aOnline);
11396 rc = mData->mSession.mLockedMedia.Lock();
11397 AssertComRC(rc);
11398 }
11399
11400 /* commit the hard disk changes */
11401 mMediumAttachments.commit();
11402
11403 if (i_isSessionMachine())
11404 {
11405 /*
11406 * Update the parent machine to point to the new owner.
11407 * This is necessary because the stored parent will point to the
11408 * session machine otherwise and cause crashes or errors later
11409 * when the session machine gets invalid.
11410 */
11411 /** @todo Change the MediumAttachment class to behave like any other
11412 * class in this regard by creating peer MediumAttachment
11413 * objects for session machines and share the data with the peer
11414 * machine.
11415 */
11416 for (MediumAttachmentList::const_iterator
11417 it = mMediumAttachments->begin();
11418 it != mMediumAttachments->end();
11419 ++it)
11420 (*it)->i_updateParentMachine(mPeer);
11421
11422 /* attach new data to the primary machine and reshare it */
11423 mPeer->mMediumAttachments.attach(mMediumAttachments);
11424 }
11425
11426 return;
11427}
11428
11429/**
11430 * Perform deferred deletion of implicitly created diffs.
11431 *
11432 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11433 * changed (not backed up).
11434 *
11435 * @note Locks this object for writing!
11436 */
11437void Machine::i_rollbackMedia()
11438{
11439 AutoCaller autoCaller(this);
11440 AssertComRCReturnVoid(autoCaller.rc());
11441
11442 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11443 LogFlowThisFunc(("Entering rollbackMedia\n"));
11444
11445 HRESULT rc = S_OK;
11446
11447 /* no attach/detach operations -- nothing to do */
11448 if (!mMediumAttachments.isBackedUp())
11449 return;
11450
11451 /* enumerate new attachments */
11452 for (MediumAttachmentList::const_iterator
11453 it = mMediumAttachments->begin();
11454 it != mMediumAttachments->end();
11455 ++it)
11456 {
11457 MediumAttachment *pAttach = *it;
11458 /* Fix up the backrefs for DVD/floppy media. */
11459 if (pAttach->i_getType() != DeviceType_HardDisk)
11460 {
11461 Medium *pMedium = pAttach->i_getMedium();
11462 if (pMedium)
11463 {
11464 rc = pMedium->i_removeBackReference(mData->mUuid);
11465 AssertComRC(rc);
11466 }
11467 }
11468
11469 (*it)->i_rollback();
11470
11471 pAttach = *it;
11472 /* Fix up the backrefs for DVD/floppy media. */
11473 if (pAttach->i_getType() != DeviceType_HardDisk)
11474 {
11475 Medium *pMedium = pAttach->i_getMedium();
11476 if (pMedium)
11477 {
11478 rc = pMedium->i_addBackReference(mData->mUuid);
11479 AssertComRC(rc);
11480 }
11481 }
11482 }
11483
11484 /** @todo convert all this Machine-based voodoo to MediumAttachment
11485 * based rollback logic. */
11486 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11487
11488 return;
11489}
11490
11491/**
11492 * Returns true if the settings file is located in the directory named exactly
11493 * as the machine; this means, among other things, that the machine directory
11494 * should be auto-renamed.
11495 *
11496 * @param aSettingsDir if not NULL, the full machine settings file directory
11497 * name will be assigned there.
11498 *
11499 * @note Doesn't lock anything.
11500 * @note Not thread safe (must be called from this object's lock).
11501 */
11502bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11503{
11504 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11505 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11506 if (aSettingsDir)
11507 *aSettingsDir = strMachineDirName;
11508 strMachineDirName.stripPath(); // vmname
11509 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11510 strConfigFileOnly.stripPath() // vmname.vbox
11511 .stripSuffix(); // vmname
11512 /** @todo hack, make somehow use of ComposeMachineFilename */
11513 if (mUserData->s.fDirectoryIncludesUUID)
11514 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11515
11516 AssertReturn(!strMachineDirName.isEmpty(), false);
11517 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11518
11519 return strMachineDirName == strConfigFileOnly;
11520}
11521
11522/**
11523 * Discards all changes to machine settings.
11524 *
11525 * @param aNotify Whether to notify the direct session about changes or not.
11526 *
11527 * @note Locks objects for writing!
11528 */
11529void Machine::i_rollback(bool aNotify)
11530{
11531 AutoCaller autoCaller(this);
11532 AssertComRCReturn(autoCaller.rc(), (void)0);
11533
11534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11535
11536 if (!mStorageControllers.isNull())
11537 {
11538 if (mStorageControllers.isBackedUp())
11539 {
11540 /* unitialize all new devices (absent in the backed up list). */
11541 StorageControllerList *backedList = mStorageControllers.backedUpData();
11542 for (StorageControllerList::const_iterator
11543 it = mStorageControllers->begin();
11544 it != mStorageControllers->end();
11545 ++it)
11546 {
11547 if ( std::find(backedList->begin(), backedList->end(), *it)
11548 == backedList->end()
11549 )
11550 {
11551 (*it)->uninit();
11552 }
11553 }
11554
11555 /* restore the list */
11556 mStorageControllers.rollback();
11557 }
11558
11559 /* rollback any changes to devices after restoring the list */
11560 if (mData->flModifications & IsModified_Storage)
11561 {
11562 for (StorageControllerList::const_iterator
11563 it = mStorageControllers->begin();
11564 it != mStorageControllers->end();
11565 ++it)
11566 {
11567 (*it)->i_rollback();
11568 }
11569 }
11570 }
11571
11572 if (!mUSBControllers.isNull())
11573 {
11574 if (mUSBControllers.isBackedUp())
11575 {
11576 /* unitialize all new devices (absent in the backed up list). */
11577 USBControllerList *backedList = mUSBControllers.backedUpData();
11578 for (USBControllerList::const_iterator
11579 it = mUSBControllers->begin();
11580 it != mUSBControllers->end();
11581 ++it)
11582 {
11583 if ( std::find(backedList->begin(), backedList->end(), *it)
11584 == backedList->end()
11585 )
11586 {
11587 (*it)->uninit();
11588 }
11589 }
11590
11591 /* restore the list */
11592 mUSBControllers.rollback();
11593 }
11594
11595 /* rollback any changes to devices after restoring the list */
11596 if (mData->flModifications & IsModified_USB)
11597 {
11598 for (USBControllerList::const_iterator
11599 it = mUSBControllers->begin();
11600 it != mUSBControllers->end();
11601 ++it)
11602 {
11603 (*it)->i_rollback();
11604 }
11605 }
11606 }
11607
11608 mUserData.rollback();
11609
11610 mHWData.rollback();
11611
11612 if (mData->flModifications & IsModified_Storage)
11613 i_rollbackMedia();
11614
11615 if (mBIOSSettings)
11616 mBIOSSettings->i_rollback();
11617
11618 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11619 mRecordingSettings->i_rollback();
11620
11621 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11622 mGraphicsAdapter->i_rollback();
11623
11624 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11625 mVRDEServer->i_rollback();
11626
11627 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11628 mAudioAdapter->i_rollback();
11629
11630 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11631 mUSBDeviceFilters->i_rollback();
11632
11633 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11634 mBandwidthControl->i_rollback();
11635
11636 if (!mHWData.isNull())
11637 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11638 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11639 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11640 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11641
11642 if (mData->flModifications & IsModified_NetworkAdapters)
11643 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11644 if ( mNetworkAdapters[slot]
11645 && mNetworkAdapters[slot]->i_isModified())
11646 {
11647 mNetworkAdapters[slot]->i_rollback();
11648 networkAdapters[slot] = mNetworkAdapters[slot];
11649 }
11650
11651 if (mData->flModifications & IsModified_SerialPorts)
11652 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11653 if ( mSerialPorts[slot]
11654 && mSerialPorts[slot]->i_isModified())
11655 {
11656 mSerialPorts[slot]->i_rollback();
11657 serialPorts[slot] = mSerialPorts[slot];
11658 }
11659
11660 if (mData->flModifications & IsModified_ParallelPorts)
11661 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11662 if ( mParallelPorts[slot]
11663 && mParallelPorts[slot]->i_isModified())
11664 {
11665 mParallelPorts[slot]->i_rollback();
11666 parallelPorts[slot] = mParallelPorts[slot];
11667 }
11668
11669 if (aNotify)
11670 {
11671 /* inform the direct session about changes */
11672
11673 ComObjPtr<Machine> that = this;
11674 uint32_t flModifications = mData->flModifications;
11675 alock.release();
11676
11677 if (flModifications & IsModified_SharedFolders)
11678 that->i_onSharedFolderChange();
11679
11680 if (flModifications & IsModified_VRDEServer)
11681 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11682 if (flModifications & IsModified_USB)
11683 that->i_onUSBControllerChange();
11684
11685 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11686 if (networkAdapters[slot])
11687 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11688 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11689 if (serialPorts[slot])
11690 that->i_onSerialPortChange(serialPorts[slot]);
11691 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11692 if (parallelPorts[slot])
11693 that->i_onParallelPortChange(parallelPorts[slot]);
11694
11695 if (flModifications & IsModified_Storage)
11696 {
11697 for (StorageControllerList::const_iterator
11698 it = mStorageControllers->begin();
11699 it != mStorageControllers->end();
11700 ++it)
11701 {
11702 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11703 }
11704 }
11705
11706
11707#if 0
11708 if (flModifications & IsModified_BandwidthControl)
11709 that->onBandwidthControlChange();
11710#endif
11711 }
11712}
11713
11714/**
11715 * Commits all the changes to machine settings.
11716 *
11717 * Note that this operation is supposed to never fail.
11718 *
11719 * @note Locks this object and children for writing.
11720 */
11721void Machine::i_commit()
11722{
11723 AutoCaller autoCaller(this);
11724 AssertComRCReturnVoid(autoCaller.rc());
11725
11726 AutoCaller peerCaller(mPeer);
11727 AssertComRCReturnVoid(peerCaller.rc());
11728
11729 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11730
11731 /*
11732 * use safe commit to ensure Snapshot machines (that share mUserData)
11733 * will still refer to a valid memory location
11734 */
11735 mUserData.commitCopy();
11736
11737 mHWData.commit();
11738
11739 if (mMediumAttachments.isBackedUp())
11740 i_commitMedia(Global::IsOnline(mData->mMachineState));
11741
11742 mBIOSSettings->i_commit();
11743 mRecordingSettings->i_commit();
11744 mGraphicsAdapter->i_commit();
11745 mVRDEServer->i_commit();
11746 mAudioAdapter->i_commit();
11747 mUSBDeviceFilters->i_commit();
11748 mBandwidthControl->i_commit();
11749
11750 /* Since mNetworkAdapters is a list which might have been changed (resized)
11751 * without using the Backupable<> template we need to handle the copying
11752 * of the list entries manually, including the creation of peers for the
11753 * new objects. */
11754 bool commitNetworkAdapters = false;
11755 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11756 if (mPeer)
11757 {
11758 /* commit everything, even the ones which will go away */
11759 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11760 mNetworkAdapters[slot]->i_commit();
11761 /* copy over the new entries, creating a peer and uninit the original */
11762 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11763 for (size_t slot = 0; slot < newSize; slot++)
11764 {
11765 /* look if this adapter has a peer device */
11766 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11767 if (!peer)
11768 {
11769 /* no peer means the adapter is a newly created one;
11770 * create a peer owning data this data share it with */
11771 peer.createObject();
11772 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11773 }
11774 mPeer->mNetworkAdapters[slot] = peer;
11775 }
11776 /* uninit any no longer needed network adapters */
11777 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11778 mNetworkAdapters[slot]->uninit();
11779 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11780 {
11781 if (mPeer->mNetworkAdapters[slot])
11782 mPeer->mNetworkAdapters[slot]->uninit();
11783 }
11784 /* Keep the original network adapter count until this point, so that
11785 * discarding a chipset type change will not lose settings. */
11786 mNetworkAdapters.resize(newSize);
11787 mPeer->mNetworkAdapters.resize(newSize);
11788 }
11789 else
11790 {
11791 /* we have no peer (our parent is the newly created machine);
11792 * just commit changes to the network adapters */
11793 commitNetworkAdapters = true;
11794 }
11795 if (commitNetworkAdapters)
11796 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11797 mNetworkAdapters[slot]->i_commit();
11798
11799 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11800 mSerialPorts[slot]->i_commit();
11801 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11802 mParallelPorts[slot]->i_commit();
11803
11804 bool commitStorageControllers = false;
11805
11806 if (mStorageControllers.isBackedUp())
11807 {
11808 mStorageControllers.commit();
11809
11810 if (mPeer)
11811 {
11812 /* Commit all changes to new controllers (this will reshare data with
11813 * peers for those who have peers) */
11814 StorageControllerList *newList = new StorageControllerList();
11815 for (StorageControllerList::const_iterator
11816 it = mStorageControllers->begin();
11817 it != mStorageControllers->end();
11818 ++it)
11819 {
11820 (*it)->i_commit();
11821
11822 /* look if this controller has a peer device */
11823 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11824 if (!peer)
11825 {
11826 /* no peer means the device is a newly created one;
11827 * create a peer owning data this device share it with */
11828 peer.createObject();
11829 peer->init(mPeer, *it, true /* aReshare */);
11830 }
11831 else
11832 {
11833 /* remove peer from the old list */
11834 mPeer->mStorageControllers->remove(peer);
11835 }
11836 /* and add it to the new list */
11837 newList->push_back(peer);
11838 }
11839
11840 /* uninit old peer's controllers that are left */
11841 for (StorageControllerList::const_iterator
11842 it = mPeer->mStorageControllers->begin();
11843 it != mPeer->mStorageControllers->end();
11844 ++it)
11845 {
11846 (*it)->uninit();
11847 }
11848
11849 /* attach new list of controllers to our peer */
11850 mPeer->mStorageControllers.attach(newList);
11851 }
11852 else
11853 {
11854 /* we have no peer (our parent is the newly created machine);
11855 * just commit changes to devices */
11856 commitStorageControllers = true;
11857 }
11858 }
11859 else
11860 {
11861 /* the list of controllers itself is not changed,
11862 * just commit changes to controllers themselves */
11863 commitStorageControllers = true;
11864 }
11865
11866 if (commitStorageControllers)
11867 {
11868 for (StorageControllerList::const_iterator
11869 it = mStorageControllers->begin();
11870 it != mStorageControllers->end();
11871 ++it)
11872 {
11873 (*it)->i_commit();
11874 }
11875 }
11876
11877 bool commitUSBControllers = false;
11878
11879 if (mUSBControllers.isBackedUp())
11880 {
11881 mUSBControllers.commit();
11882
11883 if (mPeer)
11884 {
11885 /* Commit all changes to new controllers (this will reshare data with
11886 * peers for those who have peers) */
11887 USBControllerList *newList = new USBControllerList();
11888 for (USBControllerList::const_iterator
11889 it = mUSBControllers->begin();
11890 it != mUSBControllers->end();
11891 ++it)
11892 {
11893 (*it)->i_commit();
11894
11895 /* look if this controller has a peer device */
11896 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11897 if (!peer)
11898 {
11899 /* no peer means the device is a newly created one;
11900 * create a peer owning data this device share it with */
11901 peer.createObject();
11902 peer->init(mPeer, *it, true /* aReshare */);
11903 }
11904 else
11905 {
11906 /* remove peer from the old list */
11907 mPeer->mUSBControllers->remove(peer);
11908 }
11909 /* and add it to the new list */
11910 newList->push_back(peer);
11911 }
11912
11913 /* uninit old peer's controllers that are left */
11914 for (USBControllerList::const_iterator
11915 it = mPeer->mUSBControllers->begin();
11916 it != mPeer->mUSBControllers->end();
11917 ++it)
11918 {
11919 (*it)->uninit();
11920 }
11921
11922 /* attach new list of controllers to our peer */
11923 mPeer->mUSBControllers.attach(newList);
11924 }
11925 else
11926 {
11927 /* we have no peer (our parent is the newly created machine);
11928 * just commit changes to devices */
11929 commitUSBControllers = true;
11930 }
11931 }
11932 else
11933 {
11934 /* the list of controllers itself is not changed,
11935 * just commit changes to controllers themselves */
11936 commitUSBControllers = true;
11937 }
11938
11939 if (commitUSBControllers)
11940 {
11941 for (USBControllerList::const_iterator
11942 it = mUSBControllers->begin();
11943 it != mUSBControllers->end();
11944 ++it)
11945 {
11946 (*it)->i_commit();
11947 }
11948 }
11949
11950 if (i_isSessionMachine())
11951 {
11952 /* attach new data to the primary machine and reshare it */
11953 mPeer->mUserData.attach(mUserData);
11954 mPeer->mHWData.attach(mHWData);
11955 /* mmMediumAttachments is reshared by fixupMedia */
11956 // mPeer->mMediumAttachments.attach(mMediumAttachments);
11957 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
11958 }
11959}
11960
11961/**
11962 * Copies all the hardware data from the given machine.
11963 *
11964 * Currently, only called when the VM is being restored from a snapshot. In
11965 * particular, this implies that the VM is not running during this method's
11966 * call.
11967 *
11968 * @note This method must be called from under this object's lock.
11969 *
11970 * @note This method doesn't call #i_commit(), so all data remains backed up and
11971 * unsaved.
11972 */
11973void Machine::i_copyFrom(Machine *aThat)
11974{
11975 AssertReturnVoid(!i_isSnapshotMachine());
11976 AssertReturnVoid(aThat->i_isSnapshotMachine());
11977
11978 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11979
11980 mHWData.assignCopy(aThat->mHWData);
11981
11982 // create copies of all shared folders (mHWData after attaching a copy
11983 // contains just references to original objects)
11984 for (HWData::SharedFolderList::iterator
11985 it = mHWData->mSharedFolders.begin();
11986 it != mHWData->mSharedFolders.end();
11987 ++it)
11988 {
11989 ComObjPtr<SharedFolder> folder;
11990 folder.createObject();
11991 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11992 AssertComRC(rc);
11993 *it = folder;
11994 }
11995
11996 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11997 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
11998 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
11999 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12000 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12001 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12002 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12003
12004 /* create private copies of all controllers */
12005 mStorageControllers.backup();
12006 mStorageControllers->clear();
12007 for (StorageControllerList::const_iterator
12008 it = aThat->mStorageControllers->begin();
12009 it != aThat->mStorageControllers->end();
12010 ++it)
12011 {
12012 ComObjPtr<StorageController> ctrl;
12013 ctrl.createObject();
12014 ctrl->initCopy(this, *it);
12015 mStorageControllers->push_back(ctrl);
12016 }
12017
12018 /* create private copies of all USB controllers */
12019 mUSBControllers.backup();
12020 mUSBControllers->clear();
12021 for (USBControllerList::const_iterator
12022 it = aThat->mUSBControllers->begin();
12023 it != aThat->mUSBControllers->end();
12024 ++it)
12025 {
12026 ComObjPtr<USBController> ctrl;
12027 ctrl.createObject();
12028 ctrl->initCopy(this, *it);
12029 mUSBControllers->push_back(ctrl);
12030 }
12031
12032 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12033 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12034 {
12035 if (mNetworkAdapters[slot].isNotNull())
12036 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12037 else
12038 {
12039 unconst(mNetworkAdapters[slot]).createObject();
12040 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12041 }
12042 }
12043 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12044 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12045 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12046 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12047}
12048
12049/**
12050 * Returns whether the given storage controller is hotplug capable.
12051 *
12052 * @returns true if the controller supports hotplugging
12053 * false otherwise.
12054 * @param enmCtrlType The controller type to check for.
12055 */
12056bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12057{
12058 ComPtr<ISystemProperties> systemProperties;
12059 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12060 if (FAILED(rc))
12061 return false;
12062
12063 BOOL aHotplugCapable = FALSE;
12064 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12065
12066 return RT_BOOL(aHotplugCapable);
12067}
12068
12069#ifdef VBOX_WITH_RESOURCE_USAGE_API
12070
12071void Machine::i_getDiskList(MediaList &list)
12072{
12073 for (MediumAttachmentList::const_iterator
12074 it = mMediumAttachments->begin();
12075 it != mMediumAttachments->end();
12076 ++it)
12077 {
12078 MediumAttachment *pAttach = *it;
12079 /* just in case */
12080 AssertContinue(pAttach);
12081
12082 AutoCaller localAutoCallerA(pAttach);
12083 if (FAILED(localAutoCallerA.rc())) continue;
12084
12085 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12086
12087 if (pAttach->i_getType() == DeviceType_HardDisk)
12088 list.push_back(pAttach->i_getMedium());
12089 }
12090}
12091
12092void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12093{
12094 AssertReturnVoid(isWriteLockOnCurrentThread());
12095 AssertPtrReturnVoid(aCollector);
12096
12097 pm::CollectorHAL *hal = aCollector->getHAL();
12098 /* Create sub metrics */
12099 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12100 "Percentage of processor time spent in user mode by the VM process.");
12101 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12102 "Percentage of processor time spent in kernel mode by the VM process.");
12103 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12104 "Size of resident portion of VM process in memory.");
12105 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12106 "Actual size of all VM disks combined.");
12107 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12108 "Network receive rate.");
12109 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12110 "Network transmit rate.");
12111 /* Create and register base metrics */
12112 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12113 cpuLoadUser, cpuLoadKernel);
12114 aCollector->registerBaseMetric(cpuLoad);
12115 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12116 ramUsageUsed);
12117 aCollector->registerBaseMetric(ramUsage);
12118 MediaList disks;
12119 i_getDiskList(disks);
12120 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12121 diskUsageUsed);
12122 aCollector->registerBaseMetric(diskUsage);
12123
12124 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12125 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12126 new pm::AggregateAvg()));
12127 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12128 new pm::AggregateMin()));
12129 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12130 new pm::AggregateMax()));
12131 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12132 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12133 new pm::AggregateAvg()));
12134 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12135 new pm::AggregateMin()));
12136 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12137 new pm::AggregateMax()));
12138
12139 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12140 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12141 new pm::AggregateAvg()));
12142 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12143 new pm::AggregateMin()));
12144 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12145 new pm::AggregateMax()));
12146
12147 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12148 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12149 new pm::AggregateAvg()));
12150 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12151 new pm::AggregateMin()));
12152 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12153 new pm::AggregateMax()));
12154
12155
12156 /* Guest metrics collector */
12157 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12158 aCollector->registerGuest(mCollectorGuest);
12159 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12160
12161 /* Create sub metrics */
12162 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12163 "Percentage of processor time spent in user mode as seen by the guest.");
12164 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12165 "Percentage of processor time spent in kernel mode as seen by the guest.");
12166 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12167 "Percentage of processor time spent idling as seen by the guest.");
12168
12169 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12170 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12171 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12172 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12173 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12174 pm::SubMetric *guestMemCache = new pm::SubMetric(
12175 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12176
12177 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12178 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12179
12180 /* Create and register base metrics */
12181 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12182 machineNetRx, machineNetTx);
12183 aCollector->registerBaseMetric(machineNetRate);
12184
12185 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12186 guestLoadUser, guestLoadKernel, guestLoadIdle);
12187 aCollector->registerBaseMetric(guestCpuLoad);
12188
12189 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12190 guestMemTotal, guestMemFree,
12191 guestMemBalloon, guestMemShared,
12192 guestMemCache, guestPagedTotal);
12193 aCollector->registerBaseMetric(guestCpuMem);
12194
12195 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12196 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12197 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12198 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12199
12200 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12201 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12202 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12203 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12204
12205 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12206 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12207 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12208 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12209
12210 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12211 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12212 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12213 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12214
12215 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12216 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12217 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12218 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12219
12220 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12221 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12222 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12223 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12224
12225 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12226 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12227 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12228 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12229
12230 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12231 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12233 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12234
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12239
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12244
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12246 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12249}
12250
12251void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12252{
12253 AssertReturnVoid(isWriteLockOnCurrentThread());
12254
12255 if (aCollector)
12256 {
12257 aCollector->unregisterMetricsFor(aMachine);
12258 aCollector->unregisterBaseMetricsFor(aMachine);
12259 }
12260}
12261
12262#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12263
12264
12265////////////////////////////////////////////////////////////////////////////////
12266
12267DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12268
12269HRESULT SessionMachine::FinalConstruct()
12270{
12271 LogFlowThisFunc(("\n"));
12272
12273 mClientToken = NULL;
12274
12275 return BaseFinalConstruct();
12276}
12277
12278void SessionMachine::FinalRelease()
12279{
12280 LogFlowThisFunc(("\n"));
12281
12282 Assert(!mClientToken);
12283 /* paranoia, should not hang around any more */
12284 if (mClientToken)
12285 {
12286 delete mClientToken;
12287 mClientToken = NULL;
12288 }
12289
12290 uninit(Uninit::Unexpected);
12291
12292 BaseFinalRelease();
12293}
12294
12295/**
12296 * @note Must be called only by Machine::LockMachine() from its own write lock.
12297 */
12298HRESULT SessionMachine::init(Machine *aMachine)
12299{
12300 LogFlowThisFuncEnter();
12301 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12302
12303 AssertReturn(aMachine, E_INVALIDARG);
12304
12305 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12306
12307 /* Enclose the state transition NotReady->InInit->Ready */
12308 AutoInitSpan autoInitSpan(this);
12309 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12310
12311 HRESULT rc = S_OK;
12312
12313 RT_ZERO(mAuthLibCtx);
12314
12315 /* create the machine client token */
12316 try
12317 {
12318 mClientToken = new ClientToken(aMachine, this);
12319 if (!mClientToken->isReady())
12320 {
12321 delete mClientToken;
12322 mClientToken = NULL;
12323 rc = E_FAIL;
12324 }
12325 }
12326 catch (std::bad_alloc &)
12327 {
12328 rc = E_OUTOFMEMORY;
12329 }
12330 if (FAILED(rc))
12331 return rc;
12332
12333 /* memorize the peer Machine */
12334 unconst(mPeer) = aMachine;
12335 /* share the parent pointer */
12336 unconst(mParent) = aMachine->mParent;
12337
12338 /* take the pointers to data to share */
12339 mData.share(aMachine->mData);
12340 mSSData.share(aMachine->mSSData);
12341
12342 mUserData.share(aMachine->mUserData);
12343 mHWData.share(aMachine->mHWData);
12344 mMediumAttachments.share(aMachine->mMediumAttachments);
12345
12346 mStorageControllers.allocate();
12347 for (StorageControllerList::const_iterator
12348 it = aMachine->mStorageControllers->begin();
12349 it != aMachine->mStorageControllers->end();
12350 ++it)
12351 {
12352 ComObjPtr<StorageController> ctl;
12353 ctl.createObject();
12354 ctl->init(this, *it);
12355 mStorageControllers->push_back(ctl);
12356 }
12357
12358 mUSBControllers.allocate();
12359 for (USBControllerList::const_iterator
12360 it = aMachine->mUSBControllers->begin();
12361 it != aMachine->mUSBControllers->end();
12362 ++it)
12363 {
12364 ComObjPtr<USBController> ctl;
12365 ctl.createObject();
12366 ctl->init(this, *it);
12367 mUSBControllers->push_back(ctl);
12368 }
12369
12370 unconst(mBIOSSettings).createObject();
12371 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12372 unconst(mRecordingSettings).createObject();
12373 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12374 /* create another GraphicsAdapter object that will be mutable */
12375 unconst(mGraphicsAdapter).createObject();
12376 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12377 /* create another VRDEServer object that will be mutable */
12378 unconst(mVRDEServer).createObject();
12379 mVRDEServer->init(this, aMachine->mVRDEServer);
12380 /* create another audio adapter object that will be mutable */
12381 unconst(mAudioAdapter).createObject();
12382 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12383 /* create a list of serial ports that will be mutable */
12384 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12385 {
12386 unconst(mSerialPorts[slot]).createObject();
12387 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12388 }
12389 /* create a list of parallel ports that will be mutable */
12390 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12391 {
12392 unconst(mParallelPorts[slot]).createObject();
12393 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12394 }
12395
12396 /* create another USB device filters object that will be mutable */
12397 unconst(mUSBDeviceFilters).createObject();
12398 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12399
12400 /* create a list of network adapters that will be mutable */
12401 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12402 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12403 {
12404 unconst(mNetworkAdapters[slot]).createObject();
12405 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12406 }
12407
12408 /* create another bandwidth control object that will be mutable */
12409 unconst(mBandwidthControl).createObject();
12410 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12411
12412 /* default is to delete saved state on Saved -> PoweredOff transition */
12413 mRemoveSavedState = true;
12414
12415 /* Confirm a successful initialization when it's the case */
12416 autoInitSpan.setSucceeded();
12417
12418 miNATNetworksStarted = 0;
12419
12420 LogFlowThisFuncLeave();
12421 return rc;
12422}
12423
12424/**
12425 * Uninitializes this session object. If the reason is other than
12426 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12427 * or the client watcher code.
12428 *
12429 * @param aReason uninitialization reason
12430 *
12431 * @note Locks mParent + this object for writing.
12432 */
12433void SessionMachine::uninit(Uninit::Reason aReason)
12434{
12435 LogFlowThisFuncEnter();
12436 LogFlowThisFunc(("reason=%d\n", aReason));
12437
12438 /*
12439 * Strongly reference ourselves to prevent this object deletion after
12440 * mData->mSession.mMachine.setNull() below (which can release the last
12441 * reference and call the destructor). Important: this must be done before
12442 * accessing any members (and before AutoUninitSpan that does it as well).
12443 * This self reference will be released as the very last step on return.
12444 */
12445 ComObjPtr<SessionMachine> selfRef;
12446 if (aReason != Uninit::Unexpected)
12447 selfRef = this;
12448
12449 /* Enclose the state transition Ready->InUninit->NotReady */
12450 AutoUninitSpan autoUninitSpan(this);
12451 if (autoUninitSpan.uninitDone())
12452 {
12453 LogFlowThisFunc(("Already uninitialized\n"));
12454 LogFlowThisFuncLeave();
12455 return;
12456 }
12457
12458 if (autoUninitSpan.initFailed())
12459 {
12460 /* We've been called by init() because it's failed. It's not really
12461 * necessary (nor it's safe) to perform the regular uninit sequence
12462 * below, the following is enough.
12463 */
12464 LogFlowThisFunc(("Initialization failed.\n"));
12465 /* destroy the machine client token */
12466 if (mClientToken)
12467 {
12468 delete mClientToken;
12469 mClientToken = NULL;
12470 }
12471 uninitDataAndChildObjects();
12472 mData.free();
12473 unconst(mParent) = NULL;
12474 unconst(mPeer) = NULL;
12475 LogFlowThisFuncLeave();
12476 return;
12477 }
12478
12479 MachineState_T lastState;
12480 {
12481 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12482 lastState = mData->mMachineState;
12483 }
12484 NOREF(lastState);
12485
12486#ifdef VBOX_WITH_USB
12487 // release all captured USB devices, but do this before requesting the locks below
12488 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12489 {
12490 /* Console::captureUSBDevices() is called in the VM process only after
12491 * setting the machine state to Starting or Restoring.
12492 * Console::detachAllUSBDevices() will be called upon successful
12493 * termination. So, we need to release USB devices only if there was
12494 * an abnormal termination of a running VM.
12495 *
12496 * This is identical to SessionMachine::DetachAllUSBDevices except
12497 * for the aAbnormal argument. */
12498 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12499 AssertComRC(rc);
12500 NOREF(rc);
12501
12502 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12503 if (service)
12504 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12505 }
12506#endif /* VBOX_WITH_USB */
12507
12508 // we need to lock this object in uninit() because the lock is shared
12509 // with mPeer (as well as data we modify below). mParent lock is needed
12510 // by several calls to it.
12511 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12512
12513#ifdef VBOX_WITH_RESOURCE_USAGE_API
12514 /*
12515 * It is safe to call Machine::i_unregisterMetrics() here because
12516 * PerformanceCollector::samplerCallback no longer accesses guest methods
12517 * holding the lock.
12518 */
12519 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12520 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12521 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12522 if (mCollectorGuest)
12523 {
12524 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12525 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12526 mCollectorGuest = NULL;
12527 }
12528#endif
12529
12530 if (aReason == Uninit::Abnormal)
12531 {
12532 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12533
12534 /* reset the state to Aborted */
12535 if (mData->mMachineState != MachineState_Aborted)
12536 i_setMachineState(MachineState_Aborted);
12537 }
12538
12539 // any machine settings modified?
12540 if (mData->flModifications)
12541 {
12542 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12543 i_rollback(false /* aNotify */);
12544 }
12545
12546 mData->mSession.mPID = NIL_RTPROCESS;
12547
12548 if (aReason == Uninit::Unexpected)
12549 {
12550 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12551 * client watcher thread to update the set of machines that have open
12552 * sessions. */
12553 mParent->i_updateClientWatcher();
12554 }
12555
12556 /* uninitialize all remote controls */
12557 if (mData->mSession.mRemoteControls.size())
12558 {
12559 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12560 mData->mSession.mRemoteControls.size()));
12561
12562 /* Always restart a the beginning, since the iterator is invalidated
12563 * by using erase(). */
12564 for (Data::Session::RemoteControlList::iterator
12565 it = mData->mSession.mRemoteControls.begin();
12566 it != mData->mSession.mRemoteControls.end();
12567 it = mData->mSession.mRemoteControls.begin())
12568 {
12569 ComPtr<IInternalSessionControl> pControl = *it;
12570 mData->mSession.mRemoteControls.erase(it);
12571 multilock.release();
12572 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12573 HRESULT rc = pControl->Uninitialize();
12574 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12575 if (FAILED(rc))
12576 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12577 multilock.acquire();
12578 }
12579 mData->mSession.mRemoteControls.clear();
12580 }
12581
12582 /* Remove all references to the NAT network service. The service will stop
12583 * if all references (also from other VMs) are removed. */
12584 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12585 {
12586 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12587 {
12588 BOOL enabled;
12589 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12590 if ( FAILED(hrc)
12591 || !enabled)
12592 continue;
12593
12594 NetworkAttachmentType_T type;
12595 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12596 if ( SUCCEEDED(hrc)
12597 && type == NetworkAttachmentType_NATNetwork)
12598 {
12599 Bstr name;
12600 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12601 if (SUCCEEDED(hrc))
12602 {
12603 multilock.release();
12604 Utf8Str strName(name);
12605 LogRel(("VM '%s' stops using NAT network '%s'\n",
12606 mUserData->s.strName.c_str(), strName.c_str()));
12607 mParent->i_natNetworkRefDec(strName);
12608 multilock.acquire();
12609 }
12610 }
12611 }
12612 }
12613
12614 /*
12615 * An expected uninitialization can come only from #i_checkForDeath().
12616 * Otherwise it means that something's gone really wrong (for example,
12617 * the Session implementation has released the VirtualBox reference
12618 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12619 * etc). However, it's also possible, that the client releases the IPC
12620 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12621 * but the VirtualBox release event comes first to the server process.
12622 * This case is practically possible, so we should not assert on an
12623 * unexpected uninit, just log a warning.
12624 */
12625
12626 if (aReason == Uninit::Unexpected)
12627 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12628
12629 if (aReason != Uninit::Normal)
12630 {
12631 mData->mSession.mDirectControl.setNull();
12632 }
12633 else
12634 {
12635 /* this must be null here (see #OnSessionEnd()) */
12636 Assert(mData->mSession.mDirectControl.isNull());
12637 Assert(mData->mSession.mState == SessionState_Unlocking);
12638 Assert(!mData->mSession.mProgress.isNull());
12639 }
12640 if (mData->mSession.mProgress)
12641 {
12642 if (aReason == Uninit::Normal)
12643 mData->mSession.mProgress->i_notifyComplete(S_OK);
12644 else
12645 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12646 COM_IIDOF(ISession),
12647 getComponentName(),
12648 tr("The VM session was aborted"));
12649 mData->mSession.mProgress.setNull();
12650 }
12651
12652 if (mConsoleTaskData.mProgress)
12653 {
12654 Assert(aReason == Uninit::Abnormal);
12655 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12656 COM_IIDOF(ISession),
12657 getComponentName(),
12658 tr("The VM session was aborted"));
12659 mConsoleTaskData.mProgress.setNull();
12660 }
12661
12662 /* remove the association between the peer machine and this session machine */
12663 Assert( (SessionMachine*)mData->mSession.mMachine == this
12664 || aReason == Uninit::Unexpected);
12665
12666 /* reset the rest of session data */
12667 mData->mSession.mLockType = LockType_Null;
12668 mData->mSession.mMachine.setNull();
12669 mData->mSession.mState = SessionState_Unlocked;
12670 mData->mSession.mName.setNull();
12671
12672 /* destroy the machine client token before leaving the exclusive lock */
12673 if (mClientToken)
12674 {
12675 delete mClientToken;
12676 mClientToken = NULL;
12677 }
12678
12679 /* fire an event */
12680 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12681
12682 uninitDataAndChildObjects();
12683
12684 /* free the essential data structure last */
12685 mData.free();
12686
12687 /* release the exclusive lock before setting the below two to NULL */
12688 multilock.release();
12689
12690 unconst(mParent) = NULL;
12691 unconst(mPeer) = NULL;
12692
12693 AuthLibUnload(&mAuthLibCtx);
12694
12695 LogFlowThisFuncLeave();
12696}
12697
12698// util::Lockable interface
12699////////////////////////////////////////////////////////////////////////////////
12700
12701/**
12702 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12703 * with the primary Machine instance (mPeer).
12704 */
12705RWLockHandle *SessionMachine::lockHandle() const
12706{
12707 AssertReturn(mPeer != NULL, NULL);
12708 return mPeer->lockHandle();
12709}
12710
12711// IInternalMachineControl methods
12712////////////////////////////////////////////////////////////////////////////////
12713
12714/**
12715 * Passes collected guest statistics to performance collector object
12716 */
12717HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12718 ULONG aCpuKernel, ULONG aCpuIdle,
12719 ULONG aMemTotal, ULONG aMemFree,
12720 ULONG aMemBalloon, ULONG aMemShared,
12721 ULONG aMemCache, ULONG aPageTotal,
12722 ULONG aAllocVMM, ULONG aFreeVMM,
12723 ULONG aBalloonedVMM, ULONG aSharedVMM,
12724 ULONG aVmNetRx, ULONG aVmNetTx)
12725{
12726#ifdef VBOX_WITH_RESOURCE_USAGE_API
12727 if (mCollectorGuest)
12728 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12729 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12730 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12731 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12732
12733 return S_OK;
12734#else
12735 NOREF(aValidStats);
12736 NOREF(aCpuUser);
12737 NOREF(aCpuKernel);
12738 NOREF(aCpuIdle);
12739 NOREF(aMemTotal);
12740 NOREF(aMemFree);
12741 NOREF(aMemBalloon);
12742 NOREF(aMemShared);
12743 NOREF(aMemCache);
12744 NOREF(aPageTotal);
12745 NOREF(aAllocVMM);
12746 NOREF(aFreeVMM);
12747 NOREF(aBalloonedVMM);
12748 NOREF(aSharedVMM);
12749 NOREF(aVmNetRx);
12750 NOREF(aVmNetTx);
12751 return E_NOTIMPL;
12752#endif
12753}
12754
12755////////////////////////////////////////////////////////////////////////////////
12756//
12757// SessionMachine task records
12758//
12759////////////////////////////////////////////////////////////////////////////////
12760
12761/**
12762 * Task record for saving the machine state.
12763 */
12764class SessionMachine::SaveStateTask
12765 : public Machine::Task
12766{
12767public:
12768 SaveStateTask(SessionMachine *m,
12769 Progress *p,
12770 const Utf8Str &t,
12771 Reason_T enmReason,
12772 const Utf8Str &strStateFilePath)
12773 : Task(m, p, t),
12774 m_enmReason(enmReason),
12775 m_strStateFilePath(strStateFilePath)
12776 {}
12777
12778private:
12779 void handler()
12780 {
12781 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12782 }
12783
12784 Reason_T m_enmReason;
12785 Utf8Str m_strStateFilePath;
12786
12787 friend class SessionMachine;
12788};
12789
12790/**
12791 * Task thread implementation for SessionMachine::SaveState(), called from
12792 * SessionMachine::taskHandler().
12793 *
12794 * @note Locks this object for writing.
12795 *
12796 * @param task
12797 * @return
12798 */
12799void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12800{
12801 LogFlowThisFuncEnter();
12802
12803 AutoCaller autoCaller(this);
12804 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12805 if (FAILED(autoCaller.rc()))
12806 {
12807 /* we might have been uninitialized because the session was accidentally
12808 * closed by the client, so don't assert */
12809 HRESULT rc = setError(E_FAIL,
12810 tr("The session has been accidentally closed"));
12811 task.m_pProgress->i_notifyComplete(rc);
12812 LogFlowThisFuncLeave();
12813 return;
12814 }
12815
12816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12817
12818 HRESULT rc = S_OK;
12819
12820 try
12821 {
12822 ComPtr<IInternalSessionControl> directControl;
12823 if (mData->mSession.mLockType == LockType_VM)
12824 directControl = mData->mSession.mDirectControl;
12825 if (directControl.isNull())
12826 throw setError(VBOX_E_INVALID_VM_STATE,
12827 tr("Trying to save state without a running VM"));
12828 alock.release();
12829 BOOL fSuspendedBySave;
12830 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12831 Assert(!fSuspendedBySave);
12832 alock.acquire();
12833
12834 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12835 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12836 throw E_FAIL);
12837
12838 if (SUCCEEDED(rc))
12839 {
12840 mSSData->strStateFilePath = task.m_strStateFilePath;
12841
12842 /* save all VM settings */
12843 rc = i_saveSettings(NULL);
12844 // no need to check whether VirtualBox.xml needs saving also since
12845 // we can't have a name change pending at this point
12846 }
12847 else
12848 {
12849 // On failure, set the state to the state we had at the beginning.
12850 i_setMachineState(task.m_machineStateBackup);
12851 i_updateMachineStateOnClient();
12852
12853 // Delete the saved state file (might have been already created).
12854 // No need to check whether this is shared with a snapshot here
12855 // because we certainly created a fresh saved state file here.
12856 RTFileDelete(task.m_strStateFilePath.c_str());
12857 }
12858 }
12859 catch (HRESULT aRC) { rc = aRC; }
12860
12861 task.m_pProgress->i_notifyComplete(rc);
12862
12863 LogFlowThisFuncLeave();
12864}
12865
12866/**
12867 * @note Locks this object for writing.
12868 */
12869HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12870{
12871 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12872}
12873
12874HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12875{
12876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12877
12878 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12879 if (FAILED(rc)) return rc;
12880
12881 if ( mData->mMachineState != MachineState_Running
12882 && mData->mMachineState != MachineState_Paused
12883 )
12884 return setError(VBOX_E_INVALID_VM_STATE,
12885 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12886 Global::stringifyMachineState(mData->mMachineState));
12887
12888 ComObjPtr<Progress> pProgress;
12889 pProgress.createObject();
12890 rc = pProgress->init(i_getVirtualBox(),
12891 static_cast<IMachine *>(this) /* aInitiator */,
12892 tr("Saving the execution state of the virtual machine"),
12893 FALSE /* aCancelable */);
12894 if (FAILED(rc))
12895 return rc;
12896
12897 Utf8Str strStateFilePath;
12898 i_composeSavedStateFilename(strStateFilePath);
12899
12900 /* create and start the task on a separate thread (note that it will not
12901 * start working until we release alock) */
12902 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12903 rc = pTask->createThread();
12904 if (FAILED(rc))
12905 return rc;
12906
12907 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12908 i_setMachineState(MachineState_Saving);
12909 i_updateMachineStateOnClient();
12910
12911 pProgress.queryInterfaceTo(aProgress.asOutParam());
12912
12913 return S_OK;
12914}
12915
12916/**
12917 * @note Locks this object for writing.
12918 */
12919HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12920{
12921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12922
12923 HRESULT rc = i_checkStateDependency(MutableStateDep);
12924 if (FAILED(rc)) return rc;
12925
12926 if ( mData->mMachineState != MachineState_PoweredOff
12927 && mData->mMachineState != MachineState_Teleported
12928 && mData->mMachineState != MachineState_Aborted
12929 )
12930 return setError(VBOX_E_INVALID_VM_STATE,
12931 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12932 Global::stringifyMachineState(mData->mMachineState));
12933
12934 com::Utf8Str stateFilePathFull;
12935 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12936 if (RT_FAILURE(vrc))
12937 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12938 tr("Invalid saved state file path '%s' (%Rrc)"),
12939 aSavedStateFile.c_str(),
12940 vrc);
12941
12942 mSSData->strStateFilePath = stateFilePathFull;
12943
12944 /* The below i_setMachineState() will detect the state transition and will
12945 * update the settings file */
12946
12947 return i_setMachineState(MachineState_Saved);
12948}
12949
12950/**
12951 * @note Locks this object for writing.
12952 */
12953HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12954{
12955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12956
12957 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12958 if (FAILED(rc)) return rc;
12959
12960 if (mData->mMachineState != MachineState_Saved)
12961 return setError(VBOX_E_INVALID_VM_STATE,
12962 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12963 Global::stringifyMachineState(mData->mMachineState));
12964
12965 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12966
12967 /*
12968 * Saved -> PoweredOff transition will be detected in the SessionMachine
12969 * and properly handled.
12970 */
12971 rc = i_setMachineState(MachineState_PoweredOff);
12972 return rc;
12973}
12974
12975
12976/**
12977 * @note Locks the same as #i_setMachineState() does.
12978 */
12979HRESULT SessionMachine::updateState(MachineState_T aState)
12980{
12981 return i_setMachineState(aState);
12982}
12983
12984/**
12985 * @note Locks this object for writing.
12986 */
12987HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12988{
12989 IProgress *pProgress(aProgress);
12990
12991 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12992
12993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12994
12995 if (mData->mSession.mState != SessionState_Locked)
12996 return VBOX_E_INVALID_OBJECT_STATE;
12997
12998 if (!mData->mSession.mProgress.isNull())
12999 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13000
13001 /* If we didn't reference the NAT network service yet, add a reference to
13002 * force a start */
13003 if (miNATNetworksStarted < 1)
13004 {
13005 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13006 {
13007 BOOL enabled;
13008 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13009 if ( FAILED(hrc)
13010 || !enabled)
13011 continue;
13012
13013 NetworkAttachmentType_T type;
13014 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13015 if ( SUCCEEDED(hrc)
13016 && type == NetworkAttachmentType_NATNetwork)
13017 {
13018 Bstr name;
13019 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13020 if (SUCCEEDED(hrc))
13021 {
13022 Utf8Str strName(name);
13023 LogRel(("VM '%s' starts using NAT network '%s'\n",
13024 mUserData->s.strName.c_str(), strName.c_str()));
13025 mPeer->lockHandle()->unlockWrite();
13026 mParent->i_natNetworkRefInc(strName);
13027#ifdef RT_LOCK_STRICT
13028 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13029#else
13030 mPeer->lockHandle()->lockWrite();
13031#endif
13032 }
13033 }
13034 }
13035 miNATNetworksStarted++;
13036 }
13037
13038 LogFlowThisFunc(("returns S_OK.\n"));
13039 return S_OK;
13040}
13041
13042/**
13043 * @note Locks this object for writing.
13044 */
13045HRESULT SessionMachine::endPowerUp(LONG aResult)
13046{
13047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13048
13049 if (mData->mSession.mState != SessionState_Locked)
13050 return VBOX_E_INVALID_OBJECT_STATE;
13051
13052 /* Finalize the LaunchVMProcess progress object. */
13053 if (mData->mSession.mProgress)
13054 {
13055 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13056 mData->mSession.mProgress.setNull();
13057 }
13058
13059 if (SUCCEEDED((HRESULT)aResult))
13060 {
13061#ifdef VBOX_WITH_RESOURCE_USAGE_API
13062 /* The VM has been powered up successfully, so it makes sense
13063 * now to offer the performance metrics for a running machine
13064 * object. Doing it earlier wouldn't be safe. */
13065 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13066 mData->mSession.mPID);
13067#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13068 }
13069
13070 return S_OK;
13071}
13072
13073/**
13074 * @note Locks this object for writing.
13075 */
13076HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13077{
13078 LogFlowThisFuncEnter();
13079
13080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13081
13082 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13083 E_FAIL);
13084
13085 /* create a progress object to track operation completion */
13086 ComObjPtr<Progress> pProgress;
13087 pProgress.createObject();
13088 pProgress->init(i_getVirtualBox(),
13089 static_cast<IMachine *>(this) /* aInitiator */,
13090 tr("Stopping the virtual machine"),
13091 FALSE /* aCancelable */);
13092
13093 /* fill in the console task data */
13094 mConsoleTaskData.mLastState = mData->mMachineState;
13095 mConsoleTaskData.mProgress = pProgress;
13096
13097 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13098 i_setMachineState(MachineState_Stopping);
13099
13100 pProgress.queryInterfaceTo(aProgress.asOutParam());
13101
13102 return S_OK;
13103}
13104
13105/**
13106 * @note Locks this object for writing.
13107 */
13108HRESULT SessionMachine::endPoweringDown(LONG aResult,
13109 const com::Utf8Str &aErrMsg)
13110{
13111 LogFlowThisFuncEnter();
13112
13113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13114
13115 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13116 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13117 && mConsoleTaskData.mLastState != MachineState_Null,
13118 E_FAIL);
13119
13120 /*
13121 * On failure, set the state to the state we had when BeginPoweringDown()
13122 * was called (this is expected by Console::PowerDown() and the associated
13123 * task). On success the VM process already changed the state to
13124 * MachineState_PoweredOff, so no need to do anything.
13125 */
13126 if (FAILED(aResult))
13127 i_setMachineState(mConsoleTaskData.mLastState);
13128
13129 /* notify the progress object about operation completion */
13130 Assert(mConsoleTaskData.mProgress);
13131 if (SUCCEEDED(aResult))
13132 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13133 else
13134 {
13135 if (aErrMsg.length())
13136 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13137 COM_IIDOF(ISession),
13138 getComponentName(),
13139 aErrMsg.c_str());
13140 else
13141 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13142 }
13143
13144 /* clear out the temporary saved state data */
13145 mConsoleTaskData.mLastState = MachineState_Null;
13146 mConsoleTaskData.mProgress.setNull();
13147
13148 LogFlowThisFuncLeave();
13149 return S_OK;
13150}
13151
13152
13153/**
13154 * Goes through the USB filters of the given machine to see if the given
13155 * device matches any filter or not.
13156 *
13157 * @note Locks the same as USBController::hasMatchingFilter() does.
13158 */
13159HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13160 BOOL *aMatched,
13161 ULONG *aMaskedInterfaces)
13162{
13163 LogFlowThisFunc(("\n"));
13164
13165#ifdef VBOX_WITH_USB
13166 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13167#else
13168 NOREF(aDevice);
13169 NOREF(aMaskedInterfaces);
13170 *aMatched = FALSE;
13171#endif
13172
13173 return S_OK;
13174}
13175
13176/**
13177 * @note Locks the same as Host::captureUSBDevice() does.
13178 */
13179HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13180{
13181 LogFlowThisFunc(("\n"));
13182
13183#ifdef VBOX_WITH_USB
13184 /* if captureDeviceForVM() fails, it must have set extended error info */
13185 clearError();
13186 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13187 if (FAILED(rc)) return rc;
13188
13189 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13190 AssertReturn(service, E_FAIL);
13191 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13192#else
13193 RT_NOREF(aId, aCaptureFilename);
13194 return E_NOTIMPL;
13195#endif
13196}
13197
13198/**
13199 * @note Locks the same as Host::detachUSBDevice() does.
13200 */
13201HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13202 BOOL aDone)
13203{
13204 LogFlowThisFunc(("\n"));
13205
13206#ifdef VBOX_WITH_USB
13207 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13208 AssertReturn(service, E_FAIL);
13209 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13210#else
13211 NOREF(aId);
13212 NOREF(aDone);
13213 return E_NOTIMPL;
13214#endif
13215}
13216
13217/**
13218 * Inserts all machine filters to the USB proxy service and then calls
13219 * Host::autoCaptureUSBDevices().
13220 *
13221 * Called by Console from the VM process upon VM startup.
13222 *
13223 * @note Locks what called methods lock.
13224 */
13225HRESULT SessionMachine::autoCaptureUSBDevices()
13226{
13227 LogFlowThisFunc(("\n"));
13228
13229#ifdef VBOX_WITH_USB
13230 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13231 AssertComRC(rc);
13232 NOREF(rc);
13233
13234 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13235 AssertReturn(service, E_FAIL);
13236 return service->autoCaptureDevicesForVM(this);
13237#else
13238 return S_OK;
13239#endif
13240}
13241
13242/**
13243 * Removes all machine filters from the USB proxy service and then calls
13244 * Host::detachAllUSBDevices().
13245 *
13246 * Called by Console from the VM process upon normal VM termination or by
13247 * SessionMachine::uninit() upon abnormal VM termination (from under the
13248 * Machine/SessionMachine lock).
13249 *
13250 * @note Locks what called methods lock.
13251 */
13252HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13253{
13254 LogFlowThisFunc(("\n"));
13255
13256#ifdef VBOX_WITH_USB
13257 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13258 AssertComRC(rc);
13259 NOREF(rc);
13260
13261 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13262 AssertReturn(service, E_FAIL);
13263 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13264#else
13265 NOREF(aDone);
13266 return S_OK;
13267#endif
13268}
13269
13270/**
13271 * @note Locks this object for writing.
13272 */
13273HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13274 ComPtr<IProgress> &aProgress)
13275{
13276 LogFlowThisFuncEnter();
13277
13278 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13279 /*
13280 * We don't assert below because it might happen that a non-direct session
13281 * informs us it is closed right after we've been uninitialized -- it's ok.
13282 */
13283
13284 /* get IInternalSessionControl interface */
13285 ComPtr<IInternalSessionControl> control(aSession);
13286
13287 ComAssertRet(!control.isNull(), E_INVALIDARG);
13288
13289 /* Creating a Progress object requires the VirtualBox lock, and
13290 * thus locking it here is required by the lock order rules. */
13291 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13292
13293 if (control == mData->mSession.mDirectControl)
13294 {
13295 /* The direct session is being normally closed by the client process
13296 * ----------------------------------------------------------------- */
13297
13298 /* go to the closing state (essential for all open*Session() calls and
13299 * for #i_checkForDeath()) */
13300 Assert(mData->mSession.mState == SessionState_Locked);
13301 mData->mSession.mState = SessionState_Unlocking;
13302
13303 /* set direct control to NULL to release the remote instance */
13304 mData->mSession.mDirectControl.setNull();
13305 LogFlowThisFunc(("Direct control is set to NULL\n"));
13306
13307 if (mData->mSession.mProgress)
13308 {
13309 /* finalize the progress, someone might wait if a frontend
13310 * closes the session before powering on the VM. */
13311 mData->mSession.mProgress->notifyComplete(E_FAIL,
13312 COM_IIDOF(ISession),
13313 getComponentName(),
13314 tr("The VM session was closed before any attempt to power it on"));
13315 mData->mSession.mProgress.setNull();
13316 }
13317
13318 /* Create the progress object the client will use to wait until
13319 * #i_checkForDeath() is called to uninitialize this session object after
13320 * it releases the IPC semaphore.
13321 * Note! Because we're "reusing" mProgress here, this must be a proxy
13322 * object just like for LaunchVMProcess. */
13323 Assert(mData->mSession.mProgress.isNull());
13324 ComObjPtr<ProgressProxy> progress;
13325 progress.createObject();
13326 ComPtr<IUnknown> pPeer(mPeer);
13327 progress->init(mParent, pPeer,
13328 Bstr(tr("Closing session")).raw(),
13329 FALSE /* aCancelable */);
13330 progress.queryInterfaceTo(aProgress.asOutParam());
13331 mData->mSession.mProgress = progress;
13332 }
13333 else
13334 {
13335 /* the remote session is being normally closed */
13336 bool found = false;
13337 for (Data::Session::RemoteControlList::iterator
13338 it = mData->mSession.mRemoteControls.begin();
13339 it != mData->mSession.mRemoteControls.end();
13340 ++it)
13341 {
13342 if (control == *it)
13343 {
13344 found = true;
13345 // This MUST be erase(it), not remove(*it) as the latter
13346 // triggers a very nasty use after free due to the place where
13347 // the value "lives".
13348 mData->mSession.mRemoteControls.erase(it);
13349 break;
13350 }
13351 }
13352 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13353 E_INVALIDARG);
13354 }
13355
13356 /* signal the client watcher thread, because the client is going away */
13357 mParent->i_updateClientWatcher();
13358
13359 LogFlowThisFuncLeave();
13360 return S_OK;
13361}
13362
13363HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13364{
13365#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13366 ULONG uID;
13367 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13368 if (RT_SUCCESS(rc))
13369 {
13370 if (aID)
13371 *aID = uID;
13372 return S_OK;
13373 }
13374 return E_FAIL;
13375#else
13376 RT_NOREF(aParms, aID);
13377 ReturnComNotImplemented();
13378#endif
13379}
13380
13381HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13382{
13383#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13384 return mParent->i_onClipboardAreaUnregister(aID);
13385#else
13386 RT_NOREF(aID);
13387 ReturnComNotImplemented();
13388#endif
13389}
13390
13391HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13392{
13393#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13394 return mParent->i_onClipboardAreaAttach(aID);
13395#else
13396 RT_NOREF(aID);
13397 ReturnComNotImplemented();
13398#endif
13399}
13400HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13401{
13402#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13403 return mParent->i_onClipboardAreaDetach(aID);
13404#else
13405 RT_NOREF(aID);
13406 ReturnComNotImplemented();
13407#endif
13408}
13409
13410HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13411{
13412#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13413 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13414 if (aID)
13415 *aID = uID;
13416 return S_OK;
13417#else
13418 RT_NOREF(aID);
13419 ReturnComNotImplemented();
13420#endif
13421}
13422
13423HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13424{
13425#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13426 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13427 if (aRefCount)
13428 *aRefCount = uRefCount;
13429 return S_OK;
13430#else
13431 RT_NOREF(aID, aRefCount);
13432 ReturnComNotImplemented();
13433#endif
13434}
13435
13436HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13437 std::vector<com::Utf8Str> &aValues,
13438 std::vector<LONG64> &aTimestamps,
13439 std::vector<com::Utf8Str> &aFlags)
13440{
13441 LogFlowThisFunc(("\n"));
13442
13443#ifdef VBOX_WITH_GUEST_PROPS
13444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13445
13446 size_t cEntries = mHWData->mGuestProperties.size();
13447 aNames.resize(cEntries);
13448 aValues.resize(cEntries);
13449 aTimestamps.resize(cEntries);
13450 aFlags.resize(cEntries);
13451
13452 size_t i = 0;
13453 for (HWData::GuestPropertyMap::const_iterator
13454 it = mHWData->mGuestProperties.begin();
13455 it != mHWData->mGuestProperties.end();
13456 ++it, ++i)
13457 {
13458 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13459 aNames[i] = it->first;
13460 aValues[i] = it->second.strValue;
13461 aTimestamps[i] = it->second.mTimestamp;
13462
13463 /* If it is NULL, keep it NULL. */
13464 if (it->second.mFlags)
13465 {
13466 GuestPropWriteFlags(it->second.mFlags, szFlags);
13467 aFlags[i] = szFlags;
13468 }
13469 else
13470 aFlags[i] = "";
13471 }
13472 return S_OK;
13473#else
13474 ReturnComNotImplemented();
13475#endif
13476}
13477
13478HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13479 const com::Utf8Str &aValue,
13480 LONG64 aTimestamp,
13481 const com::Utf8Str &aFlags)
13482{
13483 LogFlowThisFunc(("\n"));
13484
13485#ifdef VBOX_WITH_GUEST_PROPS
13486 try
13487 {
13488 /*
13489 * Convert input up front.
13490 */
13491 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13492 if (aFlags.length())
13493 {
13494 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13495 AssertRCReturn(vrc, E_INVALIDARG);
13496 }
13497
13498 /*
13499 * Now grab the object lock, validate the state and do the update.
13500 */
13501
13502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13503
13504 if (!Global::IsOnline(mData->mMachineState))
13505 {
13506 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13507 VBOX_E_INVALID_VM_STATE);
13508 }
13509
13510 i_setModified(IsModified_MachineData);
13511 mHWData.backup();
13512
13513 bool fDelete = !aValue.length();
13514 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13515 if (it != mHWData->mGuestProperties.end())
13516 {
13517 if (!fDelete)
13518 {
13519 it->second.strValue = aValue;
13520 it->second.mTimestamp = aTimestamp;
13521 it->second.mFlags = fFlags;
13522 }
13523 else
13524 mHWData->mGuestProperties.erase(it);
13525
13526 mData->mGuestPropertiesModified = TRUE;
13527 }
13528 else if (!fDelete)
13529 {
13530 HWData::GuestProperty prop;
13531 prop.strValue = aValue;
13532 prop.mTimestamp = aTimestamp;
13533 prop.mFlags = fFlags;
13534
13535 mHWData->mGuestProperties[aName] = prop;
13536 mData->mGuestPropertiesModified = TRUE;
13537 }
13538
13539 alock.release();
13540
13541 mParent->i_onGuestPropertyChange(mData->mUuid,
13542 Bstr(aName).raw(),
13543 Bstr(aValue).raw(),
13544 Bstr(aFlags).raw());
13545 }
13546 catch (...)
13547 {
13548 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13549 }
13550 return S_OK;
13551#else
13552 ReturnComNotImplemented();
13553#endif
13554}
13555
13556
13557HRESULT SessionMachine::lockMedia()
13558{
13559 AutoMultiWriteLock2 alock(this->lockHandle(),
13560 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13561
13562 AssertReturn( mData->mMachineState == MachineState_Starting
13563 || mData->mMachineState == MachineState_Restoring
13564 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13565
13566 clearError();
13567 alock.release();
13568 return i_lockMedia();
13569}
13570
13571HRESULT SessionMachine::unlockMedia()
13572{
13573 HRESULT hrc = i_unlockMedia();
13574 return hrc;
13575}
13576
13577HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13578 ComPtr<IMediumAttachment> &aNewAttachment)
13579{
13580 // request the host lock first, since might be calling Host methods for getting host drives;
13581 // next, protect the media tree all the while we're in here, as well as our member variables
13582 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13583 this->lockHandle(),
13584 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13585
13586 IMediumAttachment *iAttach = aAttachment;
13587 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13588
13589 Utf8Str ctrlName;
13590 LONG lPort;
13591 LONG lDevice;
13592 bool fTempEject;
13593 {
13594 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13595
13596 /* Need to query the details first, as the IMediumAttachment reference
13597 * might be to the original settings, which we are going to change. */
13598 ctrlName = pAttach->i_getControllerName();
13599 lPort = pAttach->i_getPort();
13600 lDevice = pAttach->i_getDevice();
13601 fTempEject = pAttach->i_getTempEject();
13602 }
13603
13604 if (!fTempEject)
13605 {
13606 /* Remember previously mounted medium. The medium before taking the
13607 * backup is not necessarily the same thing. */
13608 ComObjPtr<Medium> oldmedium;
13609 oldmedium = pAttach->i_getMedium();
13610
13611 i_setModified(IsModified_Storage);
13612 mMediumAttachments.backup();
13613
13614 // The backup operation makes the pAttach reference point to the
13615 // old settings. Re-get the correct reference.
13616 pAttach = i_findAttachment(*mMediumAttachments.data(),
13617 ctrlName,
13618 lPort,
13619 lDevice);
13620
13621 {
13622 AutoCaller autoAttachCaller(this);
13623 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13624
13625 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13626 if (!oldmedium.isNull())
13627 oldmedium->i_removeBackReference(mData->mUuid);
13628
13629 pAttach->i_updateMedium(NULL);
13630 pAttach->i_updateEjected();
13631 }
13632
13633 i_setModified(IsModified_Storage);
13634 }
13635 else
13636 {
13637 {
13638 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13639 pAttach->i_updateEjected();
13640 }
13641 }
13642
13643 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13644
13645 return S_OK;
13646}
13647
13648HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13649 com::Utf8Str &aResult)
13650{
13651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13652
13653 HRESULT hr = S_OK;
13654
13655 if (!mAuthLibCtx.hAuthLibrary)
13656 {
13657 /* Load the external authentication library. */
13658 Bstr authLibrary;
13659 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13660
13661 Utf8Str filename = authLibrary;
13662
13663 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13664 if (RT_FAILURE(vrc))
13665 hr = setErrorBoth(E_FAIL, vrc,
13666 tr("Could not load the external authentication library '%s' (%Rrc)"),
13667 filename.c_str(), vrc);
13668 }
13669
13670 /* The auth library might need the machine lock. */
13671 alock.release();
13672
13673 if (FAILED(hr))
13674 return hr;
13675
13676 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13677 {
13678 enum VRDEAuthParams
13679 {
13680 parmUuid = 1,
13681 parmGuestJudgement,
13682 parmUser,
13683 parmPassword,
13684 parmDomain,
13685 parmClientId
13686 };
13687
13688 AuthResult result = AuthResultAccessDenied;
13689
13690 Guid uuid(aAuthParams[parmUuid]);
13691 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13692 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13693
13694 result = AuthLibAuthenticate(&mAuthLibCtx,
13695 uuid.raw(), guestJudgement,
13696 aAuthParams[parmUser].c_str(),
13697 aAuthParams[parmPassword].c_str(),
13698 aAuthParams[parmDomain].c_str(),
13699 u32ClientId);
13700
13701 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13702 size_t cbPassword = aAuthParams[parmPassword].length();
13703 if (cbPassword)
13704 {
13705 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13706 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13707 }
13708
13709 if (result == AuthResultAccessGranted)
13710 aResult = "granted";
13711 else
13712 aResult = "denied";
13713
13714 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13715 aAuthParams[parmUser].c_str(), aResult.c_str()));
13716 }
13717 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13718 {
13719 enum VRDEAuthDisconnectParams
13720 {
13721 parmUuid = 1,
13722 parmClientId
13723 };
13724
13725 Guid uuid(aAuthParams[parmUuid]);
13726 uint32_t u32ClientId = 0;
13727 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13728 }
13729 else
13730 {
13731 hr = E_INVALIDARG;
13732 }
13733
13734 return hr;
13735}
13736
13737// public methods only for internal purposes
13738/////////////////////////////////////////////////////////////////////////////
13739
13740#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13741/**
13742 * Called from the client watcher thread to check for expected or unexpected
13743 * death of the client process that has a direct session to this machine.
13744 *
13745 * On Win32 and on OS/2, this method is called only when we've got the
13746 * mutex (i.e. the client has either died or terminated normally) so it always
13747 * returns @c true (the client is terminated, the session machine is
13748 * uninitialized).
13749 *
13750 * On other platforms, the method returns @c true if the client process has
13751 * terminated normally or abnormally and the session machine was uninitialized,
13752 * and @c false if the client process is still alive.
13753 *
13754 * @note Locks this object for writing.
13755 */
13756bool SessionMachine::i_checkForDeath()
13757{
13758 Uninit::Reason reason;
13759 bool terminated = false;
13760
13761 /* Enclose autoCaller with a block because calling uninit() from under it
13762 * will deadlock. */
13763 {
13764 AutoCaller autoCaller(this);
13765 if (!autoCaller.isOk())
13766 {
13767 /* return true if not ready, to cause the client watcher to exclude
13768 * the corresponding session from watching */
13769 LogFlowThisFunc(("Already uninitialized!\n"));
13770 return true;
13771 }
13772
13773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13774
13775 /* Determine the reason of death: if the session state is Closing here,
13776 * everything is fine. Otherwise it means that the client did not call
13777 * OnSessionEnd() before it released the IPC semaphore. This may happen
13778 * either because the client process has abnormally terminated, or
13779 * because it simply forgot to call ISession::Close() before exiting. We
13780 * threat the latter also as an abnormal termination (see
13781 * Session::uninit() for details). */
13782 reason = mData->mSession.mState == SessionState_Unlocking ?
13783 Uninit::Normal :
13784 Uninit::Abnormal;
13785
13786 if (mClientToken)
13787 terminated = mClientToken->release();
13788 } /* AutoCaller block */
13789
13790 if (terminated)
13791 uninit(reason);
13792
13793 return terminated;
13794}
13795
13796void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13797{
13798 LogFlowThisFunc(("\n"));
13799
13800 strTokenId.setNull();
13801
13802 AutoCaller autoCaller(this);
13803 AssertComRCReturnVoid(autoCaller.rc());
13804
13805 Assert(mClientToken);
13806 if (mClientToken)
13807 mClientToken->getId(strTokenId);
13808}
13809#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13810IToken *SessionMachine::i_getToken()
13811{
13812 LogFlowThisFunc(("\n"));
13813
13814 AutoCaller autoCaller(this);
13815 AssertComRCReturn(autoCaller.rc(), NULL);
13816
13817 Assert(mClientToken);
13818 if (mClientToken)
13819 return mClientToken->getToken();
13820 else
13821 return NULL;
13822}
13823#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13824
13825Machine::ClientToken *SessionMachine::i_getClientToken()
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829 AutoCaller autoCaller(this);
13830 AssertComRCReturn(autoCaller.rc(), NULL);
13831
13832 return mClientToken;
13833}
13834
13835
13836/**
13837 * @note Locks this object for reading.
13838 */
13839HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13840{
13841 LogFlowThisFunc(("\n"));
13842
13843 AutoCaller autoCaller(this);
13844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13845
13846 ComPtr<IInternalSessionControl> directControl;
13847 {
13848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13849 if (mData->mSession.mLockType == LockType_VM)
13850 directControl = mData->mSession.mDirectControl;
13851 }
13852
13853 /* ignore notifications sent after #OnSessionEnd() is called */
13854 if (!directControl)
13855 return S_OK;
13856
13857 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13858}
13859
13860/**
13861 * @note Locks this object for reading.
13862 */
13863HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13864 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13865 IN_BSTR aGuestIp, LONG aGuestPort)
13866{
13867 LogFlowThisFunc(("\n"));
13868
13869 AutoCaller autoCaller(this);
13870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13871
13872 ComPtr<IInternalSessionControl> directControl;
13873 {
13874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13875 if (mData->mSession.mLockType == LockType_VM)
13876 directControl = mData->mSession.mDirectControl;
13877 }
13878
13879 /* ignore notifications sent after #OnSessionEnd() is called */
13880 if (!directControl)
13881 return S_OK;
13882 /*
13883 * instead acting like callback we ask IVirtualBox deliver corresponding event
13884 */
13885
13886 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13887 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13888 return S_OK;
13889}
13890
13891/**
13892 * @note Locks this object for reading.
13893 */
13894HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13895{
13896 LogFlowThisFunc(("\n"));
13897
13898 AutoCaller autoCaller(this);
13899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13900
13901 ComPtr<IInternalSessionControl> directControl;
13902 {
13903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13904 if (mData->mSession.mLockType == LockType_VM)
13905 directControl = mData->mSession.mDirectControl;
13906 }
13907
13908 /* ignore notifications sent after #OnSessionEnd() is called */
13909 if (!directControl)
13910 return S_OK;
13911
13912 return directControl->OnAudioAdapterChange(audioAdapter);
13913}
13914
13915/**
13916 * @note Locks this object for reading.
13917 */
13918HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13919{
13920 LogFlowThisFunc(("\n"));
13921
13922 AutoCaller autoCaller(this);
13923 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13924
13925 ComPtr<IInternalSessionControl> directControl;
13926 {
13927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13928 if (mData->mSession.mLockType == LockType_VM)
13929 directControl = mData->mSession.mDirectControl;
13930 }
13931
13932 /* ignore notifications sent after #OnSessionEnd() is called */
13933 if (!directControl)
13934 return S_OK;
13935
13936 return directControl->OnSerialPortChange(serialPort);
13937}
13938
13939/**
13940 * @note Locks this object for reading.
13941 */
13942HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
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 return directControl->OnParallelPortChange(parallelPort);
13961}
13962
13963/**
13964 * @note Locks this object for reading.
13965 */
13966HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13967{
13968 LogFlowThisFunc(("\n"));
13969
13970 AutoCaller autoCaller(this);
13971 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13972
13973 ComPtr<IInternalSessionControl> directControl;
13974 {
13975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13976 if (mData->mSession.mLockType == LockType_VM)
13977 directControl = mData->mSession.mDirectControl;
13978 }
13979
13980 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
13981
13982 /* ignore notifications sent after #OnSessionEnd() is called */
13983 if (!directControl)
13984 return S_OK;
13985
13986 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
13987}
13988
13989/**
13990 * @note Locks this object for reading.
13991 */
13992HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13993{
13994 LogFlowThisFunc(("\n"));
13995
13996 AutoCaller autoCaller(this);
13997 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13998
13999 ComPtr<IInternalSessionControl> directControl;
14000 {
14001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14002 if (mData->mSession.mLockType == LockType_VM)
14003 directControl = mData->mSession.mDirectControl;
14004 }
14005
14006 mParent->i_onMediumChanged(aAttachment);
14007
14008 /* ignore notifications sent after #OnSessionEnd() is called */
14009 if (!directControl)
14010 return S_OK;
14011
14012 return directControl->OnMediumChange(aAttachment, aForce);
14013}
14014
14015HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14016{
14017 LogFlowThisFunc(("\n"));
14018
14019 AutoCaller autoCaller(this);
14020 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14021
14022 ComPtr<IInternalSessionControl> directControl;
14023 {
14024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14025 if (mData->mSession.mLockType == LockType_VM)
14026 directControl = mData->mSession.mDirectControl;
14027 }
14028
14029 /* ignore notifications sent after #OnSessionEnd() is called */
14030 if (!directControl)
14031 return S_OK;
14032
14033 return directControl->OnVMProcessPriorityChange(aPriority);
14034}
14035
14036/**
14037 * @note Locks this object for reading.
14038 */
14039HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14040{
14041 LogFlowThisFunc(("\n"));
14042
14043 AutoCaller autoCaller(this);
14044 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14045
14046 ComPtr<IInternalSessionControl> directControl;
14047 {
14048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14049 if (mData->mSession.mLockType == LockType_VM)
14050 directControl = mData->mSession.mDirectControl;
14051 }
14052
14053 /* ignore notifications sent after #OnSessionEnd() is called */
14054 if (!directControl)
14055 return S_OK;
14056
14057 return directControl->OnCPUChange(aCPU, aRemove);
14058}
14059
14060HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14061{
14062 LogFlowThisFunc(("\n"));
14063
14064 AutoCaller autoCaller(this);
14065 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14066
14067 ComPtr<IInternalSessionControl> directControl;
14068 {
14069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14070 if (mData->mSession.mLockType == LockType_VM)
14071 directControl = mData->mSession.mDirectControl;
14072 }
14073
14074 /* ignore notifications sent after #OnSessionEnd() is called */
14075 if (!directControl)
14076 return S_OK;
14077
14078 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14079}
14080
14081/**
14082 * @note Locks this object for reading.
14083 */
14084HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14085{
14086 LogFlowThisFunc(("\n"));
14087
14088 AutoCaller autoCaller(this);
14089 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14090
14091 ComPtr<IInternalSessionControl> directControl;
14092 {
14093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14094 if (mData->mSession.mLockType == LockType_VM)
14095 directControl = mData->mSession.mDirectControl;
14096 }
14097
14098 /* ignore notifications sent after #OnSessionEnd() is called */
14099 if (!directControl)
14100 return S_OK;
14101
14102 return directControl->OnVRDEServerChange(aRestart);
14103}
14104
14105/**
14106 * @note Locks this object for reading.
14107 */
14108HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14109{
14110 LogFlowThisFunc(("\n"));
14111
14112 AutoCaller autoCaller(this);
14113 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14114
14115 ComPtr<IInternalSessionControl> directControl;
14116 {
14117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14118 if (mData->mSession.mLockType == LockType_VM)
14119 directControl = mData->mSession.mDirectControl;
14120 }
14121
14122 /* ignore notifications sent after #OnSessionEnd() is called */
14123 if (!directControl)
14124 return S_OK;
14125
14126 return directControl->OnRecordingChange(aEnable);
14127}
14128
14129/**
14130 * @note Locks this object for reading.
14131 */
14132HRESULT SessionMachine::i_onUSBControllerChange()
14133{
14134 LogFlowThisFunc(("\n"));
14135
14136 AutoCaller autoCaller(this);
14137 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14138
14139 ComPtr<IInternalSessionControl> directControl;
14140 {
14141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14142 if (mData->mSession.mLockType == LockType_VM)
14143 directControl = mData->mSession.mDirectControl;
14144 }
14145
14146 /* ignore notifications sent after #OnSessionEnd() is called */
14147 if (!directControl)
14148 return S_OK;
14149
14150 return directControl->OnUSBControllerChange();
14151}
14152
14153/**
14154 * @note Locks this object for reading.
14155 */
14156HRESULT SessionMachine::i_onSharedFolderChange()
14157{
14158 LogFlowThisFunc(("\n"));
14159
14160 AutoCaller autoCaller(this);
14161 AssertComRCReturnRC(autoCaller.rc());
14162
14163 ComPtr<IInternalSessionControl> directControl;
14164 {
14165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14166 if (mData->mSession.mLockType == LockType_VM)
14167 directControl = mData->mSession.mDirectControl;
14168 }
14169
14170 /* ignore notifications sent after #OnSessionEnd() is called */
14171 if (!directControl)
14172 return S_OK;
14173
14174 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14175}
14176
14177/**
14178 * @note Locks this object for reading.
14179 */
14180HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14181{
14182 LogFlowThisFunc(("\n"));
14183
14184 AutoCaller autoCaller(this);
14185 AssertComRCReturnRC(autoCaller.rc());
14186
14187 ComPtr<IInternalSessionControl> directControl;
14188 {
14189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14190 if (mData->mSession.mLockType == LockType_VM)
14191 directControl = mData->mSession.mDirectControl;
14192 }
14193
14194 /* ignore notifications sent after #OnSessionEnd() is called */
14195 if (!directControl)
14196 return S_OK;
14197
14198 return directControl->OnClipboardModeChange(aClipboardMode);
14199}
14200
14201/**
14202 * @note Locks this object for reading.
14203 */
14204HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14205{
14206 LogFlowThisFunc(("\n"));
14207
14208 AutoCaller autoCaller(this);
14209 AssertComRCReturnRC(autoCaller.rc());
14210
14211 ComPtr<IInternalSessionControl> directControl;
14212 {
14213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14214 if (mData->mSession.mLockType == LockType_VM)
14215 directControl = mData->mSession.mDirectControl;
14216 }
14217
14218 /* ignore notifications sent after #OnSessionEnd() is called */
14219 if (!directControl)
14220 return S_OK;
14221
14222 return directControl->OnClipboardFileTransferModeChange(aEnable);
14223}
14224
14225/**
14226 * @note Locks this object for reading.
14227 */
14228HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14229{
14230 LogFlowThisFunc(("\n"));
14231
14232 AutoCaller autoCaller(this);
14233 AssertComRCReturnRC(autoCaller.rc());
14234
14235 ComPtr<IInternalSessionControl> directControl;
14236 {
14237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14238 if (mData->mSession.mLockType == LockType_VM)
14239 directControl = mData->mSession.mDirectControl;
14240 }
14241
14242 /* ignore notifications sent after #OnSessionEnd() is called */
14243 if (!directControl)
14244 return S_OK;
14245
14246 return directControl->OnDnDModeChange(aDnDMode);
14247}
14248
14249/**
14250 * @note Locks this object for reading.
14251 */
14252HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14253{
14254 LogFlowThisFunc(("\n"));
14255
14256 AutoCaller autoCaller(this);
14257 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14258
14259 ComPtr<IInternalSessionControl> directControl;
14260 {
14261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14262 if (mData->mSession.mLockType == LockType_VM)
14263 directControl = mData->mSession.mDirectControl;
14264 }
14265
14266 /* ignore notifications sent after #OnSessionEnd() is called */
14267 if (!directControl)
14268 return S_OK;
14269
14270 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14271}
14272
14273/**
14274 * @note Locks this object for reading.
14275 */
14276HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14277{
14278 LogFlowThisFunc(("\n"));
14279
14280 AutoCaller autoCaller(this);
14281 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14282
14283 ComPtr<IInternalSessionControl> directControl;
14284 {
14285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14286 if (mData->mSession.mLockType == LockType_VM)
14287 directControl = mData->mSession.mDirectControl;
14288 }
14289
14290 /* ignore notifications sent after #OnSessionEnd() is called */
14291 if (!directControl)
14292 return S_OK;
14293
14294 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14295}
14296
14297/**
14298 * Returns @c true if this machine's USB controller reports it has a matching
14299 * filter for the given USB device and @c false otherwise.
14300 *
14301 * @note locks this object for reading.
14302 */
14303bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14304{
14305 AutoCaller autoCaller(this);
14306 /* silently return if not ready -- this method may be called after the
14307 * direct machine session has been called */
14308 if (!autoCaller.isOk())
14309 return false;
14310
14311#ifdef VBOX_WITH_USB
14312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14313
14314 switch (mData->mMachineState)
14315 {
14316 case MachineState_Starting:
14317 case MachineState_Restoring:
14318 case MachineState_TeleportingIn:
14319 case MachineState_Paused:
14320 case MachineState_Running:
14321 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14322 * elsewhere... */
14323 alock.release();
14324 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14325 default: break;
14326 }
14327#else
14328 NOREF(aDevice);
14329 NOREF(aMaskedIfs);
14330#endif
14331 return false;
14332}
14333
14334/**
14335 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14336 */
14337HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14338 IVirtualBoxErrorInfo *aError,
14339 ULONG aMaskedIfs,
14340 const com::Utf8Str &aCaptureFilename)
14341{
14342 LogFlowThisFunc(("\n"));
14343
14344 AutoCaller autoCaller(this);
14345
14346 /* This notification may happen after the machine object has been
14347 * uninitialized (the session was closed), so don't assert. */
14348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14349
14350 ComPtr<IInternalSessionControl> directControl;
14351 {
14352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14353 if (mData->mSession.mLockType == LockType_VM)
14354 directControl = mData->mSession.mDirectControl;
14355 }
14356
14357 /* fail on notifications sent after #OnSessionEnd() is called, it is
14358 * expected by the caller */
14359 if (!directControl)
14360 return E_FAIL;
14361
14362 /* No locks should be held at this point. */
14363 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14364 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14365
14366 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14367}
14368
14369/**
14370 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14371 */
14372HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14373 IVirtualBoxErrorInfo *aError)
14374{
14375 LogFlowThisFunc(("\n"));
14376
14377 AutoCaller autoCaller(this);
14378
14379 /* This notification may happen after the machine object has been
14380 * uninitialized (the session was closed), so don't assert. */
14381 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14382
14383 ComPtr<IInternalSessionControl> directControl;
14384 {
14385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14386 if (mData->mSession.mLockType == LockType_VM)
14387 directControl = mData->mSession.mDirectControl;
14388 }
14389
14390 /* fail on notifications sent after #OnSessionEnd() is called, it is
14391 * expected by the caller */
14392 if (!directControl)
14393 return E_FAIL;
14394
14395 /* No locks should be held at this point. */
14396 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14397 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14398
14399 return directControl->OnUSBDeviceDetach(aId, aError);
14400}
14401
14402// protected methods
14403/////////////////////////////////////////////////////////////////////////////
14404
14405/**
14406 * Deletes the given file if it is no longer in use by either the current machine state
14407 * (if the machine is "saved") or any of the machine's snapshots.
14408 *
14409 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14410 * but is different for each SnapshotMachine. When calling this, the order of calling this
14411 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14412 * is therefore critical. I know, it's all rather messy.
14413 *
14414 * @param strStateFile
14415 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14416 * the test for whether the saved state file is in use.
14417 */
14418void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14419 Snapshot *pSnapshotToIgnore)
14420{
14421 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14422 if ( (strStateFile.isNotEmpty())
14423 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14424 )
14425 // ... and it must also not be shared with other snapshots
14426 if ( !mData->mFirstSnapshot
14427 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14428 // this checks the SnapshotMachine's state file paths
14429 )
14430 RTFileDelete(strStateFile.c_str());
14431}
14432
14433/**
14434 * Locks the attached media.
14435 *
14436 * All attached hard disks are locked for writing and DVD/floppy are locked for
14437 * reading. Parents of attached hard disks (if any) are locked for reading.
14438 *
14439 * This method also performs accessibility check of all media it locks: if some
14440 * media is inaccessible, the method will return a failure and a bunch of
14441 * extended error info objects per each inaccessible medium.
14442 *
14443 * Note that this method is atomic: if it returns a success, all media are
14444 * locked as described above; on failure no media is locked at all (all
14445 * succeeded individual locks will be undone).
14446 *
14447 * The caller is responsible for doing the necessary state sanity checks.
14448 *
14449 * The locks made by this method must be undone by calling #unlockMedia() when
14450 * no more needed.
14451 */
14452HRESULT SessionMachine::i_lockMedia()
14453{
14454 AutoCaller autoCaller(this);
14455 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14456
14457 AutoMultiWriteLock2 alock(this->lockHandle(),
14458 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14459
14460 /* bail out if trying to lock things with already set up locking */
14461 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14462
14463 MultiResult mrc(S_OK);
14464
14465 /* Collect locking information for all medium objects attached to the VM. */
14466 for (MediumAttachmentList::const_iterator
14467 it = mMediumAttachments->begin();
14468 it != mMediumAttachments->end();
14469 ++it)
14470 {
14471 MediumAttachment *pAtt = *it;
14472 DeviceType_T devType = pAtt->i_getType();
14473 Medium *pMedium = pAtt->i_getMedium();
14474
14475 MediumLockList *pMediumLockList(new MediumLockList());
14476 // There can be attachments without a medium (floppy/dvd), and thus
14477 // it's impossible to create a medium lock list. It still makes sense
14478 // to have the empty medium lock list in the map in case a medium is
14479 // attached later.
14480 if (pMedium != NULL)
14481 {
14482 MediumType_T mediumType = pMedium->i_getType();
14483 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14484 || mediumType == MediumType_Shareable;
14485 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14486
14487 alock.release();
14488 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14489 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14490 false /* fMediumLockWriteAll */,
14491 NULL,
14492 *pMediumLockList);
14493 alock.acquire();
14494 if (FAILED(mrc))
14495 {
14496 delete pMediumLockList;
14497 mData->mSession.mLockedMedia.Clear();
14498 break;
14499 }
14500 }
14501
14502 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14503 if (FAILED(rc))
14504 {
14505 mData->mSession.mLockedMedia.Clear();
14506 mrc = setError(rc,
14507 tr("Collecting locking information for all attached media failed"));
14508 break;
14509 }
14510 }
14511
14512 if (SUCCEEDED(mrc))
14513 {
14514 /* Now lock all media. If this fails, nothing is locked. */
14515 alock.release();
14516 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14517 alock.acquire();
14518 if (FAILED(rc))
14519 {
14520 mrc = setError(rc,
14521 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14522 }
14523 }
14524
14525 return mrc;
14526}
14527
14528/**
14529 * Undoes the locks made by by #lockMedia().
14530 */
14531HRESULT SessionMachine::i_unlockMedia()
14532{
14533 AutoCaller autoCaller(this);
14534 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14535
14536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14537
14538 /* we may be holding important error info on the current thread;
14539 * preserve it */
14540 ErrorInfoKeeper eik;
14541
14542 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14543 AssertComRC(rc);
14544 return rc;
14545}
14546
14547/**
14548 * Helper to change the machine state (reimplementation).
14549 *
14550 * @note Locks this object for writing.
14551 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14552 * it can cause crashes in random places due to unexpectedly committing
14553 * the current settings. The caller is responsible for that. The call
14554 * to saveStateSettings is fine, because this method does not commit.
14555 */
14556HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14557{
14558 LogFlowThisFuncEnter();
14559 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14560
14561 AutoCaller autoCaller(this);
14562 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14563
14564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14565
14566 MachineState_T oldMachineState = mData->mMachineState;
14567
14568 AssertMsgReturn(oldMachineState != aMachineState,
14569 ("oldMachineState=%s, aMachineState=%s\n",
14570 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14571 E_FAIL);
14572
14573 HRESULT rc = S_OK;
14574
14575 int stsFlags = 0;
14576 bool deleteSavedState = false;
14577
14578 /* detect some state transitions */
14579
14580 if ( ( oldMachineState == MachineState_Saved
14581 && aMachineState == MachineState_Restoring)
14582 || ( ( oldMachineState == MachineState_PoweredOff
14583 || oldMachineState == MachineState_Teleported
14584 || oldMachineState == MachineState_Aborted
14585 )
14586 && ( aMachineState == MachineState_TeleportingIn
14587 || aMachineState == MachineState_Starting
14588 )
14589 )
14590 )
14591 {
14592 /* The EMT thread is about to start */
14593
14594 /* Nothing to do here for now... */
14595
14596 /// @todo NEWMEDIA don't let mDVDDrive and other children
14597 /// change anything when in the Starting/Restoring state
14598 }
14599 else if ( ( oldMachineState == MachineState_Running
14600 || oldMachineState == MachineState_Paused
14601 || oldMachineState == MachineState_Teleporting
14602 || oldMachineState == MachineState_OnlineSnapshotting
14603 || oldMachineState == MachineState_LiveSnapshotting
14604 || oldMachineState == MachineState_Stuck
14605 || oldMachineState == MachineState_Starting
14606 || oldMachineState == MachineState_Stopping
14607 || oldMachineState == MachineState_Saving
14608 || oldMachineState == MachineState_Restoring
14609 || oldMachineState == MachineState_TeleportingPausedVM
14610 || oldMachineState == MachineState_TeleportingIn
14611 )
14612 && ( aMachineState == MachineState_PoweredOff
14613 || aMachineState == MachineState_Saved
14614 || aMachineState == MachineState_Teleported
14615 || aMachineState == MachineState_Aborted
14616 )
14617 )
14618 {
14619 /* The EMT thread has just stopped, unlock attached media. Note that as
14620 * opposed to locking that is done from Console, we do unlocking here
14621 * because the VM process may have aborted before having a chance to
14622 * properly unlock all media it locked. */
14623
14624 unlockMedia();
14625 }
14626
14627 if (oldMachineState == MachineState_Restoring)
14628 {
14629 if (aMachineState != MachineState_Saved)
14630 {
14631 /*
14632 * delete the saved state file once the machine has finished
14633 * restoring from it (note that Console sets the state from
14634 * Restoring to Saved if the VM couldn't restore successfully,
14635 * to give the user an ability to fix an error and retry --
14636 * we keep the saved state file in this case)
14637 */
14638 deleteSavedState = true;
14639 }
14640 }
14641 else if ( oldMachineState == MachineState_Saved
14642 && ( aMachineState == MachineState_PoweredOff
14643 || aMachineState == MachineState_Aborted
14644 || aMachineState == MachineState_Teleported
14645 )
14646 )
14647 {
14648 /*
14649 * delete the saved state after SessionMachine::ForgetSavedState() is called
14650 * or if the VM process (owning a direct VM session) crashed while the
14651 * VM was Saved
14652 */
14653
14654 /// @todo (dmik)
14655 // Not sure that deleting the saved state file just because of the
14656 // client death before it attempted to restore the VM is a good
14657 // thing. But when it crashes we need to go to the Aborted state
14658 // which cannot have the saved state file associated... The only
14659 // way to fix this is to make the Aborted condition not a VM state
14660 // but a bool flag: i.e., when a crash occurs, set it to true and
14661 // change the state to PoweredOff or Saved depending on the
14662 // saved state presence.
14663
14664 deleteSavedState = true;
14665 mData->mCurrentStateModified = TRUE;
14666 stsFlags |= SaveSTS_CurStateModified;
14667 }
14668
14669 if ( aMachineState == MachineState_Starting
14670 || aMachineState == MachineState_Restoring
14671 || aMachineState == MachineState_TeleportingIn
14672 )
14673 {
14674 /* set the current state modified flag to indicate that the current
14675 * state is no more identical to the state in the
14676 * current snapshot */
14677 if (!mData->mCurrentSnapshot.isNull())
14678 {
14679 mData->mCurrentStateModified = TRUE;
14680 stsFlags |= SaveSTS_CurStateModified;
14681 }
14682 }
14683
14684 if (deleteSavedState)
14685 {
14686 if (mRemoveSavedState)
14687 {
14688 Assert(!mSSData->strStateFilePath.isEmpty());
14689
14690 // it is safe to delete the saved state file if ...
14691 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14692 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14693 // ... none of the snapshots share the saved state file
14694 )
14695 RTFileDelete(mSSData->strStateFilePath.c_str());
14696 }
14697
14698 mSSData->strStateFilePath.setNull();
14699 stsFlags |= SaveSTS_StateFilePath;
14700 }
14701
14702 /* redirect to the underlying peer machine */
14703 mPeer->i_setMachineState(aMachineState);
14704
14705 if ( oldMachineState != MachineState_RestoringSnapshot
14706 && ( aMachineState == MachineState_PoweredOff
14707 || aMachineState == MachineState_Teleported
14708 || aMachineState == MachineState_Aborted
14709 || aMachineState == MachineState_Saved))
14710 {
14711 /* the machine has stopped execution
14712 * (or the saved state file was adopted) */
14713 stsFlags |= SaveSTS_StateTimeStamp;
14714 }
14715
14716 if ( ( oldMachineState == MachineState_PoweredOff
14717 || oldMachineState == MachineState_Aborted
14718 || oldMachineState == MachineState_Teleported
14719 )
14720 && aMachineState == MachineState_Saved)
14721 {
14722 /* the saved state file was adopted */
14723 Assert(!mSSData->strStateFilePath.isEmpty());
14724 stsFlags |= SaveSTS_StateFilePath;
14725 }
14726
14727#ifdef VBOX_WITH_GUEST_PROPS
14728 if ( aMachineState == MachineState_PoweredOff
14729 || aMachineState == MachineState_Aborted
14730 || aMachineState == MachineState_Teleported)
14731 {
14732 /* Make sure any transient guest properties get removed from the
14733 * property store on shutdown. */
14734 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14735
14736 /* remove it from the settings representation */
14737 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14738 for (settings::GuestPropertiesList::iterator
14739 it = llGuestProperties.begin();
14740 it != llGuestProperties.end();
14741 /*nothing*/)
14742 {
14743 const settings::GuestProperty &prop = *it;
14744 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14745 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14746 {
14747 it = llGuestProperties.erase(it);
14748 fNeedsSaving = true;
14749 }
14750 else
14751 {
14752 ++it;
14753 }
14754 }
14755
14756 /* Additionally remove it from the HWData representation. Required to
14757 * keep everything in sync, as this is what the API keeps using. */
14758 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14759 for (HWData::GuestPropertyMap::iterator
14760 it = llHWGuestProperties.begin();
14761 it != llHWGuestProperties.end();
14762 /*nothing*/)
14763 {
14764 uint32_t fFlags = it->second.mFlags;
14765 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14766 {
14767 /* iterator where we need to continue after the erase call
14768 * (C++03 is a fact still, and it doesn't return the iterator
14769 * which would allow continuing) */
14770 HWData::GuestPropertyMap::iterator it2 = it;
14771 ++it2;
14772 llHWGuestProperties.erase(it);
14773 it = it2;
14774 fNeedsSaving = true;
14775 }
14776 else
14777 {
14778 ++it;
14779 }
14780 }
14781
14782 if (fNeedsSaving)
14783 {
14784 mData->mCurrentStateModified = TRUE;
14785 stsFlags |= SaveSTS_CurStateModified;
14786 }
14787 }
14788#endif /* VBOX_WITH_GUEST_PROPS */
14789
14790 rc = i_saveStateSettings(stsFlags);
14791
14792 if ( ( oldMachineState != MachineState_PoweredOff
14793 && oldMachineState != MachineState_Aborted
14794 && oldMachineState != MachineState_Teleported
14795 )
14796 && ( aMachineState == MachineState_PoweredOff
14797 || aMachineState == MachineState_Aborted
14798 || aMachineState == MachineState_Teleported
14799 )
14800 )
14801 {
14802 /* we've been shut down for any reason */
14803 /* no special action so far */
14804 }
14805
14806 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14807 LogFlowThisFuncLeave();
14808 return rc;
14809}
14810
14811/**
14812 * Sends the current machine state value to the VM process.
14813 *
14814 * @note Locks this object for reading, then calls a client process.
14815 */
14816HRESULT SessionMachine::i_updateMachineStateOnClient()
14817{
14818 AutoCaller autoCaller(this);
14819 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14820
14821 ComPtr<IInternalSessionControl> directControl;
14822 {
14823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14824 AssertReturn(!!mData, E_FAIL);
14825 if (mData->mSession.mLockType == LockType_VM)
14826 directControl = mData->mSession.mDirectControl;
14827
14828 /* directControl may be already set to NULL here in #OnSessionEnd()
14829 * called too early by the direct session process while there is still
14830 * some operation (like deleting the snapshot) in progress. The client
14831 * process in this case is waiting inside Session::close() for the
14832 * "end session" process object to complete, while #uninit() called by
14833 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14834 * operation to complete. For now, we accept this inconsistent behavior
14835 * and simply do nothing here. */
14836
14837 if (mData->mSession.mState == SessionState_Unlocking)
14838 return S_OK;
14839 }
14840
14841 /* ignore notifications sent after #OnSessionEnd() is called */
14842 if (!directControl)
14843 return S_OK;
14844
14845 return directControl->UpdateMachineState(mData->mMachineState);
14846}
14847
14848
14849/*static*/
14850HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14851{
14852 va_list args;
14853 va_start(args, pcszMsg);
14854 HRESULT rc = setErrorInternal(aResultCode,
14855 getStaticClassIID(),
14856 getStaticComponentName(),
14857 Utf8Str(pcszMsg, args),
14858 false /* aWarning */,
14859 true /* aLogIt */);
14860 va_end(args);
14861 return rc;
14862}
14863
14864
14865HRESULT Machine::updateState(MachineState_T aState)
14866{
14867 NOREF(aState);
14868 ReturnComNotImplemented();
14869}
14870
14871HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14872{
14873 NOREF(aProgress);
14874 ReturnComNotImplemented();
14875}
14876
14877HRESULT Machine::endPowerUp(LONG aResult)
14878{
14879 NOREF(aResult);
14880 ReturnComNotImplemented();
14881}
14882
14883HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14884{
14885 NOREF(aProgress);
14886 ReturnComNotImplemented();
14887}
14888
14889HRESULT Machine::endPoweringDown(LONG aResult,
14890 const com::Utf8Str &aErrMsg)
14891{
14892 NOREF(aResult);
14893 NOREF(aErrMsg);
14894 ReturnComNotImplemented();
14895}
14896
14897HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14898 BOOL *aMatched,
14899 ULONG *aMaskedInterfaces)
14900{
14901 NOREF(aDevice);
14902 NOREF(aMatched);
14903 NOREF(aMaskedInterfaces);
14904 ReturnComNotImplemented();
14905
14906}
14907
14908HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14909{
14910 NOREF(aId); NOREF(aCaptureFilename);
14911 ReturnComNotImplemented();
14912}
14913
14914HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14915 BOOL aDone)
14916{
14917 NOREF(aId);
14918 NOREF(aDone);
14919 ReturnComNotImplemented();
14920}
14921
14922HRESULT Machine::autoCaptureUSBDevices()
14923{
14924 ReturnComNotImplemented();
14925}
14926
14927HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14928{
14929 NOREF(aDone);
14930 ReturnComNotImplemented();
14931}
14932
14933HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14934 ComPtr<IProgress> &aProgress)
14935{
14936 NOREF(aSession);
14937 NOREF(aProgress);
14938 ReturnComNotImplemented();
14939}
14940
14941HRESULT Machine::finishOnlineMergeMedium()
14942{
14943 ReturnComNotImplemented();
14944}
14945
14946HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14947{
14948 RT_NOREF(aParms, aID);
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14953{
14954 RT_NOREF(aID);
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::clipboardAreaAttach(ULONG aID)
14959{
14960 RT_NOREF(aID);
14961 ReturnComNotImplemented();
14962}
14963HRESULT Machine::clipboardAreaDetach(ULONG aID)
14964{
14965 RT_NOREF(aID);
14966 ReturnComNotImplemented();
14967}
14968
14969HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
14970{
14971 RT_NOREF(aID);
14972 ReturnComNotImplemented();
14973}
14974
14975HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
14976{
14977 RT_NOREF(aID, aRefCount);
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14982 std::vector<com::Utf8Str> &aValues,
14983 std::vector<LONG64> &aTimestamps,
14984 std::vector<com::Utf8Str> &aFlags)
14985{
14986 NOREF(aNames);
14987 NOREF(aValues);
14988 NOREF(aTimestamps);
14989 NOREF(aFlags);
14990 ReturnComNotImplemented();
14991}
14992
14993HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14994 const com::Utf8Str &aValue,
14995 LONG64 aTimestamp,
14996 const com::Utf8Str &aFlags)
14997{
14998 NOREF(aName);
14999 NOREF(aValue);
15000 NOREF(aTimestamp);
15001 NOREF(aFlags);
15002 ReturnComNotImplemented();
15003}
15004
15005HRESULT Machine::lockMedia()
15006{
15007 ReturnComNotImplemented();
15008}
15009
15010HRESULT Machine::unlockMedia()
15011{
15012 ReturnComNotImplemented();
15013}
15014
15015HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15016 ComPtr<IMediumAttachment> &aNewAttachment)
15017{
15018 NOREF(aAttachment);
15019 NOREF(aNewAttachment);
15020 ReturnComNotImplemented();
15021}
15022
15023HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15024 ULONG aCpuUser,
15025 ULONG aCpuKernel,
15026 ULONG aCpuIdle,
15027 ULONG aMemTotal,
15028 ULONG aMemFree,
15029 ULONG aMemBalloon,
15030 ULONG aMemShared,
15031 ULONG aMemCache,
15032 ULONG aPagedTotal,
15033 ULONG aMemAllocTotal,
15034 ULONG aMemFreeTotal,
15035 ULONG aMemBalloonTotal,
15036 ULONG aMemSharedTotal,
15037 ULONG aVmNetRx,
15038 ULONG aVmNetTx)
15039{
15040 NOREF(aValidStats);
15041 NOREF(aCpuUser);
15042 NOREF(aCpuKernel);
15043 NOREF(aCpuIdle);
15044 NOREF(aMemTotal);
15045 NOREF(aMemFree);
15046 NOREF(aMemBalloon);
15047 NOREF(aMemShared);
15048 NOREF(aMemCache);
15049 NOREF(aPagedTotal);
15050 NOREF(aMemAllocTotal);
15051 NOREF(aMemFreeTotal);
15052 NOREF(aMemBalloonTotal);
15053 NOREF(aMemSharedTotal);
15054 NOREF(aVmNetRx);
15055 NOREF(aVmNetTx);
15056 ReturnComNotImplemented();
15057}
15058
15059HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15060 com::Utf8Str &aResult)
15061{
15062 NOREF(aAuthParams);
15063 NOREF(aResult);
15064 ReturnComNotImplemented();
15065}
15066
15067com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15068{
15069 com::Utf8Str strControllerName = "Unknown";
15070 switch (aBusType)
15071 {
15072 case StorageBus_IDE:
15073 {
15074 strControllerName = "IDE";
15075 break;
15076 }
15077 case StorageBus_SATA:
15078 {
15079 strControllerName = "SATA";
15080 break;
15081 }
15082 case StorageBus_SCSI:
15083 {
15084 strControllerName = "SCSI";
15085 break;
15086 }
15087 case StorageBus_Floppy:
15088 {
15089 strControllerName = "Floppy";
15090 break;
15091 }
15092 case StorageBus_SAS:
15093 {
15094 strControllerName = "SAS";
15095 break;
15096 }
15097 case StorageBus_USB:
15098 {
15099 strControllerName = "USB";
15100 break;
15101 }
15102 default:
15103 break;
15104 }
15105 return strControllerName;
15106}
15107
15108HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15109{
15110 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15111
15112 AutoCaller autoCaller(this);
15113 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15114
15115 HRESULT rc = S_OK;
15116
15117 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15118 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15119 rc = getUSBDeviceFilters(usbDeviceFilters);
15120 if (FAILED(rc)) return rc;
15121
15122 NOREF(aFlags);
15123 com::Utf8Str osTypeId;
15124 ComObjPtr<GuestOSType> osType = NULL;
15125
15126 /* Get the guest os type as a string from the VB. */
15127 rc = getOSTypeId(osTypeId);
15128 if (FAILED(rc)) return rc;
15129
15130 /* Get the os type obj that coresponds, can be used to get
15131 * the defaults for this guest OS. */
15132 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15133 if (FAILED(rc)) return rc;
15134
15135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15136
15137 /* Let the OS type select 64-bit ness. */
15138 mHWData->mLongMode = osType->i_is64Bit()
15139 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15140
15141 /* Let the OS type enable the X2APIC */
15142 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15143
15144 /* This one covers IOAPICEnabled. */
15145 mBIOSSettings->i_applyDefaults(osType);
15146
15147 /* Initialize default record settings. */
15148 mRecordingSettings->i_applyDefaults();
15149
15150 /* Initialize default BIOS settings here */
15151 /* Hardware virtualization must be ON by default */
15152 mHWData->mAPIC = true;
15153 mHWData->mHWVirtExEnabled = true;
15154
15155 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15156 if (FAILED(rc)) return rc;
15157
15158 /* Graphics stuff. */
15159 GraphicsControllerType_T graphicsController;
15160 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15161 if (FAILED(rc)) return rc;
15162
15163 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15164 if (FAILED(rc)) return rc;
15165
15166 ULONG vramSize;
15167 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15168 if (FAILED(rc)) return rc;
15169
15170 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15171 if (FAILED(rc)) return rc;
15172
15173 BOOL fAccelerate2DVideoEnabled;
15174 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15175 if (FAILED(rc)) return rc;
15176
15177 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15178 if (FAILED(rc)) return rc;
15179
15180 BOOL fAccelerate3DEnabled;
15181 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15182 if (FAILED(rc)) return rc;
15183
15184 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15185 if (FAILED(rc)) return rc;
15186
15187 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15188 if (FAILED(rc)) return rc;
15189
15190 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15191 if (FAILED(rc)) return rc;
15192
15193 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15194 if (FAILED(rc)) return rc;
15195
15196 BOOL mRTCUseUTC;
15197 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15198 if (FAILED(rc)) return rc;
15199
15200 setRTCUseUTC(mRTCUseUTC);
15201 if (FAILED(rc)) return rc;
15202
15203 /* the setter does more than just the assignment, so use it */
15204 ChipsetType_T enmChipsetType;
15205 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15206 if (FAILED(rc)) return rc;
15207
15208 rc = COMSETTER(ChipsetType)(enmChipsetType);
15209 if (FAILED(rc)) return rc;
15210
15211 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15212 if (FAILED(rc)) return rc;
15213
15214 /* Apply network adapters defaults */
15215 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15216 mNetworkAdapters[slot]->i_applyDefaults(osType);
15217
15218 /* Apply serial port defaults */
15219 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15220 mSerialPorts[slot]->i_applyDefaults(osType);
15221
15222 /* Apply parallel port defaults - not OS dependent*/
15223 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15224 mParallelPorts[slot]->i_applyDefaults();
15225
15226 /* Audio stuff. */
15227 AudioControllerType_T audioController;
15228 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15229 if (FAILED(rc)) return rc;
15230
15231 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15232 if (FAILED(rc)) return rc;
15233
15234 AudioCodecType_T audioCodec;
15235 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15236 if (FAILED(rc)) return rc;
15237
15238 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15239 if (FAILED(rc)) return rc;
15240
15241 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15242 if (FAILED(rc)) return rc;
15243
15244 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15245 if (FAILED(rc)) return rc;
15246
15247 /* Storage Controllers */
15248 StorageControllerType_T hdStorageControllerType;
15249 StorageBus_T hdStorageBusType;
15250 StorageControllerType_T dvdStorageControllerType;
15251 StorageBus_T dvdStorageBusType;
15252 BOOL recommendedFloppy;
15253 ComPtr<IStorageController> floppyController;
15254 ComPtr<IStorageController> hdController;
15255 ComPtr<IStorageController> dvdController;
15256 Utf8Str strFloppyName, strDVDName, strHDName;
15257
15258 /* GUI auto generates controller names using bus type. Do the same*/
15259 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15260
15261 /* Floppy recommended? add one. */
15262 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15263 if (FAILED(rc)) return rc;
15264 if (recommendedFloppy)
15265 {
15266 rc = addStorageController(strFloppyName,
15267 StorageBus_Floppy,
15268 floppyController);
15269 if (FAILED(rc)) return rc;
15270 }
15271
15272 /* Setup one DVD storage controller. */
15273 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15274 if (FAILED(rc)) return rc;
15275
15276 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15277 if (FAILED(rc)) return rc;
15278
15279 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15280
15281 rc = addStorageController(strDVDName,
15282 dvdStorageBusType,
15283 dvdController);
15284 if (FAILED(rc)) return rc;
15285
15286 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15287 if (FAILED(rc)) return rc;
15288
15289 /* Setup one HDD storage controller. */
15290 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15291 if (FAILED(rc)) return rc;
15292
15293 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15294 if (FAILED(rc)) return rc;
15295
15296 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15297
15298 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15299 {
15300 rc = addStorageController(strHDName,
15301 hdStorageBusType,
15302 hdController);
15303 if (FAILED(rc)) return rc;
15304
15305 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15306 if (FAILED(rc)) return rc;
15307 }
15308 else
15309 {
15310 /* The HD controller is the same as DVD: */
15311 hdController = dvdController;
15312 }
15313
15314 /* Limit the AHCI port count if it's used because windows has trouble with
15315 * too many ports and other guest (OS X in particular) may take extra long
15316 * boot: */
15317
15318 // pParent = static_cast<Medium*>(aP)
15319 IStorageController *temp = hdController;
15320 ComObjPtr<StorageController> storageController;
15321 storageController = static_cast<StorageController *>(temp);
15322
15323 // tempHDController = aHDController;
15324 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15325 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15326 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15327 storageController->COMSETTER(PortCount)(1);
15328
15329 /* USB stuff */
15330
15331 bool ohciEnabled = false;
15332
15333 ComPtr<IUSBController> usbController;
15334 BOOL recommendedUSB3;
15335 BOOL recommendedUSB;
15336 BOOL usbProxyAvailable;
15337
15338 getUSBProxyAvailable(&usbProxyAvailable);
15339 if (FAILED(rc)) return rc;
15340
15341 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15342 if (FAILED(rc)) return rc;
15343 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15344 if (FAILED(rc)) return rc;
15345
15346 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15347 {
15348#ifdef VBOX_WITH_EXTPACK
15349 /* USB 3.0 is only available if the proper ExtPack is installed. */
15350 ExtPackManager *aManager = mParent->i_getExtPackManager();
15351 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15352 {
15353 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15354 if (FAILED(rc)) return rc;
15355
15356 /* xHci includes OHCI */
15357 ohciEnabled = true;
15358 }
15359#endif
15360 }
15361 if ( !ohciEnabled
15362 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15363 {
15364 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15365 if (FAILED(rc)) return rc;
15366 ohciEnabled = true;
15367
15368#ifdef VBOX_WITH_EXTPACK
15369 /* USB 2.0 is only available if the proper ExtPack is installed.
15370 * Note. Configuring EHCI here and providing messages about
15371 * the missing extpack isn't exactly clean, but it is a
15372 * necessary evil to patch over legacy compatability issues
15373 * introduced by the new distribution model. */
15374 ExtPackManager *manager = mParent->i_getExtPackManager();
15375 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15376 {
15377 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15378 if (FAILED(rc)) return rc;
15379 }
15380#endif
15381 }
15382
15383 /* Set recommended human interface device types: */
15384 BOOL recommendedUSBHID;
15385 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15386 if (FAILED(rc)) return rc;
15387
15388 if (recommendedUSBHID)
15389 {
15390 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15391 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15392 if (!ohciEnabled && !usbDeviceFilters.isNull())
15393 {
15394 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15395 if (FAILED(rc)) return rc;
15396 }
15397 }
15398
15399 BOOL recommendedUSBTablet;
15400 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15401 if (FAILED(rc)) return rc;
15402
15403 if (recommendedUSBTablet)
15404 {
15405 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15406 if (!ohciEnabled && !usbDeviceFilters.isNull())
15407 {
15408 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15409 if (FAILED(rc)) return rc;
15410 }
15411 }
15412 return S_OK;
15413}
15414
15415/* This isn't handled entirely by the wrapper generator yet. */
15416#ifdef VBOX_WITH_XPCOM
15417NS_DECL_CLASSINFO(SessionMachine)
15418NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15419
15420NS_DECL_CLASSINFO(SnapshotMachine)
15421NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15422#endif
Note: See TracBrowser for help on using the repository browser.

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