VirtualBox

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

Last change on this file since 86115 was 85908, checked in by vboxsync, 4 years ago

Main: bugref:9623: Fixed issue causes the assertion during VBoxSVC unload. Fixed the issues during snapshots creating/deleting

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 537.1 KB
Line 
1/* $Id: MachineImpl.cpp 85908 2020-08-27 17:10:34Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#ifdef VBOX_WITH_SHARED_CLIPBOARD
91# include <VBox/HostServices/VBoxClipboardSvc.h>
92#endif
93
94#include "VBox/com/MultiResult.h"
95
96#include <algorithm>
97
98#ifdef VBOX_WITH_DTRACE_R3_MAIN
99# include "dtrace/VBoxAPI.h"
100#endif
101
102#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
103# define HOSTSUFF_EXE ".exe"
104#else /* !RT_OS_WINDOWS */
105# define HOSTSUFF_EXE ""
106#endif /* !RT_OS_WINDOWS */
107
108// defines / prototypes
109/////////////////////////////////////////////////////////////////////////////
110
111/////////////////////////////////////////////////////////////////////////////
112// Machine::Data structure
113/////////////////////////////////////////////////////////////////////////////
114
115Machine::Data::Data()
116{
117 mRegistered = FALSE;
118 pMachineConfigFile = NULL;
119 /* Contains hints on what has changed when the user is using the VM (config
120 * changes, running the VM, ...). This is used to decide if a config needs
121 * to be written to disk. */
122 flModifications = 0;
123 /* VM modification usually also trigger setting the current state to
124 * "Modified". Although this is not always the case. An e.g. is the VM
125 * initialization phase or when snapshot related data is changed. The
126 * actually behavior is controlled by the following flag. */
127 m_fAllowStateModification = false;
128 mAccessible = FALSE;
129 /* mUuid is initialized in Machine::init() */
130
131 mMachineState = MachineState_PoweredOff;
132 RTTimeNow(&mLastStateChange);
133
134 mMachineStateDeps = 0;
135 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
136 mMachineStateChangePending = 0;
137
138 mCurrentStateModified = TRUE;
139 mGuestPropertiesModified = FALSE;
140
141 mSession.mPID = NIL_RTPROCESS;
142 mSession.mLockType = LockType_Null;
143 mSession.mState = SessionState_Unlocked;
144}
145
146Machine::Data::~Data()
147{
148 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
149 {
150 RTSemEventMultiDestroy(mMachineStateDepsSem);
151 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
152 }
153 if (pMachineConfigFile)
154 {
155 delete pMachineConfigFile;
156 pMachineConfigFile = NULL;
157 }
158}
159
160/////////////////////////////////////////////////////////////////////////////
161// Machine::HWData structure
162/////////////////////////////////////////////////////////////////////////////
163
164Machine::HWData::HWData()
165{
166 /* default values for a newly created machine */
167 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
168 mMemorySize = 128;
169 mCPUCount = 1;
170 mCPUHotPlugEnabled = false;
171 mMemoryBalloonSize = 0;
172 mPageFusionEnabled = false;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185 mHWVirtExVirtVmsaveVmload = true;
186#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
187 mPAEEnabled = true;
188#else
189 mPAEEnabled = false;
190#endif
191 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
192 mTripleFaultReset = false;
193 mAPIC = true;
194 mX2APIC = false;
195 mIBPBOnVMExit = false;
196 mIBPBOnVMEntry = false;
197 mSpecCtrl = false;
198 mSpecCtrlByHost = false;
199 mL1DFlushOnSched = true;
200 mL1DFlushOnVMEntry = false;
201 mMDSClearOnSched = true;
202 mMDSClearOnVMEntry = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mClipboardFileTransfersEnabled = FALSE;
218
219 mDnDMode = DnDMode_Disabled;
220
221 mFirmwareType = FirmwareType_BIOS;
222 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
223 mPointingHIDType = PointingHIDType_PS2Mouse;
224 mChipsetType = ChipsetType_PIIX3;
225 mParavirtProvider = ParavirtProvider_Default;
226 mEmulatedUSBCardReaderEnabled = FALSE;
227
228 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
229 mCPUAttached[i] = false;
230
231 mIOCacheEnabled = true;
232 mIOCacheSize = 5; /* 5MB */
233}
234
235Machine::HWData::~HWData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param strOsType OS Type string (stored as is if aOsType is NULL).
283 * @param aOsType OS Type of this machine or NULL.
284 * @param aId UUID for the new machine.
285 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
286 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
287 * scheme (includes the UUID).
288 *
289 * @return Success indicator. if not S_OK, the machine object is invalid
290 */
291HRESULT Machine::init(VirtualBox *aParent,
292 const Utf8Str &strConfigFile,
293 const Utf8Str &strName,
294 const StringsList &llGroups,
295 const Utf8Str &strOsType,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 if (llGroups.size())
332 mUserData->s.llGroups = llGroups;
333
334 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
335 // the "name sync" flag determines whether the machine directory gets renamed along
336 // with the machine file; say so if the settings file name is the same as the
337 // settings file parent directory (machine directory)
338 mUserData->s.fNameSync = i_isInOwnDir();
339
340 // initialize the default snapshots folder
341 rc = COMSETTER(SnapshotFolder)(NULL);
342 AssertComRC(rc);
343
344 if (aOsType)
345 {
346 /* Store OS type */
347 mUserData->s.strOsType = aOsType->i_id();
348
349 /* Let the OS type select 64-bit ness. */
350 mHWData->mLongMode = aOsType->i_is64Bit()
351 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
352
353 /* Let the OS type enable the X2APIC */
354 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
355 }
356 else if (!strOsType.isEmpty())
357 {
358 /* Store OS type */
359 mUserData->s.strOsType = strOsType;
360
361 /* No guest OS type object. Pick some plausible defaults which the
362 * host can handle. There's no way to know or validate anything. */
363 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
364 mHWData->mX2APIC = false;
365 }
366
367 /* Apply BIOS defaults. */
368 mBIOSSettings->i_applyDefaults(aOsType);
369
370 /* Apply record defaults. */
371 mRecordingSettings->i_applyDefaults();
372
373 /* Apply network adapters defaults */
374 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
375 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
376
377 /* Apply serial port defaults */
378 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
379 mSerialPorts[slot]->i_applyDefaults(aOsType);
380
381 /* Apply parallel port defaults */
382 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
383 mParallelPorts[slot]->i_applyDefaults();
384
385 /* At this point the changing of the current state modification
386 * flag is allowed. */
387 i_allowStateModification();
388
389 /* commit all changes made during the initialization */
390 i_commit();
391 }
392
393 /* Confirm a successful initialization when it's the case */
394 if (SUCCEEDED(rc))
395 {
396 if (mData->mAccessible)
397 autoInitSpan.setSucceeded();
398 else
399 autoInitSpan.setLimited();
400 }
401
402 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
403 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
404 mData->mRegistered,
405 mData->mAccessible,
406 rc));
407
408 LogFlowThisFuncLeave();
409
410 return rc;
411}
412
413/**
414 * Initializes a new instance with data from machine XML (formerly Init_Registered).
415 * Gets called in two modes:
416 *
417 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
418 * UUID is specified and we mark the machine as "registered";
419 *
420 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
421 * and the machine remains unregistered until RegisterMachine() is called.
422 *
423 * @param aParent Associated parent object
424 * @param strConfigFile Local file system path to the VM settings file (can
425 * be relative to the VirtualBox config directory).
426 * @param aId UUID of the machine or NULL (see above).
427 *
428 * @return Success indicator. if not S_OK, the machine object is invalid
429 */
430HRESULT Machine::initFromSettings(VirtualBox *aParent,
431 const Utf8Str &strConfigFile,
432 const Guid *aId)
433{
434 LogFlowThisFuncEnter();
435 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
436
437 /* Enclose the state transition NotReady->InInit->Ready */
438 AutoInitSpan autoInitSpan(this);
439 AssertReturn(autoInitSpan.isOk(), E_FAIL);
440
441 HRESULT rc = initImpl(aParent, strConfigFile);
442 if (FAILED(rc)) return rc;
443
444 if (aId)
445 {
446 // loading a registered VM:
447 unconst(mData->mUuid) = *aId;
448 mData->mRegistered = TRUE;
449 // now load the settings from XML:
450 rc = i_registeredInit();
451 // this calls initDataAndChildObjects() and loadSettings()
452 }
453 else
454 {
455 // opening an unregistered VM (VirtualBox::OpenMachine()):
456 rc = initDataAndChildObjects();
457
458 if (SUCCEEDED(rc))
459 {
460 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
461 mData->mAccessible = TRUE;
462
463 try
464 {
465 // load and parse machine XML; this will throw on XML or logic errors
466 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
467
468 // reject VM UUID duplicates, they can happen if someone
469 // tries to register an already known VM config again
470 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
471 true /* fPermitInaccessible */,
472 false /* aDoSetError */,
473 NULL) != VBOX_E_OBJECT_NOT_FOUND)
474 {
475 throw setError(E_FAIL,
476 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
477 mData->m_strConfigFile.c_str());
478 }
479
480 // use UUID from machine config
481 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
482
483 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
484 NULL /* puuidRegistry */);
485 if (FAILED(rc)) throw rc;
486
487 /* At this point the changing of the current state modification
488 * flag is allowed. */
489 i_allowStateModification();
490
491 i_commit();
492 }
493 catch (HRESULT err)
494 {
495 /* we assume that error info is set by the thrower */
496 rc = err;
497 }
498 catch (...)
499 {
500 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
501 }
502 }
503 }
504
505 /* Confirm a successful initialization when it's the case */
506 if (SUCCEEDED(rc))
507 {
508 if (mData->mAccessible)
509 autoInitSpan.setSucceeded();
510 else
511 {
512 autoInitSpan.setLimited();
513
514 // uninit media from this machine's media registry, or else
515 // reloading the settings will fail
516 mParent->i_unregisterMachineMedia(i_getId());
517 }
518 }
519
520 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
521 "rc=%08X\n",
522 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
523 mData->mRegistered, mData->mAccessible, rc));
524
525 LogFlowThisFuncLeave();
526
527 return rc;
528}
529
530/**
531 * Initializes a new instance from a machine config that is already in memory
532 * (import OVF case). Since we are importing, the UUID in the machine
533 * config is ignored and we always generate a fresh one.
534 *
535 * @param aParent Associated parent object.
536 * @param strName Name for the new machine; this overrides what is specified in config.
537 * @param strSettingsFilename File name of .vbox file.
538 * @param config Machine configuration loaded and parsed from XML.
539 *
540 * @return Success indicator. if not S_OK, the machine object is invalid
541 */
542HRESULT Machine::init(VirtualBox *aParent,
543 const Utf8Str &strName,
544 const Utf8Str &strSettingsFilename,
545 const settings::MachineConfigFile &config)
546{
547 LogFlowThisFuncEnter();
548
549 /* Enclose the state transition NotReady->InInit->Ready */
550 AutoInitSpan autoInitSpan(this);
551 AssertReturn(autoInitSpan.isOk(), E_FAIL);
552
553 HRESULT rc = initImpl(aParent, strSettingsFilename);
554 if (FAILED(rc)) return rc;
555
556 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
557 if (FAILED(rc)) return rc;
558
559 rc = initDataAndChildObjects();
560
561 if (SUCCEEDED(rc))
562 {
563 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
564 mData->mAccessible = TRUE;
565
566 // create empty machine config for instance data
567 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
568
569 // generate fresh UUID, ignore machine config
570 unconst(mData->mUuid).create();
571
572 rc = i_loadMachineDataFromSettings(config,
573 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
574
575 // override VM name as well, it may be different
576 mUserData->s.strName = strName;
577
578 if (SUCCEEDED(rc))
579 {
580 /* At this point the changing of the current state modification
581 * flag is allowed. */
582 i_allowStateModification();
583
584 /* commit all changes made during the initialization */
585 i_commit();
586 }
587 }
588
589 /* Confirm a successful initialization when it's the case */
590 if (SUCCEEDED(rc))
591 {
592 if (mData->mAccessible)
593 autoInitSpan.setSucceeded();
594 else
595 {
596 /* Ignore all errors from unregistering, they would destroy
597- * the more interesting error information we already have,
598- * pinpointing the issue with the VM config. */
599 ErrorInfoKeeper eik;
600
601 autoInitSpan.setLimited();
602
603 // uninit media from this machine's media registry, or else
604 // reloading the settings will fail
605 mParent->i_unregisterMachineMedia(i_getId());
606 }
607 }
608
609 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
610 "rc=%08X\n",
611 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
612 mData->mRegistered, mData->mAccessible, rc));
613
614 LogFlowThisFuncLeave();
615
616 return rc;
617}
618
619/**
620 * Shared code between the various init() implementations.
621 * @param aParent The VirtualBox object.
622 * @param strConfigFile Settings file.
623 * @return
624 */
625HRESULT Machine::initImpl(VirtualBox *aParent,
626 const Utf8Str &strConfigFile)
627{
628 LogFlowThisFuncEnter();
629
630 AssertReturn(aParent, E_INVALIDARG);
631 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
632
633 HRESULT rc = S_OK;
634
635 /* share the parent weakly */
636 unconst(mParent) = aParent;
637
638 /* allocate the essential machine data structure (the rest will be
639 * allocated later by initDataAndChildObjects() */
640 mData.allocate();
641
642 /* memorize the config file name (as provided) */
643 mData->m_strConfigFile = strConfigFile;
644
645 /* get the full file name */
646 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
647 if (RT_FAILURE(vrc1))
648 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
649 tr("Invalid machine settings file name '%s' (%Rrc)"),
650 strConfigFile.c_str(),
651 vrc1);
652
653 LogFlowThisFuncLeave();
654
655 return rc;
656}
657
658/**
659 * Tries to create a machine settings file in the path stored in the machine
660 * instance data. Used when a new machine is created to fail gracefully if
661 * the settings file could not be written (e.g. because machine dir is read-only).
662 * @return
663 */
664HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
665{
666 HRESULT rc = S_OK;
667
668 // when we create a new machine, we must be able to create the settings file
669 RTFILE f = NIL_RTFILE;
670 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
671 if ( RT_SUCCESS(vrc)
672 || vrc == VERR_SHARING_VIOLATION
673 )
674 {
675 if (RT_SUCCESS(vrc))
676 RTFileClose(f);
677 if (!fForceOverwrite)
678 rc = setError(VBOX_E_FILE_ERROR,
679 tr("Machine settings file '%s' already exists"),
680 mData->m_strConfigFileFull.c_str());
681 else
682 {
683 /* try to delete the config file, as otherwise the creation
684 * of a new settings file will fail. */
685 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
686 if (RT_FAILURE(vrc2))
687 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
688 tr("Could not delete the existing settings file '%s' (%Rrc)"),
689 mData->m_strConfigFileFull.c_str(), vrc2);
690 }
691 }
692 else if ( vrc != VERR_FILE_NOT_FOUND
693 && vrc != VERR_PATH_NOT_FOUND
694 )
695 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
696 tr("Invalid machine settings file name '%s' (%Rrc)"),
697 mData->m_strConfigFileFull.c_str(),
698 vrc);
699 return rc;
700}
701
702/**
703 * Initializes the registered machine by loading the settings file.
704 * This method is separated from #init() in order to make it possible to
705 * retry the operation after VirtualBox startup instead of refusing to
706 * startup the whole VirtualBox server in case if the settings file of some
707 * registered VM is invalid or inaccessible.
708 *
709 * @note Must be always called from this object's write lock
710 * (unless called from #init() that doesn't need any locking).
711 * @note Locks the mUSBController method for writing.
712 * @note Subclasses must not call this method.
713 */
714HRESULT Machine::i_registeredInit()
715{
716 AssertReturn(!i_isSessionMachine(), E_FAIL);
717 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
718 AssertReturn(mData->mUuid.isValid(), E_FAIL);
719 AssertReturn(!mData->mAccessible, E_FAIL);
720
721 HRESULT rc = initDataAndChildObjects();
722
723 if (SUCCEEDED(rc))
724 {
725 /* Temporarily reset the registered flag in order to let setters
726 * potentially called from loadSettings() succeed (isMutable() used in
727 * all setters will return FALSE for a Machine instance if mRegistered
728 * is TRUE). */
729 mData->mRegistered = FALSE;
730
731 try
732 {
733 // load and parse machine XML; this will throw on XML or logic errors
734 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
735
736 if (mData->mUuid != mData->pMachineConfigFile->uuid)
737 throw setError(E_FAIL,
738 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
739 mData->pMachineConfigFile->uuid.raw(),
740 mData->m_strConfigFileFull.c_str(),
741 mData->mUuid.toString().c_str(),
742 mParent->i_settingsFilePath().c_str());
743
744 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
745 NULL /* const Guid *puuidRegistry */);
746 if (FAILED(rc)) throw rc;
747 }
748 catch (HRESULT err)
749 {
750 /* we assume that error info is set by the thrower */
751 rc = err;
752 }
753 catch (...)
754 {
755 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
756 }
757
758 /* Restore the registered flag (even on failure) */
759 mData->mRegistered = TRUE;
760 }
761
762 if (SUCCEEDED(rc))
763 {
764 /* Set mAccessible to TRUE only if we successfully locked and loaded
765 * the settings file */
766 mData->mAccessible = TRUE;
767
768 /* commit all changes made during loading the settings file */
769 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
770 /// @todo r=klaus for some reason the settings loading logic backs up
771 // the settings, and therefore a commit is needed. Should probably be changed.
772 }
773 else
774 {
775 /* If the machine is registered, then, instead of returning a
776 * failure, we mark it as inaccessible and set the result to
777 * success to give it a try later */
778
779 /* fetch the current error info */
780 mData->mAccessError = com::ErrorInfo();
781 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
782
783 /* rollback all changes */
784 i_rollback(false /* aNotify */);
785
786 // uninit media from this machine's media registry, or else
787 // reloading the settings will fail
788 mParent->i_unregisterMachineMedia(i_getId());
789
790 /* uninitialize the common part to make sure all data is reset to
791 * default (null) values */
792 uninitDataAndChildObjects();
793
794 rc = S_OK;
795 }
796
797 return rc;
798}
799
800/**
801 * Uninitializes the instance.
802 * Called either from FinalRelease() or by the parent when it gets destroyed.
803 *
804 * @note The caller of this method must make sure that this object
805 * a) doesn't have active callers on the current thread and b) is not locked
806 * by the current thread; otherwise uninit() will hang either a) due to
807 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
808 * a dead-lock caused by this thread waiting for all callers on the other
809 * threads are done but preventing them from doing so by holding a lock.
810 */
811void Machine::uninit()
812{
813 LogFlowThisFuncEnter();
814
815 Assert(!isWriteLockOnCurrentThread());
816
817 Assert(!uRegistryNeedsSaving);
818 if (uRegistryNeedsSaving)
819 {
820 AutoCaller autoCaller(this);
821 if (SUCCEEDED(autoCaller.rc()))
822 {
823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
824 i_saveSettings(NULL, Machine::SaveS_Force);
825 }
826 }
827
828 /* Enclose the state transition Ready->InUninit->NotReady */
829 AutoUninitSpan autoUninitSpan(this);
830 if (autoUninitSpan.uninitDone())
831 return;
832
833 Assert(!i_isSnapshotMachine());
834 Assert(!i_isSessionMachine());
835 Assert(!!mData);
836
837 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
838 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
839
840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
841
842 if (!mData->mSession.mMachine.isNull())
843 {
844 /* Theoretically, this can only happen if the VirtualBox server has been
845 * terminated while there were clients running that owned open direct
846 * sessions. Since in this case we are definitely called by
847 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
848 * won't happen on the client watcher thread (because it has a
849 * VirtualBox caller for the duration of the
850 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
851 * cannot happen until the VirtualBox caller is released). This is
852 * important, because SessionMachine::uninit() cannot correctly operate
853 * after we return from this method (it expects the Machine instance is
854 * still valid). We'll call it ourselves below.
855 */
856 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
857 (SessionMachine*)mData->mSession.mMachine));
858
859 if (Global::IsOnlineOrTransient(mData->mMachineState))
860 {
861 Log1WarningThisFunc(("Setting state to Aborted!\n"));
862 /* set machine state using SessionMachine reimplementation */
863 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
864 }
865
866 /*
867 * Uninitialize SessionMachine using public uninit() to indicate
868 * an unexpected uninitialization.
869 */
870 mData->mSession.mMachine->uninit();
871 /* SessionMachine::uninit() must set mSession.mMachine to null */
872 Assert(mData->mSession.mMachine.isNull());
873 }
874
875 // uninit media from this machine's media registry, if they're still there
876 Guid uuidMachine(i_getId());
877
878 /* the lock is no more necessary (SessionMachine is uninitialized) */
879 alock.release();
880
881 /* XXX This will fail with
882 * "cannot be closed because it is still attached to 1 virtual machines"
883 * because at this point we did not call uninitDataAndChildObjects() yet
884 * and therefore also removeBackReference() for all these mediums was not called! */
885
886 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
887 mParent->i_unregisterMachineMedia(uuidMachine);
888
889 // has machine been modified?
890 if (mData->flModifications)
891 {
892 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
893 i_rollback(false /* aNotify */);
894 }
895
896 if (mData->mAccessible)
897 uninitDataAndChildObjects();
898
899 /* free the essential data structure last */
900 mData.free();
901
902 LogFlowThisFuncLeave();
903}
904
905// Wrapped IMachine properties
906/////////////////////////////////////////////////////////////////////////////
907HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
908{
909 /* mParent is constant during life time, no need to lock */
910 ComObjPtr<VirtualBox> pVirtualBox(mParent);
911 aParent = pVirtualBox;
912
913 return S_OK;
914}
915
916
917HRESULT Machine::getAccessible(BOOL *aAccessible)
918{
919 /* In some cases (medium registry related), it is necessary to be able to
920 * go through the list of all machines. Happens when an inaccessible VM
921 * has a sensible medium registry. */
922 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
924
925 HRESULT rc = S_OK;
926
927 if (!mData->mAccessible)
928 {
929 /* try to initialize the VM once more if not accessible */
930
931 AutoReinitSpan autoReinitSpan(this);
932 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
933
934#ifdef DEBUG
935 LogFlowThisFunc(("Dumping media backreferences\n"));
936 mParent->i_dumpAllBackRefs();
937#endif
938
939 if (mData->pMachineConfigFile)
940 {
941 // reset the XML file to force loadSettings() (called from i_registeredInit())
942 // to parse it again; the file might have changed
943 delete mData->pMachineConfigFile;
944 mData->pMachineConfigFile = NULL;
945 }
946
947 rc = i_registeredInit();
948
949 if (SUCCEEDED(rc) && mData->mAccessible)
950 {
951 autoReinitSpan.setSucceeded();
952
953 /* make sure interesting parties will notice the accessibility
954 * state change */
955 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
956 mParent->i_onMachineDataChanged(mData->mUuid);
957 }
958 }
959
960 if (SUCCEEDED(rc))
961 *aAccessible = mData->mAccessible;
962
963 LogFlowThisFuncLeave();
964
965 return rc;
966}
967
968HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
969{
970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
971
972 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
973 {
974 /* return shortly */
975 aAccessError = NULL;
976 return S_OK;
977 }
978
979 HRESULT rc = S_OK;
980
981 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
982 rc = errorInfo.createObject();
983 if (SUCCEEDED(rc))
984 {
985 errorInfo->init(mData->mAccessError.getResultCode(),
986 mData->mAccessError.getInterfaceID().ref(),
987 Utf8Str(mData->mAccessError.getComponent()).c_str(),
988 Utf8Str(mData->mAccessError.getText()));
989 aAccessError = errorInfo;
990 }
991
992 return rc;
993}
994
995HRESULT Machine::getName(com::Utf8Str &aName)
996{
997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
998
999 aName = mUserData->s.strName;
1000
1001 return S_OK;
1002}
1003
1004HRESULT Machine::setName(const com::Utf8Str &aName)
1005{
1006 // prohibit setting a UUID only as the machine name, or else it can
1007 // never be found by findMachine()
1008 Guid test(aName);
1009
1010 if (test.isValid())
1011 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1012
1013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1016 if (FAILED(rc)) return rc;
1017
1018 i_setModified(IsModified_MachineData);
1019 mUserData.backup();
1020 mUserData->s.strName = aName;
1021
1022 return S_OK;
1023}
1024
1025HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1026{
1027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1028
1029 aDescription = mUserData->s.strDescription;
1030
1031 return S_OK;
1032}
1033
1034HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1035{
1036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1037
1038 // this can be done in principle in any state as it doesn't affect the VM
1039 // significantly, but play safe by not messing around while complex
1040 // activities are going on
1041 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1042 if (FAILED(rc)) return rc;
1043
1044 i_setModified(IsModified_MachineData);
1045 mUserData.backup();
1046 mUserData->s.strDescription = aDescription;
1047
1048 return S_OK;
1049}
1050
1051HRESULT Machine::getId(com::Guid &aId)
1052{
1053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1054
1055 aId = mData->mUuid;
1056
1057 return S_OK;
1058}
1059
1060HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1061{
1062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1063 aGroups.resize(mUserData->s.llGroups.size());
1064 size_t i = 0;
1065 for (StringsList::const_iterator
1066 it = mUserData->s.llGroups.begin();
1067 it != mUserData->s.llGroups.end();
1068 ++it, ++i)
1069 aGroups[i] = (*it);
1070
1071 return S_OK;
1072}
1073
1074HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1075{
1076 StringsList llGroups;
1077 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1078 if (FAILED(rc))
1079 return rc;
1080
1081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1082
1083 rc = i_checkStateDependency(MutableOrSavedStateDep);
1084 if (FAILED(rc)) return rc;
1085
1086 i_setModified(IsModified_MachineData);
1087 mUserData.backup();
1088 mUserData->s.llGroups = llGroups;
1089
1090 return S_OK;
1091}
1092
1093HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1094{
1095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1096
1097 aOSTypeId = mUserData->s.strOsType;
1098
1099 return S_OK;
1100}
1101
1102HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1103{
1104 /* look up the object by Id to check it is valid */
1105 ComObjPtr<GuestOSType> pGuestOSType;
1106 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1107
1108 /* when setting, always use the "etalon" value for consistency -- lookup
1109 * by ID is case-insensitive and the input value may have different case */
1110 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1111
1112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 HRESULT rc = i_checkStateDependency(MutableStateDep);
1115 if (FAILED(rc)) return rc;
1116
1117 i_setModified(IsModified_MachineData);
1118 mUserData.backup();
1119 mUserData->s.strOsType = osTypeId;
1120
1121 return S_OK;
1122}
1123
1124HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1125{
1126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 *aFirmwareType = mHWData->mFirmwareType;
1129
1130 return S_OK;
1131}
1132
1133HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1134{
1135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1136
1137 HRESULT rc = i_checkStateDependency(MutableStateDep);
1138 if (FAILED(rc)) return rc;
1139
1140 i_setModified(IsModified_MachineData);
1141 mHWData.backup();
1142 mHWData->mFirmwareType = aFirmwareType;
1143 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1144 alock.release();
1145
1146 mBIOSSettings->i_updateNonVolatileStorageFile(strNVRAM);
1147
1148 return S_OK;
1149}
1150
1151HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1152{
1153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1154
1155 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1156
1157 return S_OK;
1158}
1159
1160HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1161{
1162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 HRESULT rc = i_checkStateDependency(MutableStateDep);
1165 if (FAILED(rc)) return rc;
1166
1167 i_setModified(IsModified_MachineData);
1168 mHWData.backup();
1169 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1170
1171 return S_OK;
1172}
1173
1174HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1175{
1176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 *aPointingHIDType = mHWData->mPointingHIDType;
1179
1180 return S_OK;
1181}
1182
1183HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1184{
1185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 HRESULT rc = i_checkStateDependency(MutableStateDep);
1188 if (FAILED(rc)) return rc;
1189
1190 i_setModified(IsModified_MachineData);
1191 mHWData.backup();
1192 mHWData->mPointingHIDType = aPointingHIDType;
1193
1194 return S_OK;
1195}
1196
1197HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1198{
1199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1200
1201 *aChipsetType = mHWData->mChipsetType;
1202
1203 return S_OK;
1204}
1205
1206HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1207{
1208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1209
1210 HRESULT rc = i_checkStateDependency(MutableStateDep);
1211 if (FAILED(rc)) return rc;
1212
1213 if (aChipsetType != mHWData->mChipsetType)
1214 {
1215 i_setModified(IsModified_MachineData);
1216 mHWData.backup();
1217 mHWData->mChipsetType = aChipsetType;
1218
1219 // Resize network adapter array, to be finalized on commit/rollback.
1220 // We must not throw away entries yet, otherwise settings are lost
1221 // without a way to roll back.
1222 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1223 size_t oldCount = mNetworkAdapters.size();
1224 if (newCount > oldCount)
1225 {
1226 mNetworkAdapters.resize(newCount);
1227 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1228 {
1229 unconst(mNetworkAdapters[slot]).createObject();
1230 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1231 }
1232 }
1233 }
1234
1235 return S_OK;
1236}
1237
1238HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1239{
1240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1241
1242 aParavirtDebug = mHWData->mParavirtDebug;
1243 return S_OK;
1244}
1245
1246HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1247{
1248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 HRESULT rc = i_checkStateDependency(MutableStateDep);
1251 if (FAILED(rc)) return rc;
1252
1253 /** @todo Parse/validate options? */
1254 if (aParavirtDebug != mHWData->mParavirtDebug)
1255 {
1256 i_setModified(IsModified_MachineData);
1257 mHWData.backup();
1258 mHWData->mParavirtDebug = aParavirtDebug;
1259 }
1260
1261 return S_OK;
1262}
1263
1264HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1265{
1266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 *aParavirtProvider = mHWData->mParavirtProvider;
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1274{
1275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 HRESULT rc = i_checkStateDependency(MutableStateDep);
1278 if (FAILED(rc)) return rc;
1279
1280 if (aParavirtProvider != mHWData->mParavirtProvider)
1281 {
1282 i_setModified(IsModified_MachineData);
1283 mHWData.backup();
1284 mHWData->mParavirtProvider = aParavirtProvider;
1285 }
1286
1287 return S_OK;
1288}
1289
1290HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1291{
1292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1293
1294 *aParavirtProvider = mHWData->mParavirtProvider;
1295 switch (mHWData->mParavirtProvider)
1296 {
1297 case ParavirtProvider_None:
1298 case ParavirtProvider_HyperV:
1299 case ParavirtProvider_KVM:
1300 case ParavirtProvider_Minimal:
1301 break;
1302
1303 /* Resolve dynamic provider types to the effective types. */
1304 default:
1305 {
1306 ComObjPtr<GuestOSType> pGuestOSType;
1307 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1308 pGuestOSType);
1309 if (FAILED(hrc2) || pGuestOSType.isNull())
1310 {
1311 *aParavirtProvider = ParavirtProvider_None;
1312 break;
1313 }
1314
1315 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1316 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1317
1318 switch (mHWData->mParavirtProvider)
1319 {
1320 case ParavirtProvider_Legacy:
1321 {
1322 if (fOsXGuest)
1323 *aParavirtProvider = ParavirtProvider_Minimal;
1324 else
1325 *aParavirtProvider = ParavirtProvider_None;
1326 break;
1327 }
1328
1329 case ParavirtProvider_Default:
1330 {
1331 if (fOsXGuest)
1332 *aParavirtProvider = ParavirtProvider_Minimal;
1333 else if ( mUserData->s.strOsType == "Windows10"
1334 || mUserData->s.strOsType == "Windows10_64"
1335 || mUserData->s.strOsType == "Windows81"
1336 || mUserData->s.strOsType == "Windows81_64"
1337 || mUserData->s.strOsType == "Windows8"
1338 || mUserData->s.strOsType == "Windows8_64"
1339 || mUserData->s.strOsType == "Windows7"
1340 || mUserData->s.strOsType == "Windows7_64"
1341 || mUserData->s.strOsType == "WindowsVista"
1342 || mUserData->s.strOsType == "WindowsVista_64"
1343 || mUserData->s.strOsType == "Windows2012"
1344 || mUserData->s.strOsType == "Windows2012_64"
1345 || mUserData->s.strOsType == "Windows2008"
1346 || mUserData->s.strOsType == "Windows2008_64")
1347 {
1348 *aParavirtProvider = ParavirtProvider_HyperV;
1349 }
1350 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1351 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1352 || mUserData->s.strOsType == "Linux"
1353 || mUserData->s.strOsType == "Linux_64"
1354 || mUserData->s.strOsType == "ArchLinux"
1355 || mUserData->s.strOsType == "ArchLinux_64"
1356 || mUserData->s.strOsType == "Debian"
1357 || mUserData->s.strOsType == "Debian_64"
1358 || mUserData->s.strOsType == "Fedora"
1359 || mUserData->s.strOsType == "Fedora_64"
1360 || mUserData->s.strOsType == "Gentoo"
1361 || mUserData->s.strOsType == "Gentoo_64"
1362 || mUserData->s.strOsType == "Mandriva"
1363 || mUserData->s.strOsType == "Mandriva_64"
1364 || mUserData->s.strOsType == "OpenSUSE"
1365 || mUserData->s.strOsType == "OpenSUSE_64"
1366 || mUserData->s.strOsType == "Oracle"
1367 || mUserData->s.strOsType == "Oracle_64"
1368 || mUserData->s.strOsType == "RedHat"
1369 || mUserData->s.strOsType == "RedHat_64"
1370 || mUserData->s.strOsType == "Turbolinux"
1371 || mUserData->s.strOsType == "Turbolinux_64"
1372 || mUserData->s.strOsType == "Ubuntu"
1373 || mUserData->s.strOsType == "Ubuntu_64"
1374 || mUserData->s.strOsType == "Xandros"
1375 || mUserData->s.strOsType == "Xandros_64")
1376 {
1377 *aParavirtProvider = ParavirtProvider_KVM;
1378 }
1379 else
1380 *aParavirtProvider = ParavirtProvider_None;
1381 break;
1382 }
1383
1384 default: AssertFailedBreak(); /* Shut up MSC. */
1385 }
1386 break;
1387 }
1388 }
1389
1390 Assert( *aParavirtProvider == ParavirtProvider_None
1391 || *aParavirtProvider == ParavirtProvider_Minimal
1392 || *aParavirtProvider == ParavirtProvider_HyperV
1393 || *aParavirtProvider == ParavirtProvider_KVM);
1394 return S_OK;
1395}
1396
1397HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1398{
1399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1400
1401 aHardwareVersion = mHWData->mHWVersion;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1407{
1408 /* check known version */
1409 Utf8Str hwVersion = aHardwareVersion;
1410 if ( hwVersion.compare("1") != 0
1411 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1412 return setError(E_INVALIDARG,
1413 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1414
1415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1416
1417 HRESULT rc = i_checkStateDependency(MutableStateDep);
1418 if (FAILED(rc)) return rc;
1419
1420 i_setModified(IsModified_MachineData);
1421 mHWData.backup();
1422 mHWData->mHWVersion = aHardwareVersion;
1423
1424 return S_OK;
1425}
1426
1427HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1428{
1429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1430
1431 if (!mHWData->mHardwareUUID.isZero())
1432 aHardwareUUID = mHWData->mHardwareUUID;
1433 else
1434 aHardwareUUID = mData->mUuid;
1435
1436 return S_OK;
1437}
1438
1439HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1440{
1441 if (!aHardwareUUID.isValid())
1442 return E_INVALIDARG;
1443
1444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 HRESULT rc = i_checkStateDependency(MutableStateDep);
1447 if (FAILED(rc)) return rc;
1448
1449 i_setModified(IsModified_MachineData);
1450 mHWData.backup();
1451 if (aHardwareUUID == mData->mUuid)
1452 mHWData->mHardwareUUID.clear();
1453 else
1454 mHWData->mHardwareUUID = aHardwareUUID;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1460{
1461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 *aMemorySize = mHWData->mMemorySize;
1464
1465 return S_OK;
1466}
1467
1468HRESULT Machine::setMemorySize(ULONG aMemorySize)
1469{
1470 /* check RAM limits */
1471 if ( aMemorySize < MM_RAM_MIN_IN_MB
1472 || aMemorySize > MM_RAM_MAX_IN_MB
1473 )
1474 return setError(E_INVALIDARG,
1475 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1476 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1477
1478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 HRESULT rc = i_checkStateDependency(MutableStateDep);
1481 if (FAILED(rc)) return rc;
1482
1483 i_setModified(IsModified_MachineData);
1484 mHWData.backup();
1485 mHWData->mMemorySize = aMemorySize;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1491{
1492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 *aCPUCount = mHWData->mCPUCount;
1495
1496 return S_OK;
1497}
1498
1499HRESULT Machine::setCPUCount(ULONG aCPUCount)
1500{
1501 /* check CPU limits */
1502 if ( aCPUCount < SchemaDefs::MinCPUCount
1503 || aCPUCount > SchemaDefs::MaxCPUCount
1504 )
1505 return setError(E_INVALIDARG,
1506 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1507 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1508
1509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1510
1511 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1512 if (mHWData->mCPUHotPlugEnabled)
1513 {
1514 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1515 {
1516 if (mHWData->mCPUAttached[idx])
1517 return setError(E_INVALIDARG,
1518 tr("There is still a CPU attached to socket %lu."
1519 "Detach the CPU before removing the socket"),
1520 aCPUCount, idx+1);
1521 }
1522 }
1523
1524 HRESULT rc = i_checkStateDependency(MutableStateDep);
1525 if (FAILED(rc)) return rc;
1526
1527 i_setModified(IsModified_MachineData);
1528 mHWData.backup();
1529 mHWData->mCPUCount = aCPUCount;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1535{
1536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1539
1540 return S_OK;
1541}
1542
1543HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1544{
1545 HRESULT rc = S_OK;
1546
1547 /* check throttle limits */
1548 if ( aCPUExecutionCap < 1
1549 || aCPUExecutionCap > 100
1550 )
1551 return setError(E_INVALIDARG,
1552 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1553 aCPUExecutionCap, 1, 100);
1554
1555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1556
1557 alock.release();
1558 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1559 alock.acquire();
1560 if (FAILED(rc)) return rc;
1561
1562 i_setModified(IsModified_MachineData);
1563 mHWData.backup();
1564 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1565
1566 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1567 if (Global::IsOnline(mData->mMachineState))
1568 i_saveSettings(NULL);
1569
1570 return S_OK;
1571}
1572
1573HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1574{
1575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1576
1577 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1578
1579 return S_OK;
1580}
1581
1582HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1583{
1584 HRESULT rc = S_OK;
1585
1586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 rc = i_checkStateDependency(MutableStateDep);
1589 if (FAILED(rc)) return rc;
1590
1591 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1592 {
1593 if (aCPUHotPlugEnabled)
1594 {
1595 i_setModified(IsModified_MachineData);
1596 mHWData.backup();
1597
1598 /* Add the amount of CPUs currently attached */
1599 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1600 mHWData->mCPUAttached[i] = true;
1601 }
1602 else
1603 {
1604 /*
1605 * We can disable hotplug only if the amount of maximum CPUs is equal
1606 * to the amount of attached CPUs
1607 */
1608 unsigned cCpusAttached = 0;
1609 unsigned iHighestId = 0;
1610
1611 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1612 {
1613 if (mHWData->mCPUAttached[i])
1614 {
1615 cCpusAttached++;
1616 iHighestId = i;
1617 }
1618 }
1619
1620 if ( (cCpusAttached != mHWData->mCPUCount)
1621 || (iHighestId >= mHWData->mCPUCount))
1622 return setError(E_INVALIDARG,
1623 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1624
1625 i_setModified(IsModified_MachineData);
1626 mHWData.backup();
1627 }
1628 }
1629
1630 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1631
1632 return rc;
1633}
1634
1635HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1636{
1637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1638
1639 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1640
1641 return S_OK;
1642}
1643
1644HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1645{
1646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1647
1648 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1649 if (SUCCEEDED(hrc))
1650 {
1651 i_setModified(IsModified_MachineData);
1652 mHWData.backup();
1653 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1654 }
1655 return hrc;
1656}
1657
1658HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1659{
1660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1661 aCPUProfile = mHWData->mCpuProfile;
1662 return S_OK;
1663}
1664
1665HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1666{
1667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1668 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1669 if (SUCCEEDED(hrc))
1670 {
1671 i_setModified(IsModified_MachineData);
1672 mHWData.backup();
1673 /* Empty equals 'host'. */
1674 if (aCPUProfile.isNotEmpty())
1675 mHWData->mCpuProfile = aCPUProfile;
1676 else
1677 mHWData->mCpuProfile = "host";
1678 }
1679 return hrc;
1680}
1681
1682HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1683{
1684#ifdef VBOX_WITH_USB_CARDREADER
1685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1688
1689 return S_OK;
1690#else
1691 NOREF(aEmulatedUSBCardReaderEnabled);
1692 return E_NOTIMPL;
1693#endif
1694}
1695
1696HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1697{
1698#ifdef VBOX_WITH_USB_CARDREADER
1699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1702 if (FAILED(rc)) return rc;
1703
1704 i_setModified(IsModified_MachineData);
1705 mHWData.backup();
1706 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1707
1708 return S_OK;
1709#else
1710 NOREF(aEmulatedUSBCardReaderEnabled);
1711 return E_NOTIMPL;
1712#endif
1713}
1714
1715HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1716{
1717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1718
1719 *aHPETEnabled = mHWData->mHPETEnabled;
1720
1721 return S_OK;
1722}
1723
1724HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1725{
1726 HRESULT rc = S_OK;
1727
1728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1729
1730 rc = i_checkStateDependency(MutableStateDep);
1731 if (FAILED(rc)) return rc;
1732
1733 i_setModified(IsModified_MachineData);
1734 mHWData.backup();
1735
1736 mHWData->mHPETEnabled = aHPETEnabled;
1737
1738 return rc;
1739}
1740
1741/** @todo this method should not be public */
1742HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1743{
1744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1747
1748 return S_OK;
1749}
1750
1751/**
1752 * Set the memory balloon size.
1753 *
1754 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1755 * we have to make sure that we never call IGuest from here.
1756 */
1757HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1758{
1759 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1760#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1761 /* check limits */
1762 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1763 return setError(E_INVALIDARG,
1764 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1765 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1766
1767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1768
1769 i_setModified(IsModified_MachineData);
1770 mHWData.backup();
1771 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1772
1773 return S_OK;
1774#else
1775 NOREF(aMemoryBalloonSize);
1776 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1777#endif
1778}
1779
1780HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1781{
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1785 return S_OK;
1786}
1787
1788HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1789{
1790#ifdef VBOX_WITH_PAGE_SHARING
1791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1792
1793 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1794 i_setModified(IsModified_MachineData);
1795 mHWData.backup();
1796 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1797 return S_OK;
1798#else
1799 NOREF(aPageFusionEnabled);
1800 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1801#endif
1802}
1803
1804HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1805{
1806 /* mBIOSSettings is constant during life time, no need to lock */
1807 aBIOSSettings = mBIOSSettings;
1808
1809 return S_OK;
1810}
1811
1812HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1813{
1814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1815
1816 aRecordingSettings = mRecordingSettings;
1817
1818 return S_OK;
1819}
1820
1821HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1822{
1823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1824
1825 aGraphicsAdapter = mGraphicsAdapter;
1826
1827 return S_OK;
1828}
1829
1830HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1831{
1832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1833
1834 switch (aProperty)
1835 {
1836 case CPUPropertyType_PAE:
1837 *aValue = mHWData->mPAEEnabled;
1838 break;
1839
1840 case CPUPropertyType_LongMode:
1841 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1842 *aValue = TRUE;
1843 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1844 *aValue = FALSE;
1845#if HC_ARCH_BITS == 64
1846 else
1847 *aValue = TRUE;
1848#else
1849 else
1850 {
1851 *aValue = FALSE;
1852
1853 ComObjPtr<GuestOSType> pGuestOSType;
1854 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1855 pGuestOSType);
1856 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1857 {
1858 if (pGuestOSType->i_is64Bit())
1859 {
1860 ComObjPtr<Host> pHost = mParent->i_host();
1861 alock.release();
1862
1863 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1864 if (FAILED(hrc2))
1865 *aValue = FALSE;
1866 }
1867 }
1868 }
1869#endif
1870 break;
1871
1872 case CPUPropertyType_TripleFaultReset:
1873 *aValue = mHWData->mTripleFaultReset;
1874 break;
1875
1876 case CPUPropertyType_APIC:
1877 *aValue = mHWData->mAPIC;
1878 break;
1879
1880 case CPUPropertyType_X2APIC:
1881 *aValue = mHWData->mX2APIC;
1882 break;
1883
1884 case CPUPropertyType_IBPBOnVMExit:
1885 *aValue = mHWData->mIBPBOnVMExit;
1886 break;
1887
1888 case CPUPropertyType_IBPBOnVMEntry:
1889 *aValue = mHWData->mIBPBOnVMEntry;
1890 break;
1891
1892 case CPUPropertyType_SpecCtrl:
1893 *aValue = mHWData->mSpecCtrl;
1894 break;
1895
1896 case CPUPropertyType_SpecCtrlByHost:
1897 *aValue = mHWData->mSpecCtrlByHost;
1898 break;
1899
1900 case CPUPropertyType_HWVirt:
1901 *aValue = mHWData->mNestedHWVirt;
1902 break;
1903
1904 case CPUPropertyType_L1DFlushOnEMTScheduling:
1905 *aValue = mHWData->mL1DFlushOnSched;
1906 break;
1907
1908 case CPUPropertyType_L1DFlushOnVMEntry:
1909 *aValue = mHWData->mL1DFlushOnVMEntry;
1910 break;
1911
1912 case CPUPropertyType_MDSClearOnEMTScheduling:
1913 *aValue = mHWData->mMDSClearOnSched;
1914 break;
1915
1916 case CPUPropertyType_MDSClearOnVMEntry:
1917 *aValue = mHWData->mMDSClearOnVMEntry;
1918 break;
1919
1920 default:
1921 return E_INVALIDARG;
1922 }
1923 return S_OK;
1924}
1925
1926HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1927{
1928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1929
1930 HRESULT rc = i_checkStateDependency(MutableStateDep);
1931 if (FAILED(rc)) return rc;
1932
1933 switch (aProperty)
1934 {
1935 case CPUPropertyType_PAE:
1936 i_setModified(IsModified_MachineData);
1937 mHWData.backup();
1938 mHWData->mPAEEnabled = !!aValue;
1939 break;
1940
1941 case CPUPropertyType_LongMode:
1942 i_setModified(IsModified_MachineData);
1943 mHWData.backup();
1944 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1945 break;
1946
1947 case CPUPropertyType_TripleFaultReset:
1948 i_setModified(IsModified_MachineData);
1949 mHWData.backup();
1950 mHWData->mTripleFaultReset = !!aValue;
1951 break;
1952
1953 case CPUPropertyType_APIC:
1954 if (mHWData->mX2APIC)
1955 aValue = TRUE;
1956 i_setModified(IsModified_MachineData);
1957 mHWData.backup();
1958 mHWData->mAPIC = !!aValue;
1959 break;
1960
1961 case CPUPropertyType_X2APIC:
1962 i_setModified(IsModified_MachineData);
1963 mHWData.backup();
1964 mHWData->mX2APIC = !!aValue;
1965 if (aValue)
1966 mHWData->mAPIC = !!aValue;
1967 break;
1968
1969 case CPUPropertyType_IBPBOnVMExit:
1970 i_setModified(IsModified_MachineData);
1971 mHWData.backup();
1972 mHWData->mIBPBOnVMExit = !!aValue;
1973 break;
1974
1975 case CPUPropertyType_IBPBOnVMEntry:
1976 i_setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mIBPBOnVMEntry = !!aValue;
1979 break;
1980
1981 case CPUPropertyType_SpecCtrl:
1982 i_setModified(IsModified_MachineData);
1983 mHWData.backup();
1984 mHWData->mSpecCtrl = !!aValue;
1985 break;
1986
1987 case CPUPropertyType_SpecCtrlByHost:
1988 i_setModified(IsModified_MachineData);
1989 mHWData.backup();
1990 mHWData->mSpecCtrlByHost = !!aValue;
1991 break;
1992
1993 case CPUPropertyType_HWVirt:
1994 i_setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mNestedHWVirt = !!aValue;
1997 break;
1998
1999 case CPUPropertyType_L1DFlushOnEMTScheduling:
2000 i_setModified(IsModified_MachineData);
2001 mHWData.backup();
2002 mHWData->mL1DFlushOnSched = !!aValue;
2003 break;
2004
2005 case CPUPropertyType_L1DFlushOnVMEntry:
2006 i_setModified(IsModified_MachineData);
2007 mHWData.backup();
2008 mHWData->mL1DFlushOnVMEntry = !!aValue;
2009 break;
2010
2011 case CPUPropertyType_MDSClearOnEMTScheduling:
2012 i_setModified(IsModified_MachineData);
2013 mHWData.backup();
2014 mHWData->mMDSClearOnSched = !!aValue;
2015 break;
2016
2017 case CPUPropertyType_MDSClearOnVMEntry:
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020 mHWData->mMDSClearOnVMEntry = !!aValue;
2021 break;
2022
2023 default:
2024 return E_INVALIDARG;
2025 }
2026 return S_OK;
2027}
2028
2029HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2030 ULONG *aValEcx, ULONG *aValEdx)
2031{
2032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2033 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2034 {
2035 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2036 it != mHWData->mCpuIdLeafList.end();
2037 ++it)
2038 {
2039 if (aOrdinal == 0)
2040 {
2041 const settings::CpuIdLeaf &rLeaf= *it;
2042 *aIdx = rLeaf.idx;
2043 *aSubIdx = rLeaf.idxSub;
2044 *aValEax = rLeaf.uEax;
2045 *aValEbx = rLeaf.uEbx;
2046 *aValEcx = rLeaf.uEcx;
2047 *aValEdx = rLeaf.uEdx;
2048 return S_OK;
2049 }
2050 aOrdinal--;
2051 }
2052 }
2053 return E_INVALIDARG;
2054}
2055
2056HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2057{
2058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2059
2060 /*
2061 * Search the list.
2062 */
2063 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2064 {
2065 const settings::CpuIdLeaf &rLeaf= *it;
2066 if ( rLeaf.idx == aIdx
2067 && ( aSubIdx == UINT32_MAX
2068 || rLeaf.idxSub == aSubIdx) )
2069 {
2070 *aValEax = rLeaf.uEax;
2071 *aValEbx = rLeaf.uEbx;
2072 *aValEcx = rLeaf.uEcx;
2073 *aValEdx = rLeaf.uEdx;
2074 return S_OK;
2075 }
2076 }
2077
2078 return E_INVALIDARG;
2079}
2080
2081
2082HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2083{
2084 /*
2085 * Validate input before taking locks and checking state.
2086 */
2087 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2088 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2089 if ( aIdx >= UINT32_C(0x20)
2090 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2091 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2092 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2093
2094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2095 HRESULT rc = i_checkStateDependency(MutableStateDep);
2096 if (FAILED(rc)) return rc;
2097
2098 /*
2099 * Impose a maximum number of leaves.
2100 */
2101 if (mHWData->mCpuIdLeafList.size() > 256)
2102 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2103
2104 /*
2105 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2106 */
2107 i_setModified(IsModified_MachineData);
2108 mHWData.backup();
2109
2110 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2111 {
2112 settings::CpuIdLeaf &rLeaf= *it;
2113 if ( rLeaf.idx == aIdx
2114 && ( aSubIdx == UINT32_MAX
2115 || rLeaf.idxSub == aSubIdx) )
2116 it = mHWData->mCpuIdLeafList.erase(it);
2117 else
2118 ++it;
2119 }
2120
2121 settings::CpuIdLeaf NewLeaf;
2122 NewLeaf.idx = aIdx;
2123 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2124 NewLeaf.uEax = aValEax;
2125 NewLeaf.uEbx = aValEbx;
2126 NewLeaf.uEcx = aValEcx;
2127 NewLeaf.uEdx = aValEdx;
2128 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2129 return S_OK;
2130}
2131
2132HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2133{
2134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 HRESULT rc = i_checkStateDependency(MutableStateDep);
2137 if (FAILED(rc)) return rc;
2138
2139 /*
2140 * Do the removal.
2141 */
2142 bool fModified = mHWData.isBackedUp();
2143 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2144 {
2145 settings::CpuIdLeaf &rLeaf= *it;
2146 if ( rLeaf.idx == aIdx
2147 && ( aSubIdx == UINT32_MAX
2148 || rLeaf.idxSub == aSubIdx) )
2149 {
2150 if (!fModified)
2151 {
2152 fModified = true;
2153 i_setModified(IsModified_MachineData);
2154 mHWData.backup();
2155 // Start from the beginning, since mHWData.backup() creates
2156 // a new list, causing iterator mixup. This makes sure that
2157 // the settings are not unnecessarily marked as modified,
2158 // at the price of extra list walking.
2159 it = mHWData->mCpuIdLeafList.begin();
2160 }
2161 else
2162 it = mHWData->mCpuIdLeafList.erase(it);
2163 }
2164 else
2165 ++it;
2166 }
2167
2168 return S_OK;
2169}
2170
2171HRESULT Machine::removeAllCPUIDLeaves()
2172{
2173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2174
2175 HRESULT rc = i_checkStateDependency(MutableStateDep);
2176 if (FAILED(rc)) return rc;
2177
2178 if (mHWData->mCpuIdLeafList.size() > 0)
2179 {
2180 i_setModified(IsModified_MachineData);
2181 mHWData.backup();
2182
2183 mHWData->mCpuIdLeafList.clear();
2184 }
2185
2186 return S_OK;
2187}
2188HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2189{
2190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2191
2192 switch(aProperty)
2193 {
2194 case HWVirtExPropertyType_Enabled:
2195 *aValue = mHWData->mHWVirtExEnabled;
2196 break;
2197
2198 case HWVirtExPropertyType_VPID:
2199 *aValue = mHWData->mHWVirtExVPIDEnabled;
2200 break;
2201
2202 case HWVirtExPropertyType_NestedPaging:
2203 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2204 break;
2205
2206 case HWVirtExPropertyType_UnrestrictedExecution:
2207 *aValue = mHWData->mHWVirtExUXEnabled;
2208 break;
2209
2210 case HWVirtExPropertyType_LargePages:
2211 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2212#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2213 *aValue = FALSE;
2214#endif
2215 break;
2216
2217 case HWVirtExPropertyType_Force:
2218 *aValue = mHWData->mHWVirtExForceEnabled;
2219 break;
2220
2221 case HWVirtExPropertyType_UseNativeApi:
2222 *aValue = mHWData->mHWVirtExUseNativeApi;
2223 break;
2224
2225 case HWVirtExPropertyType_VirtVmsaveVmload:
2226 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2227 break;
2228
2229 default:
2230 return E_INVALIDARG;
2231 }
2232 return S_OK;
2233}
2234
2235HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2236{
2237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2238
2239 HRESULT rc = i_checkStateDependency(MutableStateDep);
2240 if (FAILED(rc)) return rc;
2241
2242 switch (aProperty)
2243 {
2244 case HWVirtExPropertyType_Enabled:
2245 i_setModified(IsModified_MachineData);
2246 mHWData.backup();
2247 mHWData->mHWVirtExEnabled = !!aValue;
2248 break;
2249
2250 case HWVirtExPropertyType_VPID:
2251 i_setModified(IsModified_MachineData);
2252 mHWData.backup();
2253 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2254 break;
2255
2256 case HWVirtExPropertyType_NestedPaging:
2257 i_setModified(IsModified_MachineData);
2258 mHWData.backup();
2259 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2260 break;
2261
2262 case HWVirtExPropertyType_UnrestrictedExecution:
2263 i_setModified(IsModified_MachineData);
2264 mHWData.backup();
2265 mHWData->mHWVirtExUXEnabled = !!aValue;
2266 break;
2267
2268 case HWVirtExPropertyType_LargePages:
2269 i_setModified(IsModified_MachineData);
2270 mHWData.backup();
2271 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2272 break;
2273
2274 case HWVirtExPropertyType_Force:
2275 i_setModified(IsModified_MachineData);
2276 mHWData.backup();
2277 mHWData->mHWVirtExForceEnabled = !!aValue;
2278 break;
2279
2280 case HWVirtExPropertyType_UseNativeApi:
2281 i_setModified(IsModified_MachineData);
2282 mHWData.backup();
2283 mHWData->mHWVirtExUseNativeApi = !!aValue;
2284 break;
2285
2286 case HWVirtExPropertyType_VirtVmsaveVmload:
2287 i_setModified(IsModified_MachineData);
2288 mHWData.backup();
2289 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2290 break;
2291
2292 default:
2293 return E_INVALIDARG;
2294 }
2295
2296 return S_OK;
2297}
2298
2299HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2300{
2301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2302
2303 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2304
2305 return S_OK;
2306}
2307
2308HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2309{
2310 /** @todo (r=dmik):
2311 * 1. Allow to change the name of the snapshot folder containing snapshots
2312 * 2. Rename the folder on disk instead of just changing the property
2313 * value (to be smart and not to leave garbage). Note that it cannot be
2314 * done here because the change may be rolled back. Thus, the right
2315 * place is #saveSettings().
2316 */
2317
2318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2319
2320 HRESULT rc = i_checkStateDependency(MutableStateDep);
2321 if (FAILED(rc)) return rc;
2322
2323 if (!mData->mCurrentSnapshot.isNull())
2324 return setError(E_FAIL,
2325 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2326
2327 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2328
2329 if (strSnapshotFolder.isEmpty())
2330 strSnapshotFolder = "Snapshots";
2331 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2332 if (RT_FAILURE(vrc))
2333 return setErrorBoth(E_FAIL, vrc,
2334 tr("Invalid snapshot folder '%s' (%Rrc)"),
2335 strSnapshotFolder.c_str(), vrc);
2336
2337 i_setModified(IsModified_MachineData);
2338 mUserData.backup();
2339
2340 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2341
2342 return S_OK;
2343}
2344
2345HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2346{
2347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2348
2349 aMediumAttachments.resize(mMediumAttachments->size());
2350 size_t i = 0;
2351 for (MediumAttachmentList::const_iterator
2352 it = mMediumAttachments->begin();
2353 it != mMediumAttachments->end();
2354 ++it, ++i)
2355 aMediumAttachments[i] = *it;
2356
2357 return S_OK;
2358}
2359
2360HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2361{
2362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 Assert(!!mVRDEServer);
2365
2366 aVRDEServer = mVRDEServer;
2367
2368 return S_OK;
2369}
2370
2371HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2372{
2373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2374
2375 aAudioAdapter = mAudioAdapter;
2376
2377 return S_OK;
2378}
2379
2380HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2381{
2382#ifdef VBOX_WITH_VUSB
2383 clearError();
2384 MultiResult rc(S_OK);
2385
2386# ifdef VBOX_WITH_USB
2387 rc = mParent->i_host()->i_checkUSBProxyService();
2388 if (FAILED(rc)) return rc;
2389# endif
2390
2391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2392
2393 aUSBControllers.resize(mUSBControllers->size());
2394 size_t i = 0;
2395 for (USBControllerList::const_iterator
2396 it = mUSBControllers->begin();
2397 it != mUSBControllers->end();
2398 ++it, ++i)
2399 aUSBControllers[i] = *it;
2400
2401 return S_OK;
2402#else
2403 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2404 * extended error info to indicate that USB is simply not available
2405 * (w/o treating it as a failure), for example, as in OSE */
2406 NOREF(aUSBControllers);
2407 ReturnComNotImplemented();
2408#endif /* VBOX_WITH_VUSB */
2409}
2410
2411HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2412{
2413#ifdef VBOX_WITH_VUSB
2414 clearError();
2415 MultiResult rc(S_OK);
2416
2417# ifdef VBOX_WITH_USB
2418 rc = mParent->i_host()->i_checkUSBProxyService();
2419 if (FAILED(rc)) return rc;
2420# endif
2421
2422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2423
2424 aUSBDeviceFilters = mUSBDeviceFilters;
2425 return rc;
2426#else
2427 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2428 * extended error info to indicate that USB is simply not available
2429 * (w/o treating it as a failure), for example, as in OSE */
2430 NOREF(aUSBDeviceFilters);
2431 ReturnComNotImplemented();
2432#endif /* VBOX_WITH_VUSB */
2433}
2434
2435HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2436{
2437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2438
2439 aSettingsFilePath = mData->m_strConfigFileFull;
2440
2441 return S_OK;
2442}
2443
2444HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2445{
2446 RT_NOREF(aSettingsFilePath);
2447 ReturnComNotImplemented();
2448}
2449
2450HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2451{
2452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2455 if (FAILED(rc)) return rc;
2456
2457 if (!mData->pMachineConfigFile->fileExists())
2458 // this is a new machine, and no config file exists yet:
2459 *aSettingsModified = TRUE;
2460 else
2461 *aSettingsModified = (mData->flModifications != 0);
2462
2463 return S_OK;
2464}
2465
2466HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2467{
2468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2469
2470 *aSessionState = mData->mSession.mState;
2471
2472 return S_OK;
2473}
2474
2475HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2476{
2477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 aSessionName = mData->mSession.mName;
2480
2481 return S_OK;
2482}
2483
2484HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2485{
2486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2487
2488 *aSessionPID = mData->mSession.mPID;
2489
2490 return S_OK;
2491}
2492
2493HRESULT Machine::getState(MachineState_T *aState)
2494{
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 *aState = mData->mMachineState;
2498 Assert(mData->mMachineState != MachineState_Null);
2499
2500 return S_OK;
2501}
2502
2503HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2504{
2505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2506
2507 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2508
2509 return S_OK;
2510}
2511
2512HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2513{
2514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2515
2516 aStateFilePath = mSSData->strStateFilePath;
2517
2518 return S_OK;
2519}
2520
2521HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2522{
2523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 i_getLogFolder(aLogFolder);
2526
2527 return S_OK;
2528}
2529
2530HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2531{
2532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 aCurrentSnapshot = mData->mCurrentSnapshot;
2535
2536 return S_OK;
2537}
2538
2539HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2540{
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2544 ? 0
2545 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2546
2547 return S_OK;
2548}
2549
2550HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2551{
2552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2553
2554 /* Note: for machines with no snapshots, we always return FALSE
2555 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2556 * reasons :) */
2557
2558 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2559 ? FALSE
2560 : mData->mCurrentStateModified;
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 aSharedFolders.resize(mHWData->mSharedFolders.size());
2570 size_t i = 0;
2571 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2572 it = mHWData->mSharedFolders.begin();
2573 it != mHWData->mSharedFolders.end();
2574 ++it, ++i)
2575 aSharedFolders[i] = *it;
2576
2577 return S_OK;
2578}
2579
2580HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2581{
2582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2583
2584 *aClipboardMode = mHWData->mClipboardMode;
2585
2586 return S_OK;
2587}
2588
2589HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2590{
2591 HRESULT rc = S_OK;
2592
2593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2594
2595 alock.release();
2596 rc = i_onClipboardModeChange(aClipboardMode);
2597 alock.acquire();
2598 if (FAILED(rc)) return rc;
2599
2600 i_setModified(IsModified_MachineData);
2601 mHWData.backup();
2602 mHWData->mClipboardMode = aClipboardMode;
2603
2604 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2605 if (Global::IsOnline(mData->mMachineState))
2606 i_saveSettings(NULL);
2607
2608 return S_OK;
2609}
2610
2611HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2612{
2613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2616
2617 return S_OK;
2618}
2619
2620HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2621{
2622 HRESULT rc = S_OK;
2623
2624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2625
2626 alock.release();
2627 rc = i_onClipboardFileTransferModeChange(aEnabled);
2628 alock.acquire();
2629 if (FAILED(rc)) return rc;
2630
2631 i_setModified(IsModified_MachineData);
2632 mHWData.backup();
2633 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2634
2635 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2636 if (Global::IsOnline(mData->mMachineState))
2637 i_saveSettings(NULL);
2638
2639 return S_OK;
2640}
2641
2642HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2643{
2644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 *aDnDMode = mHWData->mDnDMode;
2647
2648 return S_OK;
2649}
2650
2651HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2652{
2653 HRESULT rc = S_OK;
2654
2655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2656
2657 alock.release();
2658 rc = i_onDnDModeChange(aDnDMode);
2659
2660 alock.acquire();
2661 if (FAILED(rc)) return rc;
2662
2663 i_setModified(IsModified_MachineData);
2664 mHWData.backup();
2665 mHWData->mDnDMode = aDnDMode;
2666
2667 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2668 if (Global::IsOnline(mData->mMachineState))
2669 i_saveSettings(NULL);
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2675{
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 aStorageControllers.resize(mStorageControllers->size());
2679 size_t i = 0;
2680 for (StorageControllerList::const_iterator
2681 it = mStorageControllers->begin();
2682 it != mStorageControllers->end();
2683 ++it, ++i)
2684 aStorageControllers[i] = *it;
2685
2686 return S_OK;
2687}
2688
2689HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2690{
2691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2692
2693 *aEnabled = mUserData->s.fTeleporterEnabled;
2694
2695 return S_OK;
2696}
2697
2698HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2699{
2700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2701
2702 /* Only allow it to be set to true when PoweredOff or Aborted.
2703 (Clearing it is always permitted.) */
2704 if ( aTeleporterEnabled
2705 && mData->mRegistered
2706 && ( !i_isSessionMachine()
2707 || ( mData->mMachineState != MachineState_PoweredOff
2708 && mData->mMachineState != MachineState_Teleported
2709 && mData->mMachineState != MachineState_Aborted
2710 )
2711 )
2712 )
2713 return setError(VBOX_E_INVALID_VM_STATE,
2714 tr("The machine is not powered off (state is %s)"),
2715 Global::stringifyMachineState(mData->mMachineState));
2716
2717 i_setModified(IsModified_MachineData);
2718 mUserData.backup();
2719 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2720
2721 return S_OK;
2722}
2723
2724HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2725{
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2729
2730 return S_OK;
2731}
2732
2733HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2734{
2735 if (aTeleporterPort >= _64K)
2736 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2737
2738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2741 if (FAILED(rc)) return rc;
2742
2743 i_setModified(IsModified_MachineData);
2744 mUserData.backup();
2745 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2751{
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2760{
2761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2764 if (FAILED(rc)) return rc;
2765
2766 i_setModified(IsModified_MachineData);
2767 mUserData.backup();
2768 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2769
2770 return S_OK;
2771}
2772
2773HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2774{
2775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2776 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2777
2778 return S_OK;
2779}
2780
2781HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2782{
2783 /*
2784 * Hash the password first.
2785 */
2786 com::Utf8Str aT = aTeleporterPassword;
2787
2788 if (!aT.isEmpty())
2789 {
2790 if (VBoxIsPasswordHashed(&aT))
2791 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2792 VBoxHashPassword(&aT);
2793 }
2794
2795 /*
2796 * Do the update.
2797 */
2798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2799 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2800 if (SUCCEEDED(hrc))
2801 {
2802 i_setModified(IsModified_MachineData);
2803 mUserData.backup();
2804 mUserData->s.strTeleporterPassword = aT;
2805 }
2806
2807 return hrc;
2808}
2809
2810HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2811{
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2815
2816 return S_OK;
2817}
2818
2819HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2820{
2821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2822
2823 /* Only allow it to be set to true when PoweredOff or Aborted.
2824 (Clearing it is always permitted.) */
2825 if ( aRTCUseUTC
2826 && mData->mRegistered
2827 && ( !i_isSessionMachine()
2828 || ( mData->mMachineState != MachineState_PoweredOff
2829 && mData->mMachineState != MachineState_Teleported
2830 && mData->mMachineState != MachineState_Aborted
2831 )
2832 )
2833 )
2834 return setError(VBOX_E_INVALID_VM_STATE,
2835 tr("The machine is not powered off (state is %s)"),
2836 Global::stringifyMachineState(mData->mMachineState));
2837
2838 i_setModified(IsModified_MachineData);
2839 mUserData.backup();
2840 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2841
2842 return S_OK;
2843}
2844
2845HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2846{
2847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2855{
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 HRESULT rc = i_checkStateDependency(MutableStateDep);
2859 if (FAILED(rc)) return rc;
2860
2861 i_setModified(IsModified_MachineData);
2862 mHWData.backup();
2863 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2864
2865 return S_OK;
2866}
2867
2868HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2869{
2870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2871
2872 *aIOCacheSize = mHWData->mIOCacheSize;
2873
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 HRESULT rc = i_checkStateDependency(MutableStateDep);
2882 if (FAILED(rc)) return rc;
2883
2884 i_setModified(IsModified_MachineData);
2885 mHWData.backup();
2886 mHWData->mIOCacheSize = aIOCacheSize;
2887
2888 return S_OK;
2889}
2890
2891
2892/**
2893 * @note Locks objects!
2894 */
2895HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2896 LockType_T aLockType)
2897{
2898 /* check the session state */
2899 SessionState_T state;
2900 HRESULT rc = aSession->COMGETTER(State)(&state);
2901 if (FAILED(rc)) return rc;
2902
2903 if (state != SessionState_Unlocked)
2904 return setError(VBOX_E_INVALID_OBJECT_STATE,
2905 tr("The given session is busy"));
2906
2907 // get the client's IInternalSessionControl interface
2908 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2909 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2910 E_INVALIDARG);
2911
2912 // session name (only used in some code paths)
2913 Utf8Str strSessionName;
2914
2915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 if (!mData->mRegistered)
2918 return setError(E_UNEXPECTED,
2919 tr("The machine '%s' is not registered"),
2920 mUserData->s.strName.c_str());
2921
2922 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2923
2924 SessionState_T oldState = mData->mSession.mState;
2925 /* Hack: in case the session is closing and there is a progress object
2926 * which allows waiting for the session to be closed, take the opportunity
2927 * and do a limited wait (max. 1 second). This helps a lot when the system
2928 * is busy and thus session closing can take a little while. */
2929 if ( mData->mSession.mState == SessionState_Unlocking
2930 && mData->mSession.mProgress)
2931 {
2932 alock.release();
2933 mData->mSession.mProgress->WaitForCompletion(1000);
2934 alock.acquire();
2935 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2936 }
2937
2938 // try again now
2939 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2940 // (i.e. session machine exists)
2941 && (aLockType == LockType_Shared) // caller wants a shared link to the
2942 // existing session that holds the write lock:
2943 )
2944 {
2945 // OK, share the session... we are now dealing with three processes:
2946 // 1) VBoxSVC (where this code runs);
2947 // 2) process C: the caller's client process (who wants a shared session);
2948 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2949
2950 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2951 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2952 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2953 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2954 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2955
2956 /*
2957 * Release the lock before calling the client process. It's safe here
2958 * since the only thing to do after we get the lock again is to add
2959 * the remote control to the list (which doesn't directly influence
2960 * anything).
2961 */
2962 alock.release();
2963
2964 // get the console of the session holding the write lock (this is a remote call)
2965 ComPtr<IConsole> pConsoleW;
2966 if (mData->mSession.mLockType == LockType_VM)
2967 {
2968 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2969 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2970 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2971 if (FAILED(rc))
2972 // the failure may occur w/o any error info (from RPC), so provide one
2973 return setError(VBOX_E_VM_ERROR,
2974 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
2975 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2976 }
2977
2978 // share the session machine and W's console with the caller's session
2979 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2980 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2981 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
2982
2983 if (FAILED(rc))
2984 // the failure may occur w/o any error info (from RPC), so provide one
2985 return setError(VBOX_E_VM_ERROR,
2986 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
2987 alock.acquire();
2988
2989 // need to revalidate the state after acquiring the lock again
2990 if (mData->mSession.mState != SessionState_Locked)
2991 {
2992 pSessionControl->Uninitialize();
2993 return setError(VBOX_E_INVALID_SESSION_STATE,
2994 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2995 mUserData->s.strName.c_str());
2996 }
2997
2998 // add the caller's session to the list
2999 mData->mSession.mRemoteControls.push_back(pSessionControl);
3000 }
3001 else if ( mData->mSession.mState == SessionState_Locked
3002 || mData->mSession.mState == SessionState_Unlocking
3003 )
3004 {
3005 // sharing not permitted, or machine still unlocking:
3006 return setError(VBOX_E_INVALID_OBJECT_STATE,
3007 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3008 mUserData->s.strName.c_str());
3009 }
3010 else
3011 {
3012 // machine is not locked: then write-lock the machine (create the session machine)
3013
3014 // must not be busy
3015 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3016
3017 // get the caller's session PID
3018 RTPROCESS pid = NIL_RTPROCESS;
3019 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3020 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3021 Assert(pid != NIL_RTPROCESS);
3022
3023 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3024
3025 if (fLaunchingVMProcess)
3026 {
3027 if (mData->mSession.mPID == NIL_RTPROCESS)
3028 {
3029 // two or more clients racing for a lock, the one which set the
3030 // session state to Spawning will win, the others will get an
3031 // error as we can't decide here if waiting a little would help
3032 // (only for shared locks this would avoid an error)
3033 return setError(VBOX_E_INVALID_OBJECT_STATE,
3034 tr("The machine '%s' already has a lock request pending"),
3035 mUserData->s.strName.c_str());
3036 }
3037
3038 // this machine is awaiting for a spawning session to be opened:
3039 // then the calling process must be the one that got started by
3040 // LaunchVMProcess()
3041
3042 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3043 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3044
3045#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3046 /* Hardened windows builds spawns three processes when a VM is
3047 launched, the 3rd one is the one that will end up here. */
3048 RTPROCESS pidParent;
3049 int vrc = RTProcQueryParent(pid, &pidParent);
3050 if (RT_SUCCESS(vrc))
3051 vrc = RTProcQueryParent(pidParent, &pidParent);
3052 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3053 || vrc == VERR_ACCESS_DENIED)
3054 {
3055 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3056 mData->mSession.mPID = pid;
3057 }
3058#endif
3059
3060 if (mData->mSession.mPID != pid)
3061 return setError(E_ACCESSDENIED,
3062 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3063 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3064 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3065 }
3066
3067 // create the mutable SessionMachine from the current machine
3068 ComObjPtr<SessionMachine> sessionMachine;
3069 sessionMachine.createObject();
3070 rc = sessionMachine->init(this);
3071 AssertComRC(rc);
3072
3073 /* NOTE: doing return from this function after this point but
3074 * before the end is forbidden since it may call SessionMachine::uninit()
3075 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3076 * lock while still holding the Machine lock in alock so that a deadlock
3077 * is possible due to the wrong lock order. */
3078
3079 if (SUCCEEDED(rc))
3080 {
3081 /*
3082 * Set the session state to Spawning to protect against subsequent
3083 * attempts to open a session and to unregister the machine after
3084 * we release the lock.
3085 */
3086 SessionState_T origState = mData->mSession.mState;
3087 mData->mSession.mState = SessionState_Spawning;
3088
3089#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3090 /* Get the client token ID to be passed to the client process */
3091 Utf8Str strTokenId;
3092 sessionMachine->i_getTokenId(strTokenId);
3093 Assert(!strTokenId.isEmpty());
3094#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3095 /* Get the client token to be passed to the client process */
3096 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3097 /* The token is now "owned" by pToken, fix refcount */
3098 if (!pToken.isNull())
3099 pToken->Release();
3100#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3101
3102 /*
3103 * Release the lock before calling the client process -- it will call
3104 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3105 * because the state is Spawning, so that LaunchVMProcess() and
3106 * LockMachine() calls will fail. This method, called before we
3107 * acquire the lock again, will fail because of the wrong PID.
3108 *
3109 * Note that mData->mSession.mRemoteControls accessed outside
3110 * the lock may not be modified when state is Spawning, so it's safe.
3111 */
3112 alock.release();
3113
3114 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3115#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3116 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3117#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3118 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3119 /* Now the token is owned by the client process. */
3120 pToken.setNull();
3121#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3122 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3123
3124 /* The failure may occur w/o any error info (from RPC), so provide one */
3125 if (FAILED(rc))
3126 setError(VBOX_E_VM_ERROR,
3127 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3128
3129 // get session name, either to remember or to compare against
3130 // the already known session name.
3131 {
3132 Bstr bstrSessionName;
3133 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3134 if (SUCCEEDED(rc2))
3135 strSessionName = bstrSessionName;
3136 }
3137
3138 if ( SUCCEEDED(rc)
3139 && fLaunchingVMProcess
3140 )
3141 {
3142 /* complete the remote session initialization */
3143
3144 /* get the console from the direct session */
3145 ComPtr<IConsole> console;
3146 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3147 ComAssertComRC(rc);
3148
3149 if (SUCCEEDED(rc) && !console)
3150 {
3151 ComAssert(!!console);
3152 rc = E_FAIL;
3153 }
3154
3155 /* assign machine & console to the remote session */
3156 if (SUCCEEDED(rc))
3157 {
3158 /*
3159 * after LaunchVMProcess(), the first and the only
3160 * entry in remoteControls is that remote session
3161 */
3162 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3163 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3164 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3165
3166 /* The failure may occur w/o any error info (from RPC), so provide one */
3167 if (FAILED(rc))
3168 setError(VBOX_E_VM_ERROR,
3169 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3170 }
3171
3172 if (FAILED(rc))
3173 pSessionControl->Uninitialize();
3174 }
3175
3176 /* acquire the lock again */
3177 alock.acquire();
3178
3179 /* Restore the session state */
3180 mData->mSession.mState = origState;
3181 }
3182
3183 // finalize spawning anyway (this is why we don't return on errors above)
3184 if (fLaunchingVMProcess)
3185 {
3186 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3187 /* Note that the progress object is finalized later */
3188 /** @todo Consider checking mData->mSession.mProgress for cancellation
3189 * around here. */
3190
3191 /* We don't reset mSession.mPID here because it is necessary for
3192 * SessionMachine::uninit() to reap the child process later. */
3193
3194 if (FAILED(rc))
3195 {
3196 /* Close the remote session, remove the remote control from the list
3197 * and reset session state to Closed (@note keep the code in sync
3198 * with the relevant part in checkForSpawnFailure()). */
3199
3200 Assert(mData->mSession.mRemoteControls.size() == 1);
3201 if (mData->mSession.mRemoteControls.size() == 1)
3202 {
3203 ErrorInfoKeeper eik;
3204 mData->mSession.mRemoteControls.front()->Uninitialize();
3205 }
3206
3207 mData->mSession.mRemoteControls.clear();
3208 mData->mSession.mState = SessionState_Unlocked;
3209 }
3210 }
3211 else
3212 {
3213 /* memorize PID of the directly opened session */
3214 if (SUCCEEDED(rc))
3215 mData->mSession.mPID = pid;
3216 }
3217
3218 if (SUCCEEDED(rc))
3219 {
3220 mData->mSession.mLockType = aLockType;
3221 /* memorize the direct session control and cache IUnknown for it */
3222 mData->mSession.mDirectControl = pSessionControl;
3223 mData->mSession.mState = SessionState_Locked;
3224 if (!fLaunchingVMProcess)
3225 mData->mSession.mName = strSessionName;
3226 /* associate the SessionMachine with this Machine */
3227 mData->mSession.mMachine = sessionMachine;
3228
3229 /* request an IUnknown pointer early from the remote party for later
3230 * identity checks (it will be internally cached within mDirectControl
3231 * at least on XPCOM) */
3232 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3233 NOREF(unk);
3234 }
3235
3236 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3237 * would break the lock order */
3238 alock.release();
3239
3240 /* uninitialize the created session machine on failure */
3241 if (FAILED(rc))
3242 sessionMachine->uninit();
3243 }
3244
3245 if (SUCCEEDED(rc))
3246 {
3247 /*
3248 * tell the client watcher thread to update the set of
3249 * machines that have open sessions
3250 */
3251 mParent->i_updateClientWatcher();
3252
3253 if (oldState != SessionState_Locked)
3254 /* fire an event */
3255 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3256 }
3257
3258 return rc;
3259}
3260
3261/**
3262 * @note Locks objects!
3263 */
3264HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3265 const com::Utf8Str &aName,
3266 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3267 ComPtr<IProgress> &aProgress)
3268{
3269 Utf8Str strFrontend(aName);
3270 /* "emergencystop" doesn't need the session, so skip the checks/interface
3271 * retrieval. This code doesn't quite fit in here, but introducing a
3272 * special API method would be even more effort, and would require explicit
3273 * support by every API client. It's better to hide the feature a bit. */
3274 if (strFrontend != "emergencystop")
3275 CheckComArgNotNull(aSession);
3276
3277 HRESULT rc = S_OK;
3278 if (strFrontend.isEmpty())
3279 {
3280 Bstr bstrFrontend;
3281 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3282 if (FAILED(rc))
3283 return rc;
3284 strFrontend = bstrFrontend;
3285 if (strFrontend.isEmpty())
3286 {
3287 ComPtr<ISystemProperties> systemProperties;
3288 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3289 if (FAILED(rc))
3290 return rc;
3291 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3292 if (FAILED(rc))
3293 return rc;
3294 strFrontend = bstrFrontend;
3295 }
3296 /* paranoia - emergencystop is not a valid default */
3297 if (strFrontend == "emergencystop")
3298 strFrontend = Utf8Str::Empty;
3299 }
3300 /* default frontend: Qt GUI */
3301 if (strFrontend.isEmpty())
3302 strFrontend = "GUI/Qt";
3303
3304 if (strFrontend != "emergencystop")
3305 {
3306 /* check the session state */
3307 SessionState_T state;
3308 rc = aSession->COMGETTER(State)(&state);
3309 if (FAILED(rc))
3310 return rc;
3311
3312 if (state != SessionState_Unlocked)
3313 return setError(VBOX_E_INVALID_OBJECT_STATE,
3314 tr("The given session is busy"));
3315
3316 /* get the IInternalSessionControl interface */
3317 ComPtr<IInternalSessionControl> control(aSession);
3318 ComAssertMsgRet(!control.isNull(),
3319 ("No IInternalSessionControl interface"),
3320 E_INVALIDARG);
3321
3322 /* get the teleporter enable state for the progress object init. */
3323 BOOL fTeleporterEnabled;
3324 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3325 if (FAILED(rc))
3326 return rc;
3327
3328 /* create a progress object */
3329 ComObjPtr<ProgressProxy> progress;
3330 progress.createObject();
3331 rc = progress->init(mParent,
3332 static_cast<IMachine*>(this),
3333 Bstr(tr("Starting VM")).raw(),
3334 TRUE /* aCancelable */,
3335 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3336 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3337 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3338 2 /* uFirstOperationWeight */,
3339 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3340
3341 if (SUCCEEDED(rc))
3342 {
3343 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3344 if (SUCCEEDED(rc))
3345 {
3346 aProgress = progress;
3347
3348 /* signal the client watcher thread */
3349 mParent->i_updateClientWatcher();
3350
3351 /* fire an event */
3352 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3353 }
3354 }
3355 }
3356 else
3357 {
3358 /* no progress object - either instant success or failure */
3359 aProgress = NULL;
3360
3361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3362
3363 if (mData->mSession.mState != SessionState_Locked)
3364 return setError(VBOX_E_INVALID_OBJECT_STATE,
3365 tr("The machine '%s' is not locked by a session"),
3366 mUserData->s.strName.c_str());
3367
3368 /* must have a VM process associated - do not kill normal API clients
3369 * with an open session */
3370 if (!Global::IsOnline(mData->mMachineState))
3371 return setError(VBOX_E_INVALID_OBJECT_STATE,
3372 tr("The machine '%s' does not have a VM process"),
3373 mUserData->s.strName.c_str());
3374
3375 /* forcibly terminate the VM process */
3376 if (mData->mSession.mPID != NIL_RTPROCESS)
3377 RTProcTerminate(mData->mSession.mPID);
3378
3379 /* signal the client watcher thread, as most likely the client has
3380 * been terminated */
3381 mParent->i_updateClientWatcher();
3382 }
3383
3384 return rc;
3385}
3386
3387HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3388{
3389 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3390 return setError(E_INVALIDARG,
3391 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3392 aPosition, SchemaDefs::MaxBootPosition);
3393
3394 if (aDevice == DeviceType_USB)
3395 return setError(E_NOTIMPL,
3396 tr("Booting from USB device is currently not supported"));
3397
3398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3399
3400 HRESULT rc = i_checkStateDependency(MutableStateDep);
3401 if (FAILED(rc)) return rc;
3402
3403 i_setModified(IsModified_MachineData);
3404 mHWData.backup();
3405 mHWData->mBootOrder[aPosition - 1] = aDevice;
3406
3407 return S_OK;
3408}
3409
3410HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3411{
3412 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3413 return setError(E_INVALIDARG,
3414 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3415 aPosition, SchemaDefs::MaxBootPosition);
3416
3417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3418
3419 *aDevice = mHWData->mBootOrder[aPosition - 1];
3420
3421 return S_OK;
3422}
3423
3424HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3425 LONG aControllerPort,
3426 LONG aDevice,
3427 DeviceType_T aType,
3428 const ComPtr<IMedium> &aMedium)
3429{
3430 IMedium *aM = aMedium;
3431 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3432 aName.c_str(), aControllerPort, aDevice, aType, aM));
3433
3434 // request the host lock first, since might be calling Host methods for getting host drives;
3435 // next, protect the media tree all the while we're in here, as well as our member variables
3436 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3437 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3438
3439 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3440 if (FAILED(rc)) return rc;
3441
3442 /// @todo NEWMEDIA implicit machine registration
3443 if (!mData->mRegistered)
3444 return setError(VBOX_E_INVALID_OBJECT_STATE,
3445 tr("Cannot attach storage devices to an unregistered machine"));
3446
3447 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3448
3449 /* Check for an existing controller. */
3450 ComObjPtr<StorageController> ctl;
3451 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3452 if (FAILED(rc)) return rc;
3453
3454 StorageControllerType_T ctrlType;
3455 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3456 if (FAILED(rc))
3457 return setError(E_FAIL,
3458 tr("Could not get type of controller '%s'"),
3459 aName.c_str());
3460
3461 bool fSilent = false;
3462 Utf8Str strReconfig;
3463
3464 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3465 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3466 if ( mData->mMachineState == MachineState_Paused
3467 && strReconfig == "1")
3468 fSilent = true;
3469
3470 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3471 bool fHotplug = false;
3472 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3473 fHotplug = true;
3474
3475 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3476 return setError(VBOX_E_INVALID_VM_STATE,
3477 tr("Controller '%s' does not support hotplugging"),
3478 aName.c_str());
3479
3480 // check that the port and device are not out of range
3481 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3482 if (FAILED(rc)) return rc;
3483
3484 /* check if the device slot is already busy */
3485 MediumAttachment *pAttachTemp;
3486 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3487 aName,
3488 aControllerPort,
3489 aDevice)))
3490 {
3491 Medium *pMedium = pAttachTemp->i_getMedium();
3492 if (pMedium)
3493 {
3494 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3495 return setError(VBOX_E_OBJECT_IN_USE,
3496 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3497 pMedium->i_getLocationFull().c_str(),
3498 aControllerPort,
3499 aDevice,
3500 aName.c_str());
3501 }
3502 else
3503 return setError(VBOX_E_OBJECT_IN_USE,
3504 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3505 aControllerPort, aDevice, aName.c_str());
3506 }
3507
3508 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3509 if (aMedium && medium.isNull())
3510 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3511
3512 AutoCaller mediumCaller(medium);
3513 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3514
3515 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3516
3517 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3518 && !medium.isNull() && medium->i_getType() != MediumType_Readonly
3519 && medium->i_getDeviceType() != DeviceType_DVD
3520 )
3521 return setError(VBOX_E_OBJECT_IN_USE,
3522 tr("Medium '%s' is already attached to this virtual machine"),
3523 medium->i_getLocationFull().c_str());
3524
3525 if (!medium.isNull())
3526 {
3527 MediumType_T mtype = medium->i_getType();
3528 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3529 // For DVDs it's not written to the config file, so needs no global config
3530 // version bump. For floppies it's a new attribute "type", which is ignored
3531 // by older VirtualBox version, so needs no global config version bump either.
3532 // For hard disks this type is not accepted.
3533 if (mtype == MediumType_MultiAttach)
3534 {
3535 // This type is new with VirtualBox 4.0 and therefore requires settings
3536 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3537 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3538 // two reasons: The medium type is a property of the media registry tree, which
3539 // can reside in the global config file (for pre-4.0 media); we would therefore
3540 // possibly need to bump the global config version. We don't want to do that though
3541 // because that might make downgrading to pre-4.0 impossible.
3542 // As a result, we can only use these two new types if the medium is NOT in the
3543 // global registry:
3544 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3545 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3546 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3547 )
3548 return setError(VBOX_E_INVALID_OBJECT_STATE,
3549 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3550 "to machines that were created with VirtualBox 4.0 or later"),
3551 medium->i_getLocationFull().c_str());
3552 }
3553 }
3554
3555 bool fIndirect = false;
3556 if (!medium.isNull())
3557 fIndirect = medium->i_isReadOnly();
3558 bool associate = true;
3559
3560 do
3561 {
3562 if ( aType == DeviceType_HardDisk
3563 && mMediumAttachments.isBackedUp())
3564 {
3565 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3566
3567 /* check if the medium was attached to the VM before we started
3568 * changing attachments in which case the attachment just needs to
3569 * be restored */
3570 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3571 {
3572 AssertReturn(!fIndirect, E_FAIL);
3573
3574 /* see if it's the same bus/channel/device */
3575 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3576 {
3577 /* the simplest case: restore the whole attachment
3578 * and return, nothing else to do */
3579 mMediumAttachments->push_back(pAttachTemp);
3580
3581 /* Reattach the medium to the VM. */
3582 if (fHotplug || fSilent)
3583 {
3584 mediumLock.release();
3585 treeLock.release();
3586 alock.release();
3587
3588 MediumLockList *pMediumLockList(new MediumLockList());
3589
3590 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3591 medium /* pToLockWrite */,
3592 false /* fMediumLockWriteAll */,
3593 NULL,
3594 *pMediumLockList);
3595 alock.acquire();
3596 if (FAILED(rc))
3597 delete pMediumLockList;
3598 else
3599 {
3600 mData->mSession.mLockedMedia.Unlock();
3601 alock.release();
3602 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3603 mData->mSession.mLockedMedia.Lock();
3604 alock.acquire();
3605 }
3606 alock.release();
3607
3608 if (SUCCEEDED(rc))
3609 {
3610 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3611 /* Remove lock list in case of error. */
3612 if (FAILED(rc))
3613 {
3614 mData->mSession.mLockedMedia.Unlock();
3615 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3616 mData->mSession.mLockedMedia.Lock();
3617 }
3618 }
3619 }
3620
3621 return S_OK;
3622 }
3623
3624 /* bus/channel/device differ; we need a new attachment object,
3625 * but don't try to associate it again */
3626 associate = false;
3627 break;
3628 }
3629 }
3630
3631 /* go further only if the attachment is to be indirect */
3632 if (!fIndirect)
3633 break;
3634
3635 /* perform the so called smart attachment logic for indirect
3636 * attachments. Note that smart attachment is only applicable to base
3637 * hard disks. */
3638
3639 if (medium->i_getParent().isNull())
3640 {
3641 /* first, investigate the backup copy of the current hard disk
3642 * attachments to make it possible to re-attach existing diffs to
3643 * another device slot w/o losing their contents */
3644 if (mMediumAttachments.isBackedUp())
3645 {
3646 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3647
3648 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3649 uint32_t foundLevel = 0;
3650
3651 for (MediumAttachmentList::const_iterator
3652 it = oldAtts.begin();
3653 it != oldAtts.end();
3654 ++it)
3655 {
3656 uint32_t level = 0;
3657 MediumAttachment *pAttach = *it;
3658 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3659 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3660 if (pMedium.isNull())
3661 continue;
3662
3663 if (pMedium->i_getBase(&level) == medium)
3664 {
3665 /* skip the hard disk if its currently attached (we
3666 * cannot attach the same hard disk twice) */
3667 if (i_findAttachment(*mMediumAttachments.data(),
3668 pMedium))
3669 continue;
3670
3671 /* matched device, channel and bus (i.e. attached to the
3672 * same place) will win and immediately stop the search;
3673 * otherwise the attachment that has the youngest
3674 * descendant of medium will be used
3675 */
3676 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3677 {
3678 /* the simplest case: restore the whole attachment
3679 * and return, nothing else to do */
3680 mMediumAttachments->push_back(*it);
3681
3682 /* Reattach the medium to the VM. */
3683 if (fHotplug || fSilent)
3684 {
3685 mediumLock.release();
3686 treeLock.release();
3687 alock.release();
3688
3689 MediumLockList *pMediumLockList(new MediumLockList());
3690
3691 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3692 medium /* pToLockWrite */,
3693 false /* fMediumLockWriteAll */,
3694 NULL,
3695 *pMediumLockList);
3696 alock.acquire();
3697 if (FAILED(rc))
3698 delete pMediumLockList;
3699 else
3700 {
3701 mData->mSession.mLockedMedia.Unlock();
3702 alock.release();
3703 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3704 mData->mSession.mLockedMedia.Lock();
3705 alock.acquire();
3706 }
3707 alock.release();
3708
3709 if (SUCCEEDED(rc))
3710 {
3711 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3712 /* Remove lock list in case of error. */
3713 if (FAILED(rc))
3714 {
3715 mData->mSession.mLockedMedia.Unlock();
3716 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3717 mData->mSession.mLockedMedia.Lock();
3718 }
3719 }
3720 }
3721
3722 return S_OK;
3723 }
3724 else if ( foundIt == oldAtts.end()
3725 || level > foundLevel /* prefer younger */
3726 )
3727 {
3728 foundIt = it;
3729 foundLevel = level;
3730 }
3731 }
3732 }
3733
3734 if (foundIt != oldAtts.end())
3735 {
3736 /* use the previously attached hard disk */
3737 medium = (*foundIt)->i_getMedium();
3738 mediumCaller.attach(medium);
3739 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3740 mediumLock.attach(medium);
3741 /* not implicit, doesn't require association with this VM */
3742 fIndirect = false;
3743 associate = false;
3744 /* go right to the MediumAttachment creation */
3745 break;
3746 }
3747 }
3748
3749 /* must give up the medium lock and medium tree lock as below we
3750 * go over snapshots, which needs a lock with higher lock order. */
3751 mediumLock.release();
3752 treeLock.release();
3753
3754 /* then, search through snapshots for the best diff in the given
3755 * hard disk's chain to base the new diff on */
3756
3757 ComObjPtr<Medium> base;
3758 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3759 while (snap)
3760 {
3761 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3762
3763 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3764
3765 MediumAttachment *pAttachFound = NULL;
3766 uint32_t foundLevel = 0;
3767
3768 for (MediumAttachmentList::const_iterator
3769 it = snapAtts.begin();
3770 it != snapAtts.end();
3771 ++it)
3772 {
3773 MediumAttachment *pAttach = *it;
3774 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3775 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3776 if (pMedium.isNull())
3777 continue;
3778
3779 uint32_t level = 0;
3780 if (pMedium->i_getBase(&level) == medium)
3781 {
3782 /* matched device, channel and bus (i.e. attached to the
3783 * same place) will win and immediately stop the search;
3784 * otherwise the attachment that has the youngest
3785 * descendant of medium will be used
3786 */
3787 if ( pAttach->i_getDevice() == aDevice
3788 && pAttach->i_getPort() == aControllerPort
3789 && pAttach->i_getControllerName() == aName
3790 )
3791 {
3792 pAttachFound = pAttach;
3793 break;
3794 }
3795 else if ( !pAttachFound
3796 || level > foundLevel /* prefer younger */
3797 )
3798 {
3799 pAttachFound = pAttach;
3800 foundLevel = level;
3801 }
3802 }
3803 }
3804
3805 if (pAttachFound)
3806 {
3807 base = pAttachFound->i_getMedium();
3808 break;
3809 }
3810
3811 snap = snap->i_getParent();
3812 }
3813
3814 /* re-lock medium tree and the medium, as we need it below */
3815 treeLock.acquire();
3816 mediumLock.acquire();
3817
3818 /* found a suitable diff, use it as a base */
3819 if (!base.isNull())
3820 {
3821 medium = base;
3822 mediumCaller.attach(medium);
3823 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3824 mediumLock.attach(medium);
3825 }
3826 }
3827
3828 Utf8Str strFullSnapshotFolder;
3829 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3830
3831 ComObjPtr<Medium> diff;
3832 diff.createObject();
3833 // store this diff in the same registry as the parent
3834 Guid uuidRegistryParent;
3835 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3836 {
3837 // parent image has no registry: this can happen if we're attaching a new immutable
3838 // image that has not yet been attached (medium then points to the base and we're
3839 // creating the diff image for the immutable, and the parent is not yet registered);
3840 // put the parent in the machine registry then
3841 mediumLock.release();
3842 treeLock.release();
3843 alock.release();
3844 i_addMediumToRegistry(medium);
3845 alock.acquire();
3846 treeLock.acquire();
3847 mediumLock.acquire();
3848 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3849 }
3850 rc = diff->init(mParent,
3851 medium->i_getPreferredDiffFormat(),
3852 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3853 uuidRegistryParent,
3854 DeviceType_HardDisk);
3855 if (FAILED(rc)) return rc;
3856
3857 /* Apply the normal locking logic to the entire chain. */
3858 MediumLockList *pMediumLockList(new MediumLockList());
3859 mediumLock.release();
3860 treeLock.release();
3861 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3862 diff /* pToLockWrite */,
3863 false /* fMediumLockWriteAll */,
3864 medium,
3865 *pMediumLockList);
3866 treeLock.acquire();
3867 mediumLock.acquire();
3868 if (SUCCEEDED(rc))
3869 {
3870 mediumLock.release();
3871 treeLock.release();
3872 rc = pMediumLockList->Lock();
3873 treeLock.acquire();
3874 mediumLock.acquire();
3875 if (FAILED(rc))
3876 setError(rc,
3877 tr("Could not lock medium when creating diff '%s'"),
3878 diff->i_getLocationFull().c_str());
3879 else
3880 {
3881 /* will release the lock before the potentially lengthy
3882 * operation, so protect with the special state */
3883 MachineState_T oldState = mData->mMachineState;
3884 i_setMachineState(MachineState_SettingUp);
3885
3886 mediumLock.release();
3887 treeLock.release();
3888 alock.release();
3889
3890 rc = medium->i_createDiffStorage(diff,
3891 medium->i_getPreferredDiffVariant(),
3892 pMediumLockList,
3893 NULL /* aProgress */,
3894 true /* aWait */,
3895 false /* aNotify */);
3896
3897 alock.acquire();
3898 treeLock.acquire();
3899 mediumLock.acquire();
3900
3901 i_setMachineState(oldState);
3902 }
3903 }
3904
3905 /* Unlock the media and free the associated memory. */
3906 delete pMediumLockList;
3907
3908 if (FAILED(rc)) return rc;
3909
3910 /* use the created diff for the actual attachment */
3911 medium = diff;
3912 mediumCaller.attach(medium);
3913 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3914 mediumLock.attach(medium);
3915 }
3916 while (0);
3917
3918 ComObjPtr<MediumAttachment> attachment;
3919 attachment.createObject();
3920 rc = attachment->init(this,
3921 medium,
3922 aName,
3923 aControllerPort,
3924 aDevice,
3925 aType,
3926 fIndirect,
3927 false /* fPassthrough */,
3928 false /* fTempEject */,
3929 false /* fNonRotational */,
3930 false /* fDiscard */,
3931 fHotplug /* fHotPluggable */,
3932 Utf8Str::Empty);
3933 if (FAILED(rc)) return rc;
3934
3935 if (associate && !medium.isNull())
3936 {
3937 // as the last step, associate the medium to the VM
3938 rc = medium->i_addBackReference(mData->mUuid);
3939 // here we can fail because of Deleting, or being in process of creating a Diff
3940 if (FAILED(rc)) return rc;
3941
3942 mediumLock.release();
3943 treeLock.release();
3944 alock.release();
3945 i_addMediumToRegistry(medium);
3946 alock.acquire();
3947 treeLock.acquire();
3948 mediumLock.acquire();
3949 }
3950
3951 /* success: finally remember the attachment */
3952 i_setModified(IsModified_Storage);
3953 mMediumAttachments.backup();
3954 mMediumAttachments->push_back(attachment);
3955
3956 mediumLock.release();
3957 treeLock.release();
3958 alock.release();
3959
3960 if (fHotplug || fSilent)
3961 {
3962 if (!medium.isNull())
3963 {
3964 MediumLockList *pMediumLockList(new MediumLockList());
3965
3966 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3967 medium /* pToLockWrite */,
3968 false /* fMediumLockWriteAll */,
3969 NULL,
3970 *pMediumLockList);
3971 alock.acquire();
3972 if (FAILED(rc))
3973 delete pMediumLockList;
3974 else
3975 {
3976 mData->mSession.mLockedMedia.Unlock();
3977 alock.release();
3978 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3979 mData->mSession.mLockedMedia.Lock();
3980 alock.acquire();
3981 }
3982 alock.release();
3983 }
3984
3985 if (SUCCEEDED(rc))
3986 {
3987 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3988 /* Remove lock list in case of error. */
3989 if (FAILED(rc))
3990 {
3991 mData->mSession.mLockedMedia.Unlock();
3992 mData->mSession.mLockedMedia.Remove(attachment);
3993 mData->mSession.mLockedMedia.Lock();
3994 }
3995 }
3996 }
3997
3998 /* Save modified registries, but skip this machine as it's the caller's
3999 * job to save its settings like all other settings changes. */
4000 mParent->i_unmarkRegistryModified(i_getId());
4001 mParent->i_saveModifiedRegistries();
4002
4003 if (SUCCEEDED(rc))
4004 {
4005 if (fIndirect && medium != aM)
4006 mParent->i_onMediumConfigChanged(medium);
4007 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4008 }
4009
4010 return rc;
4011}
4012
4013HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4014 LONG aDevice)
4015{
4016 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4017 aName.c_str(), aControllerPort, aDevice));
4018
4019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4020
4021 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4022 if (FAILED(rc)) return rc;
4023
4024 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4025
4026 /* Check for an existing controller. */
4027 ComObjPtr<StorageController> ctl;
4028 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4029 if (FAILED(rc)) return rc;
4030
4031 StorageControllerType_T ctrlType;
4032 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4033 if (FAILED(rc))
4034 return setError(E_FAIL,
4035 tr("Could not get type of controller '%s'"),
4036 aName.c_str());
4037
4038 bool fSilent = false;
4039 Utf8Str strReconfig;
4040
4041 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4042 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4043 if ( mData->mMachineState == MachineState_Paused
4044 && strReconfig == "1")
4045 fSilent = true;
4046
4047 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4048 bool fHotplug = false;
4049 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4050 fHotplug = true;
4051
4052 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4053 return setError(VBOX_E_INVALID_VM_STATE,
4054 tr("Controller '%s' does not support hotplugging"),
4055 aName.c_str());
4056
4057 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4058 aName,
4059 aControllerPort,
4060 aDevice);
4061 if (!pAttach)
4062 return setError(VBOX_E_OBJECT_NOT_FOUND,
4063 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4064 aDevice, aControllerPort, aName.c_str());
4065
4066 if (fHotplug && !pAttach->i_getHotPluggable())
4067 return setError(VBOX_E_NOT_SUPPORTED,
4068 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4069 aDevice, aControllerPort, aName.c_str());
4070
4071 /*
4072 * The VM has to detach the device before we delete any implicit diffs.
4073 * If this fails we can roll back without loosing data.
4074 */
4075 if (fHotplug || fSilent)
4076 {
4077 alock.release();
4078 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4079 alock.acquire();
4080 }
4081 if (FAILED(rc)) return rc;
4082
4083 /* If we are here everything went well and we can delete the implicit now. */
4084 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4085
4086 alock.release();
4087
4088 /* Save modified registries, but skip this machine as it's the caller's
4089 * job to save its settings like all other settings changes. */
4090 mParent->i_unmarkRegistryModified(i_getId());
4091 mParent->i_saveModifiedRegistries();
4092
4093 if (SUCCEEDED(rc))
4094 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4095
4096 return rc;
4097}
4098
4099HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4100 LONG aDevice, BOOL aPassthrough)
4101{
4102 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4103 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4104
4105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4106
4107 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4108 if (FAILED(rc)) return rc;
4109
4110 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4111
4112 /* Check for an existing controller. */
4113 ComObjPtr<StorageController> ctl;
4114 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4115 if (FAILED(rc)) return rc;
4116
4117 StorageControllerType_T ctrlType;
4118 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4119 if (FAILED(rc))
4120 return setError(E_FAIL,
4121 tr("Could not get type of controller '%s'"),
4122 aName.c_str());
4123
4124 bool fSilent = false;
4125 Utf8Str strReconfig;
4126
4127 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4128 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4129 if ( mData->mMachineState == MachineState_Paused
4130 && strReconfig == "1")
4131 fSilent = true;
4132
4133 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4134 bool fHotplug = false;
4135 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4136 fHotplug = true;
4137
4138 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4139 return setError(VBOX_E_INVALID_VM_STATE,
4140 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4141 aName.c_str());
4142
4143 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4144 aName,
4145 aControllerPort,
4146 aDevice);
4147 if (!pAttach)
4148 return setError(VBOX_E_OBJECT_NOT_FOUND,
4149 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4150 aDevice, aControllerPort, aName.c_str());
4151
4152
4153 i_setModified(IsModified_Storage);
4154 mMediumAttachments.backup();
4155
4156 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4157
4158 if (pAttach->i_getType() != DeviceType_DVD)
4159 return setError(E_INVALIDARG,
4160 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4161 aDevice, aControllerPort, aName.c_str());
4162
4163 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4164
4165 pAttach->i_updatePassthrough(!!aPassthrough);
4166
4167 attLock.release();
4168 alock.release();
4169 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4170 if (SUCCEEDED(rc) && fValueChanged)
4171 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4172
4173 return rc;
4174}
4175
4176HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4177 LONG aDevice, BOOL aTemporaryEject)
4178{
4179
4180 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4181 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4182
4183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4184
4185 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4186 if (FAILED(rc)) return rc;
4187
4188 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4189 aName,
4190 aControllerPort,
4191 aDevice);
4192 if (!pAttach)
4193 return setError(VBOX_E_OBJECT_NOT_FOUND,
4194 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4195 aDevice, aControllerPort, aName.c_str());
4196
4197
4198 i_setModified(IsModified_Storage);
4199 mMediumAttachments.backup();
4200
4201 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4202
4203 if (pAttach->i_getType() != DeviceType_DVD)
4204 return setError(E_INVALIDARG,
4205 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4206 aDevice, aControllerPort, aName.c_str());
4207 pAttach->i_updateTempEject(!!aTemporaryEject);
4208
4209 return S_OK;
4210}
4211
4212HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4213 LONG aDevice, BOOL aNonRotational)
4214{
4215
4216 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4217 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4218
4219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4220
4221 HRESULT rc = i_checkStateDependency(MutableStateDep);
4222 if (FAILED(rc)) return rc;
4223
4224 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4225
4226 if (Global::IsOnlineOrTransient(mData->mMachineState))
4227 return setError(VBOX_E_INVALID_VM_STATE,
4228 tr("Invalid machine state: %s"),
4229 Global::stringifyMachineState(mData->mMachineState));
4230
4231 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4232 aName,
4233 aControllerPort,
4234 aDevice);
4235 if (!pAttach)
4236 return setError(VBOX_E_OBJECT_NOT_FOUND,
4237 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4238 aDevice, aControllerPort, aName.c_str());
4239
4240
4241 i_setModified(IsModified_Storage);
4242 mMediumAttachments.backup();
4243
4244 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4245
4246 if (pAttach->i_getType() != DeviceType_HardDisk)
4247 return setError(E_INVALIDARG,
4248 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"),
4249 aDevice, aControllerPort, aName.c_str());
4250 pAttach->i_updateNonRotational(!!aNonRotational);
4251
4252 return S_OK;
4253}
4254
4255HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4256 LONG aDevice, BOOL aDiscard)
4257{
4258
4259 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4260 aName.c_str(), aControllerPort, aDevice, aDiscard));
4261
4262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4263
4264 HRESULT rc = i_checkStateDependency(MutableStateDep);
4265 if (FAILED(rc)) return rc;
4266
4267 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4268
4269 if (Global::IsOnlineOrTransient(mData->mMachineState))
4270 return setError(VBOX_E_INVALID_VM_STATE,
4271 tr("Invalid machine state: %s"),
4272 Global::stringifyMachineState(mData->mMachineState));
4273
4274 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4275 aName,
4276 aControllerPort,
4277 aDevice);
4278 if (!pAttach)
4279 return setError(VBOX_E_OBJECT_NOT_FOUND,
4280 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4281 aDevice, aControllerPort, aName.c_str());
4282
4283
4284 i_setModified(IsModified_Storage);
4285 mMediumAttachments.backup();
4286
4287 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4288
4289 if (pAttach->i_getType() != DeviceType_HardDisk)
4290 return setError(E_INVALIDARG,
4291 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"),
4292 aDevice, aControllerPort, aName.c_str());
4293 pAttach->i_updateDiscard(!!aDiscard);
4294
4295 return S_OK;
4296}
4297
4298HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4299 LONG aDevice, BOOL aHotPluggable)
4300{
4301 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4302 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4303
4304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4305
4306 HRESULT rc = i_checkStateDependency(MutableStateDep);
4307 if (FAILED(rc)) return rc;
4308
4309 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4310
4311 if (Global::IsOnlineOrTransient(mData->mMachineState))
4312 return setError(VBOX_E_INVALID_VM_STATE,
4313 tr("Invalid machine state: %s"),
4314 Global::stringifyMachineState(mData->mMachineState));
4315
4316 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4317 aName,
4318 aControllerPort,
4319 aDevice);
4320 if (!pAttach)
4321 return setError(VBOX_E_OBJECT_NOT_FOUND,
4322 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4323 aDevice, aControllerPort, aName.c_str());
4324
4325 /* Check for an existing controller. */
4326 ComObjPtr<StorageController> ctl;
4327 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4328 if (FAILED(rc)) return rc;
4329
4330 StorageControllerType_T ctrlType;
4331 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4332 if (FAILED(rc))
4333 return setError(E_FAIL,
4334 tr("Could not get type of controller '%s'"),
4335 aName.c_str());
4336
4337 if (!i_isControllerHotplugCapable(ctrlType))
4338 return setError(VBOX_E_NOT_SUPPORTED,
4339 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4340 aName.c_str());
4341
4342 i_setModified(IsModified_Storage);
4343 mMediumAttachments.backup();
4344
4345 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4346
4347 if (pAttach->i_getType() == DeviceType_Floppy)
4348 return setError(E_INVALIDARG,
4349 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"),
4350 aDevice, aControllerPort, aName.c_str());
4351 pAttach->i_updateHotPluggable(!!aHotPluggable);
4352
4353 return S_OK;
4354}
4355
4356HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4357 LONG aDevice)
4358{
4359 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4360 aName.c_str(), aControllerPort, aDevice));
4361
4362 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4363}
4364
4365HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4366 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4367{
4368 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4369 aName.c_str(), aControllerPort, aDevice));
4370
4371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4372
4373 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4374 if (FAILED(rc)) return rc;
4375
4376 if (Global::IsOnlineOrTransient(mData->mMachineState))
4377 return setError(VBOX_E_INVALID_VM_STATE,
4378 tr("Invalid machine state: %s"),
4379 Global::stringifyMachineState(mData->mMachineState));
4380
4381 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4382 aName,
4383 aControllerPort,
4384 aDevice);
4385 if (!pAttach)
4386 return setError(VBOX_E_OBJECT_NOT_FOUND,
4387 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4388 aDevice, aControllerPort, aName.c_str());
4389
4390
4391 i_setModified(IsModified_Storage);
4392 mMediumAttachments.backup();
4393
4394 IBandwidthGroup *iB = aBandwidthGroup;
4395 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4396 if (aBandwidthGroup && group.isNull())
4397 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4398
4399 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4400
4401 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4402 if (strBandwidthGroupOld.isNotEmpty())
4403 {
4404 /* Get the bandwidth group object and release it - this must not fail. */
4405 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4406 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4407 Assert(SUCCEEDED(rc));
4408
4409 pBandwidthGroupOld->i_release();
4410 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4411 }
4412
4413 if (!group.isNull())
4414 {
4415 group->i_reference();
4416 pAttach->i_updateBandwidthGroup(group->i_getName());
4417 }
4418
4419 return S_OK;
4420}
4421
4422HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4423 LONG aControllerPort,
4424 LONG aDevice,
4425 DeviceType_T aType)
4426{
4427 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4428 aName.c_str(), aControllerPort, aDevice, aType));
4429
4430 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4431}
4432
4433
4434HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4435 LONG aControllerPort,
4436 LONG aDevice,
4437 BOOL aForce)
4438{
4439 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4440 aName.c_str(), aControllerPort, aForce));
4441
4442 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4443}
4444
4445HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4446 LONG aControllerPort,
4447 LONG aDevice,
4448 const ComPtr<IMedium> &aMedium,
4449 BOOL aForce)
4450{
4451 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4452 aName.c_str(), aControllerPort, aDevice, aForce));
4453
4454 // request the host lock first, since might be calling Host methods for getting host drives;
4455 // next, protect the media tree all the while we're in here, as well as our member variables
4456 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4457 this->lockHandle(),
4458 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4459
4460 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4461 aName,
4462 aControllerPort,
4463 aDevice);
4464 if (pAttach.isNull())
4465 return setError(VBOX_E_OBJECT_NOT_FOUND,
4466 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4467 aDevice, aControllerPort, aName.c_str());
4468
4469 /* Remember previously mounted medium. The medium before taking the
4470 * backup is not necessarily the same thing. */
4471 ComObjPtr<Medium> oldmedium;
4472 oldmedium = pAttach->i_getMedium();
4473
4474 IMedium *iM = aMedium;
4475 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4476 if (aMedium && pMedium.isNull())
4477 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4478
4479 AutoCaller mediumCaller(pMedium);
4480 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4481
4482 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4483 if (pMedium)
4484 {
4485 DeviceType_T mediumType = pAttach->i_getType();
4486 switch (mediumType)
4487 {
4488 case DeviceType_DVD:
4489 case DeviceType_Floppy:
4490 break;
4491
4492 default:
4493 return setError(VBOX_E_INVALID_OBJECT_STATE,
4494 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4495 aControllerPort,
4496 aDevice,
4497 aName.c_str());
4498 }
4499 }
4500
4501 i_setModified(IsModified_Storage);
4502 mMediumAttachments.backup();
4503
4504 {
4505 // The backup operation makes the pAttach reference point to the
4506 // old settings. Re-get the correct reference.
4507 pAttach = i_findAttachment(*mMediumAttachments.data(),
4508 aName,
4509 aControllerPort,
4510 aDevice);
4511 if (!oldmedium.isNull())
4512 oldmedium->i_removeBackReference(mData->mUuid);
4513 if (!pMedium.isNull())
4514 {
4515 pMedium->i_addBackReference(mData->mUuid);
4516
4517 mediumLock.release();
4518 multiLock.release();
4519 i_addMediumToRegistry(pMedium);
4520 multiLock.acquire();
4521 mediumLock.acquire();
4522 }
4523
4524 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4525 pAttach->i_updateMedium(pMedium);
4526 }
4527
4528 i_setModified(IsModified_Storage);
4529
4530 mediumLock.release();
4531 multiLock.release();
4532 HRESULT rc = i_onMediumChange(pAttach, aForce);
4533 multiLock.acquire();
4534 mediumLock.acquire();
4535
4536 /* On error roll back this change only. */
4537 if (FAILED(rc))
4538 {
4539 if (!pMedium.isNull())
4540 pMedium->i_removeBackReference(mData->mUuid);
4541 pAttach = i_findAttachment(*mMediumAttachments.data(),
4542 aName,
4543 aControllerPort,
4544 aDevice);
4545 /* If the attachment is gone in the meantime, bail out. */
4546 if (pAttach.isNull())
4547 return rc;
4548 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4549 if (!oldmedium.isNull())
4550 oldmedium->i_addBackReference(mData->mUuid);
4551 pAttach->i_updateMedium(oldmedium);
4552 }
4553
4554 mediumLock.release();
4555 multiLock.release();
4556
4557 /* Save modified registries, but skip this machine as it's the caller's
4558 * job to save its settings like all other settings changes. */
4559 mParent->i_unmarkRegistryModified(i_getId());
4560 mParent->i_saveModifiedRegistries();
4561
4562 return rc;
4563}
4564HRESULT Machine::getMedium(const com::Utf8Str &aName,
4565 LONG aControllerPort,
4566 LONG aDevice,
4567 ComPtr<IMedium> &aMedium)
4568{
4569 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4570 aName.c_str(), aControllerPort, aDevice));
4571
4572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4573
4574 aMedium = NULL;
4575
4576 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4577 aName,
4578 aControllerPort,
4579 aDevice);
4580 if (pAttach.isNull())
4581 return setError(VBOX_E_OBJECT_NOT_FOUND,
4582 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4583 aDevice, aControllerPort, aName.c_str());
4584
4585 aMedium = pAttach->i_getMedium();
4586
4587 return S_OK;
4588}
4589
4590HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4591{
4592
4593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4594
4595 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4596
4597 return S_OK;
4598}
4599
4600HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4601{
4602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4603
4604 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4605
4606 return S_OK;
4607}
4608
4609HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4610{
4611 /* Do not assert if slot is out of range, just return the advertised
4612 status. testdriver/vbox.py triggers this in logVmInfo. */
4613 if (aSlot >= mNetworkAdapters.size())
4614 return setError(E_INVALIDARG,
4615 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4616 aSlot, mNetworkAdapters.size());
4617
4618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4619
4620 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4621
4622 return S_OK;
4623}
4624
4625HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4626{
4627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4628
4629 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4630 size_t i = 0;
4631 for (settings::StringsMap::const_iterator
4632 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4633 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4634 ++it, ++i)
4635 aKeys[i] = it->first;
4636
4637 return S_OK;
4638}
4639
4640 /**
4641 * @note Locks this object for reading.
4642 */
4643HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4644 com::Utf8Str &aValue)
4645{
4646 /* start with nothing found */
4647 aValue = "";
4648
4649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4650
4651 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4652 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4653 // found:
4654 aValue = it->second; // source is a Utf8Str
4655
4656 /* return the result to caller (may be empty) */
4657 return S_OK;
4658}
4659
4660 /**
4661 * @note Locks mParent for writing + this object for writing.
4662 */
4663HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4664{
4665 /* Because control characters in aKey have caused problems in the settings
4666 * they are rejected unless the key should be deleted. */
4667 if (!aValue.isEmpty())
4668 {
4669 for (size_t i = 0; i < aKey.length(); ++i)
4670 {
4671 char ch = aKey[i];
4672 if (RTLocCIsCntrl(ch))
4673 return E_INVALIDARG;
4674 }
4675 }
4676
4677 Utf8Str strOldValue; // empty
4678
4679 // locking note: we only hold the read lock briefly to look up the old value,
4680 // then release it and call the onExtraCanChange callbacks. There is a small
4681 // chance of a race insofar as the callback might be called twice if two callers
4682 // change the same key at the same time, but that's a much better solution
4683 // than the deadlock we had here before. The actual changing of the extradata
4684 // is then performed under the write lock and race-free.
4685
4686 // look up the old value first; if nothing has changed then we need not do anything
4687 {
4688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4689
4690 // For snapshots don't even think about allowing changes, extradata
4691 // is global for a machine, so there is nothing snapshot specific.
4692 if (i_isSnapshotMachine())
4693 return setError(VBOX_E_INVALID_VM_STATE,
4694 tr("Cannot set extradata for a snapshot"));
4695
4696 // check if the right IMachine instance is used
4697 if (mData->mRegistered && !i_isSessionMachine())
4698 return setError(VBOX_E_INVALID_VM_STATE,
4699 tr("Cannot set extradata for an immutable machine"));
4700
4701 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4702 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4703 strOldValue = it->second;
4704 }
4705
4706 bool fChanged;
4707 if ((fChanged = (strOldValue != aValue)))
4708 {
4709 // ask for permission from all listeners outside the locks;
4710 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4711 // lock to copy the list of callbacks to invoke
4712 Bstr bstrError;
4713 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4714 {
4715 const char *sep = bstrError.isEmpty() ? "" : ": ";
4716 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4717 return setError(E_ACCESSDENIED,
4718 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4719 aKey.c_str(),
4720 aValue.c_str(),
4721 sep,
4722 bstrError.raw());
4723 }
4724
4725 // data is changing and change not vetoed: then write it out under the lock
4726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4727
4728 if (aValue.isEmpty())
4729 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4730 else
4731 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4732 // creates a new key if needed
4733
4734 bool fNeedsGlobalSaveSettings = false;
4735 // This saving of settings is tricky: there is no "old state" for the
4736 // extradata items at all (unlike all other settings), so the old/new
4737 // settings comparison would give a wrong result!
4738 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4739
4740 if (fNeedsGlobalSaveSettings)
4741 {
4742 // save the global settings; for that we should hold only the VirtualBox lock
4743 alock.release();
4744 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4745 mParent->i_saveSettings();
4746 }
4747 }
4748
4749 // fire notification outside the lock
4750 if (fChanged)
4751 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4752
4753 return S_OK;
4754}
4755
4756HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4757{
4758 aProgress = NULL;
4759 NOREF(aSettingsFilePath);
4760 ReturnComNotImplemented();
4761}
4762
4763HRESULT Machine::saveSettings()
4764{
4765 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4766
4767 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4768 if (FAILED(rc)) return rc;
4769
4770 /* the settings file path may never be null */
4771 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4772
4773 /* save all VM data excluding snapshots */
4774 bool fNeedsGlobalSaveSettings = false;
4775 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4776 mlock.release();
4777
4778 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4779 {
4780 // save the global settings; for that we should hold only the VirtualBox lock
4781 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4782 rc = mParent->i_saveSettings();
4783 }
4784
4785 return rc;
4786}
4787
4788
4789HRESULT Machine::discardSettings()
4790{
4791 /*
4792 * We need to take the machine list lock here as well as the machine one
4793 * or we'll get into trouble should any media stuff require rolling back.
4794 *
4795 * Details:
4796 *
4797 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4798 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4799 * 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]
4800 * 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
4801 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4802 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4803 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4804 * 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
4805 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4806 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4807 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4808 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4809 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4810 * 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]
4811 * 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] (*)
4812 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4813 * 0:005> k
4814 * # Child-SP RetAddr Call Site
4815 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4816 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4817 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4818 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4819 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4820 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4821 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4822 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4823 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4824 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4825 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4826 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4827 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4828 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4829 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4830 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4831 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4832 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4833 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4834 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4835 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4836 *
4837 */
4838 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4840
4841 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4842 if (FAILED(rc)) return rc;
4843
4844 /*
4845 * during this rollback, the session will be notified if data has
4846 * been actually changed
4847 */
4848 i_rollback(true /* aNotify */);
4849
4850 return S_OK;
4851}
4852
4853/** @note Locks objects! */
4854HRESULT Machine::unregister(AutoCaller &autoCaller,
4855 CleanupMode_T aCleanupMode,
4856 std::vector<ComPtr<IMedium> > &aMedia)
4857{
4858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4859
4860 Guid id(i_getId());
4861
4862 if (mData->mSession.mState != SessionState_Unlocked)
4863 return setError(VBOX_E_INVALID_OBJECT_STATE,
4864 tr("Cannot unregister the machine '%s' while it is locked"),
4865 mUserData->s.strName.c_str());
4866
4867 // wait for state dependents to drop to zero
4868 i_ensureNoStateDependencies();
4869
4870 if (!mData->mAccessible)
4871 {
4872 // inaccessible machines can only be unregistered; uninitialize ourselves
4873 // here because currently there may be no unregistered that are inaccessible
4874 // (this state combination is not supported). Note releasing the caller and
4875 // leaving the lock before calling uninit()
4876 alock.release();
4877 autoCaller.release();
4878
4879 uninit();
4880
4881 mParent->i_unregisterMachine(this, id);
4882 // calls VirtualBox::i_saveSettings()
4883
4884 return S_OK;
4885 }
4886
4887 HRESULT rc = S_OK;
4888 mData->llFilesToDelete.clear();
4889
4890 if (!mSSData->strStateFilePath.isEmpty())
4891 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4892
4893 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4894 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4895 mData->llFilesToDelete.push_back(strNVRAMFile);
4896
4897 // This list collects the medium objects from all medium attachments
4898 // which we will detach from the machine and its snapshots, in a specific
4899 // order which allows for closing all media without getting "media in use"
4900 // errors, simply by going through the list from the front to the back:
4901 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4902 // and must be closed before the parent media from the snapshots, or closing the parents
4903 // will fail because they still have children);
4904 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4905 // the root ("first") snapshot of the machine.
4906 MediaList llMedia;
4907
4908 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4909 && mMediumAttachments->size()
4910 )
4911 {
4912 // we have media attachments: detach them all and add the Medium objects to our list
4913 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4914 }
4915
4916 if (mData->mFirstSnapshot)
4917 {
4918 // add the media from the medium attachments of the snapshots to llMedia
4919 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4920 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4921 // into the children first
4922
4923 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4924 MachineState_T oldState = mData->mMachineState;
4925 mData->mMachineState = MachineState_DeletingSnapshot;
4926
4927 // make a copy of the first snapshot reference so the refcount does not
4928 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4929 // (would hang due to the AutoCaller voodoo)
4930 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4931
4932 // GO!
4933 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4934
4935 mData->mMachineState = oldState;
4936 }
4937
4938 if (FAILED(rc))
4939 {
4940 i_rollbackMedia();
4941 return rc;
4942 }
4943
4944 // commit all the media changes made above
4945 i_commitMedia();
4946
4947 mData->mRegistered = false;
4948
4949 // machine lock no longer needed
4950 alock.release();
4951
4952 /* Make sure that the settings of the current VM are not saved, because
4953 * they are rather crippled at this point to meet the cleanup expectations
4954 * and there's no point destroying the VM config on disk just because. */
4955 mParent->i_unmarkRegistryModified(id);
4956
4957 // return media to caller
4958 aMedia.resize(llMedia.size());
4959 size_t i = 0;
4960 for (MediaList::const_iterator
4961 it = llMedia.begin();
4962 it != llMedia.end();
4963 ++it, ++i)
4964 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4965
4966 mParent->i_unregisterMachine(this, id);
4967 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4968
4969 return S_OK;
4970}
4971
4972/**
4973 * Task record for deleting a machine config.
4974 */
4975class Machine::DeleteConfigTask
4976 : public Machine::Task
4977{
4978public:
4979 DeleteConfigTask(Machine *m,
4980 Progress *p,
4981 const Utf8Str &t,
4982 const RTCList<ComPtr<IMedium> > &llMediums,
4983 const StringsList &llFilesToDelete)
4984 : Task(m, p, t),
4985 m_llMediums(llMediums),
4986 m_llFilesToDelete(llFilesToDelete)
4987 {}
4988
4989private:
4990 void handler()
4991 {
4992 try
4993 {
4994 m_pMachine->i_deleteConfigHandler(*this);
4995 }
4996 catch (...)
4997 {
4998 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4999 }
5000 }
5001
5002 RTCList<ComPtr<IMedium> > m_llMediums;
5003 StringsList m_llFilesToDelete;
5004
5005 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5006};
5007
5008/**
5009 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5010 * SessionMachine::taskHandler().
5011 *
5012 * @note Locks this object for writing.
5013 *
5014 * @param task
5015 * @return
5016 */
5017void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5018{
5019 LogFlowThisFuncEnter();
5020
5021 AutoCaller autoCaller(this);
5022 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5023 if (FAILED(autoCaller.rc()))
5024 {
5025 /* we might have been uninitialized because the session was accidentally
5026 * closed by the client, so don't assert */
5027 HRESULT rc = setError(E_FAIL,
5028 tr("The session has been accidentally closed"));
5029 task.m_pProgress->i_notifyComplete(rc);
5030 LogFlowThisFuncLeave();
5031 return;
5032 }
5033
5034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5035
5036 HRESULT rc = S_OK;
5037
5038 try
5039 {
5040 ULONG uLogHistoryCount = 3;
5041 ComPtr<ISystemProperties> systemProperties;
5042 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5043 if (FAILED(rc)) throw rc;
5044
5045 if (!systemProperties.isNull())
5046 {
5047 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5048 if (FAILED(rc)) throw rc;
5049 }
5050
5051 MachineState_T oldState = mData->mMachineState;
5052 i_setMachineState(MachineState_SettingUp);
5053 alock.release();
5054 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5055 {
5056 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5057 {
5058 AutoCaller mac(pMedium);
5059 if (FAILED(mac.rc())) throw mac.rc();
5060 Utf8Str strLocation = pMedium->i_getLocationFull();
5061 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5062 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5063 if (FAILED(rc)) throw rc;
5064 }
5065 if (pMedium->i_isMediumFormatFile())
5066 {
5067 ComPtr<IProgress> pProgress2;
5068 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5069 if (FAILED(rc)) throw rc;
5070 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5071 if (FAILED(rc)) throw rc;
5072 }
5073
5074 /* Close the medium, deliberately without checking the return
5075 * code, and without leaving any trace in the error info, as
5076 * a failure here is a very minor issue, which shouldn't happen
5077 * as above we even managed to delete the medium. */
5078 {
5079 ErrorInfoKeeper eik;
5080 pMedium->Close();
5081 }
5082 }
5083 i_setMachineState(oldState);
5084 alock.acquire();
5085
5086 // delete the files pushed on the task list by Machine::Delete()
5087 // (this includes saved states of the machine and snapshots and
5088 // medium storage files from the IMedium list passed in, and the
5089 // machine XML file)
5090 for (StringsList::const_iterator
5091 it = task.m_llFilesToDelete.begin();
5092 it != task.m_llFilesToDelete.end();
5093 ++it)
5094 {
5095 const Utf8Str &strFile = *it;
5096 LogFunc(("Deleting file %s\n", strFile.c_str()));
5097 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5098 if (FAILED(rc)) throw rc;
5099
5100 int vrc = RTFileDelete(strFile.c_str());
5101 if (RT_FAILURE(vrc))
5102 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5103 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5104 }
5105
5106 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5107 if (FAILED(rc)) throw rc;
5108
5109 /* delete the settings only when the file actually exists */
5110 if (mData->pMachineConfigFile->fileExists())
5111 {
5112 /* Delete any backup or uncommitted XML files. Ignore failures.
5113 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5114 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5115 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5116 RTFileDelete(otherXml.c_str());
5117 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5118 RTFileDelete(otherXml.c_str());
5119
5120 /* delete the Logs folder, nothing important should be left
5121 * there (we don't check for errors because the user might have
5122 * some private files there that we don't want to delete) */
5123 Utf8Str logFolder;
5124 getLogFolder(logFolder);
5125 Assert(logFolder.length());
5126 if (RTDirExists(logFolder.c_str()))
5127 {
5128 /* Delete all VBox.log[.N] files from the Logs folder
5129 * (this must be in sync with the rotation logic in
5130 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5131 * files that may have been created by the GUI. */
5132 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5133 RTFileDelete(log.c_str());
5134 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5135 RTFileDelete(log.c_str());
5136 for (ULONG i = uLogHistoryCount; i > 0; i--)
5137 {
5138 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5139 RTFileDelete(log.c_str());
5140 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5141 RTFileDelete(log.c_str());
5142 }
5143#if defined(RT_OS_WINDOWS)
5144 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5145 RTFileDelete(log.c_str());
5146 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5147 RTFileDelete(log.c_str());
5148#endif
5149
5150 RTDirRemove(logFolder.c_str());
5151 }
5152
5153 /* delete the Snapshots folder, nothing important should be left
5154 * there (we don't check for errors because the user might have
5155 * some private files there that we don't want to delete) */
5156 Utf8Str strFullSnapshotFolder;
5157 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5158 Assert(!strFullSnapshotFolder.isEmpty());
5159 if (RTDirExists(strFullSnapshotFolder.c_str()))
5160 RTDirRemove(strFullSnapshotFolder.c_str());
5161
5162 // delete the directory that contains the settings file, but only
5163 // if it matches the VM name
5164 Utf8Str settingsDir;
5165 if (i_isInOwnDir(&settingsDir))
5166 RTDirRemove(settingsDir.c_str());
5167 }
5168
5169 alock.release();
5170
5171 mParent->i_saveModifiedRegistries();
5172 }
5173 catch (HRESULT aRC) { rc = aRC; }
5174
5175 task.m_pProgress->i_notifyComplete(rc);
5176
5177 LogFlowThisFuncLeave();
5178}
5179
5180HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5181{
5182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5183
5184 HRESULT rc = i_checkStateDependency(MutableStateDep);
5185 if (FAILED(rc)) return rc;
5186
5187 if (mData->mRegistered)
5188 return setError(VBOX_E_INVALID_VM_STATE,
5189 tr("Cannot delete settings of a registered machine"));
5190
5191 // collect files to delete
5192 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5193 // machine config file
5194 if (mData->pMachineConfigFile->fileExists())
5195 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5196 // backup of machine config file
5197 Utf8Str strTmp(mData->m_strConfigFileFull);
5198 strTmp.append("-prev");
5199 if (RTFileExists(strTmp.c_str()))
5200 llFilesToDelete.push_back(strTmp);
5201
5202 RTCList<ComPtr<IMedium> > llMediums;
5203 for (size_t i = 0; i < aMedia.size(); ++i)
5204 {
5205 IMedium *pIMedium(aMedia[i]);
5206 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5207 if (pMedium.isNull())
5208 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5209 SafeArray<BSTR> ids;
5210 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5211 if (FAILED(rc)) return rc;
5212 /* At this point the medium should not have any back references
5213 * anymore. If it has it is attached to another VM and *must* not
5214 * deleted. */
5215 if (ids.size() < 1)
5216 llMediums.append(pMedium);
5217 }
5218
5219 ComObjPtr<Progress> pProgress;
5220 pProgress.createObject();
5221 rc = pProgress->init(i_getVirtualBox(),
5222 static_cast<IMachine*>(this) /* aInitiator */,
5223 tr("Deleting files"),
5224 true /* fCancellable */,
5225 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5226 tr("Collecting file inventory"));
5227 if (FAILED(rc))
5228 return rc;
5229
5230 /* create and start the task on a separate thread (note that it will not
5231 * start working until we release alock) */
5232 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5233 rc = pTask->createThread();
5234 pTask = NULL;
5235 if (FAILED(rc))
5236 return rc;
5237
5238 pProgress.queryInterfaceTo(aProgress.asOutParam());
5239
5240 LogFlowFuncLeave();
5241
5242 return S_OK;
5243}
5244
5245HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5246{
5247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5248
5249 ComObjPtr<Snapshot> pSnapshot;
5250 HRESULT rc;
5251
5252 if (aNameOrId.isEmpty())
5253 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5254 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5255 else
5256 {
5257 Guid uuid(aNameOrId);
5258 if (uuid.isValid())
5259 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5260 else
5261 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5262 }
5263 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5264
5265 return rc;
5266}
5267
5268HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5269 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5270{
5271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5272
5273 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5274 if (FAILED(rc)) return rc;
5275
5276 ComObjPtr<SharedFolder> sharedFolder;
5277 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5278 if (SUCCEEDED(rc))
5279 return setError(VBOX_E_OBJECT_IN_USE,
5280 tr("Shared folder named '%s' already exists"),
5281 aName.c_str());
5282
5283 sharedFolder.createObject();
5284 rc = sharedFolder->init(i_getMachine(),
5285 aName,
5286 aHostPath,
5287 !!aWritable,
5288 !!aAutomount,
5289 aAutoMountPoint,
5290 true /* fFailOnError */);
5291 if (FAILED(rc)) return rc;
5292
5293 i_setModified(IsModified_SharedFolders);
5294 mHWData.backup();
5295 mHWData->mSharedFolders.push_back(sharedFolder);
5296
5297 /* inform the direct session if any */
5298 alock.release();
5299 i_onSharedFolderChange();
5300
5301 return S_OK;
5302}
5303
5304HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5305{
5306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5307
5308 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5309 if (FAILED(rc)) return rc;
5310
5311 ComObjPtr<SharedFolder> sharedFolder;
5312 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5313 if (FAILED(rc)) return rc;
5314
5315 i_setModified(IsModified_SharedFolders);
5316 mHWData.backup();
5317 mHWData->mSharedFolders.remove(sharedFolder);
5318
5319 /* inform the direct session if any */
5320 alock.release();
5321 i_onSharedFolderChange();
5322
5323 return S_OK;
5324}
5325
5326HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5327{
5328 /* start with No */
5329 *aCanShow = FALSE;
5330
5331 ComPtr<IInternalSessionControl> directControl;
5332 {
5333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5334
5335 if (mData->mSession.mState != SessionState_Locked)
5336 return setError(VBOX_E_INVALID_VM_STATE,
5337 tr("Machine is not locked for session (session state: %s)"),
5338 Global::stringifySessionState(mData->mSession.mState));
5339
5340 if (mData->mSession.mLockType == LockType_VM)
5341 directControl = mData->mSession.mDirectControl;
5342 }
5343
5344 /* ignore calls made after #OnSessionEnd() is called */
5345 if (!directControl)
5346 return S_OK;
5347
5348 LONG64 dummy;
5349 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5350}
5351
5352HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5353{
5354 ComPtr<IInternalSessionControl> directControl;
5355 {
5356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5357
5358 if (mData->mSession.mState != SessionState_Locked)
5359 return setError(E_FAIL,
5360 tr("Machine is not locked for session (session state: %s)"),
5361 Global::stringifySessionState(mData->mSession.mState));
5362
5363 if (mData->mSession.mLockType == LockType_VM)
5364 directControl = mData->mSession.mDirectControl;
5365 }
5366
5367 /* ignore calls made after #OnSessionEnd() is called */
5368 if (!directControl)
5369 return S_OK;
5370
5371 BOOL dummy;
5372 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5373}
5374
5375#ifdef VBOX_WITH_GUEST_PROPS
5376/**
5377 * Look up a guest property in VBoxSVC's internal structures.
5378 */
5379HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5380 com::Utf8Str &aValue,
5381 LONG64 *aTimestamp,
5382 com::Utf8Str &aFlags) const
5383{
5384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5385
5386 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5387 if (it != mHWData->mGuestProperties.end())
5388 {
5389 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5390 aValue = it->second.strValue;
5391 *aTimestamp = it->second.mTimestamp;
5392 GuestPropWriteFlags(it->second.mFlags, szFlags);
5393 aFlags = Utf8Str(szFlags);
5394 }
5395
5396 return S_OK;
5397}
5398
5399/**
5400 * Query the VM that a guest property belongs to for the property.
5401 * @returns E_ACCESSDENIED if the VM process is not available or not
5402 * currently handling queries and the lookup should then be done in
5403 * VBoxSVC.
5404 */
5405HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5406 com::Utf8Str &aValue,
5407 LONG64 *aTimestamp,
5408 com::Utf8Str &aFlags) const
5409{
5410 HRESULT rc = S_OK;
5411 Bstr bstrValue;
5412 Bstr bstrFlags;
5413
5414 ComPtr<IInternalSessionControl> directControl;
5415 {
5416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5417 if (mData->mSession.mLockType == LockType_VM)
5418 directControl = mData->mSession.mDirectControl;
5419 }
5420
5421 /* ignore calls made after #OnSessionEnd() is called */
5422 if (!directControl)
5423 rc = E_ACCESSDENIED;
5424 else
5425 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5426 0 /* accessMode */,
5427 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5428
5429 aValue = bstrValue;
5430 aFlags = bstrFlags;
5431
5432 return rc;
5433}
5434#endif // VBOX_WITH_GUEST_PROPS
5435
5436HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5437 com::Utf8Str &aValue,
5438 LONG64 *aTimestamp,
5439 com::Utf8Str &aFlags)
5440{
5441#ifndef VBOX_WITH_GUEST_PROPS
5442 ReturnComNotImplemented();
5443#else // VBOX_WITH_GUEST_PROPS
5444
5445 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5446
5447 if (rc == E_ACCESSDENIED)
5448 /* The VM is not running or the service is not (yet) accessible */
5449 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5450 return rc;
5451#endif // VBOX_WITH_GUEST_PROPS
5452}
5453
5454HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5455{
5456 LONG64 dummyTimestamp;
5457 com::Utf8Str dummyFlags;
5458 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5459 return rc;
5460
5461}
5462HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5463{
5464 com::Utf8Str dummyFlags;
5465 com::Utf8Str dummyValue;
5466 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5467 return rc;
5468}
5469
5470#ifdef VBOX_WITH_GUEST_PROPS
5471/**
5472 * Set a guest property in VBoxSVC's internal structures.
5473 */
5474HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5475 const com::Utf8Str &aFlags, bool fDelete)
5476{
5477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5478 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5479 if (FAILED(rc)) return rc;
5480
5481 try
5482 {
5483 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5484 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5485 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5486
5487 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5488 if (it == mHWData->mGuestProperties.end())
5489 {
5490 if (!fDelete)
5491 {
5492 i_setModified(IsModified_MachineData);
5493 mHWData.backupEx();
5494
5495 RTTIMESPEC time;
5496 HWData::GuestProperty prop;
5497 prop.strValue = Bstr(aValue).raw();
5498 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5499 prop.mFlags = fFlags;
5500 mHWData->mGuestProperties[aName] = prop;
5501 }
5502 }
5503 else
5504 {
5505 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5506 {
5507 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5508 }
5509 else
5510 {
5511 i_setModified(IsModified_MachineData);
5512 mHWData.backupEx();
5513
5514 /* The backupEx() operation invalidates our iterator,
5515 * so get a new one. */
5516 it = mHWData->mGuestProperties.find(aName);
5517 Assert(it != mHWData->mGuestProperties.end());
5518
5519 if (!fDelete)
5520 {
5521 RTTIMESPEC time;
5522 it->second.strValue = aValue;
5523 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5524 it->second.mFlags = fFlags;
5525 }
5526 else
5527 mHWData->mGuestProperties.erase(it);
5528 }
5529 }
5530
5531 if (SUCCEEDED(rc))
5532 {
5533 alock.release();
5534
5535 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5536 }
5537 }
5538 catch (std::bad_alloc &)
5539 {
5540 rc = E_OUTOFMEMORY;
5541 }
5542
5543 return rc;
5544}
5545
5546/**
5547 * Set a property on the VM that that property belongs to.
5548 * @returns E_ACCESSDENIED if the VM process is not available or not
5549 * currently handling queries and the setting should then be done in
5550 * VBoxSVC.
5551 */
5552HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5553 const com::Utf8Str &aFlags, bool fDelete)
5554{
5555 HRESULT rc;
5556
5557 try
5558 {
5559 ComPtr<IInternalSessionControl> directControl;
5560 {
5561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5562 if (mData->mSession.mLockType == LockType_VM)
5563 directControl = mData->mSession.mDirectControl;
5564 }
5565
5566 Bstr dummy1; /* will not be changed (setter) */
5567 Bstr dummy2; /* will not be changed (setter) */
5568 LONG64 dummy64;
5569 if (!directControl)
5570 rc = E_ACCESSDENIED;
5571 else
5572 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5573 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5574 fDelete ? 2 : 1 /* accessMode */,
5575 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5576 }
5577 catch (std::bad_alloc &)
5578 {
5579 rc = E_OUTOFMEMORY;
5580 }
5581
5582 return rc;
5583}
5584#endif // VBOX_WITH_GUEST_PROPS
5585
5586HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5587 const com::Utf8Str &aFlags)
5588{
5589#ifndef VBOX_WITH_GUEST_PROPS
5590 ReturnComNotImplemented();
5591#else // VBOX_WITH_GUEST_PROPS
5592 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5593 if (rc == E_ACCESSDENIED)
5594 /* The VM is not running or the service is not (yet) accessible */
5595 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5596 return rc;
5597#endif // VBOX_WITH_GUEST_PROPS
5598}
5599
5600HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5601{
5602 return setGuestProperty(aProperty, aValue, "");
5603}
5604
5605HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5606{
5607#ifndef VBOX_WITH_GUEST_PROPS
5608 ReturnComNotImplemented();
5609#else // VBOX_WITH_GUEST_PROPS
5610 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5611 if (rc == E_ACCESSDENIED)
5612 /* The VM is not running or the service is not (yet) accessible */
5613 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5614 return rc;
5615#endif // VBOX_WITH_GUEST_PROPS
5616}
5617
5618#ifdef VBOX_WITH_GUEST_PROPS
5619/**
5620 * Enumerate the guest properties in VBoxSVC's internal structures.
5621 */
5622HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5623 std::vector<com::Utf8Str> &aNames,
5624 std::vector<com::Utf8Str> &aValues,
5625 std::vector<LONG64> &aTimestamps,
5626 std::vector<com::Utf8Str> &aFlags)
5627{
5628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5629 Utf8Str strPatterns(aPatterns);
5630
5631 /*
5632 * Look for matching patterns and build up a list.
5633 */
5634 HWData::GuestPropertyMap propMap;
5635 for (HWData::GuestPropertyMap::const_iterator
5636 it = mHWData->mGuestProperties.begin();
5637 it != mHWData->mGuestProperties.end();
5638 ++it)
5639 {
5640 if ( strPatterns.isEmpty()
5641 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5642 RTSTR_MAX,
5643 it->first.c_str(),
5644 RTSTR_MAX,
5645 NULL)
5646 )
5647 propMap.insert(*it);
5648 }
5649
5650 alock.release();
5651
5652 /*
5653 * And build up the arrays for returning the property information.
5654 */
5655 size_t cEntries = propMap.size();
5656
5657 aNames.resize(cEntries);
5658 aValues.resize(cEntries);
5659 aTimestamps.resize(cEntries);
5660 aFlags.resize(cEntries);
5661
5662 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5663 size_t i = 0;
5664 for (HWData::GuestPropertyMap::const_iterator
5665 it = propMap.begin();
5666 it != propMap.end();
5667 ++it, ++i)
5668 {
5669 aNames[i] = it->first;
5670 aValues[i] = it->second.strValue;
5671 aTimestamps[i] = it->second.mTimestamp;
5672 GuestPropWriteFlags(it->second.mFlags, szFlags);
5673 aFlags[i] = Utf8Str(szFlags);
5674 }
5675
5676 return S_OK;
5677}
5678
5679/**
5680 * Enumerate the properties managed by a VM.
5681 * @returns E_ACCESSDENIED if the VM process is not available or not
5682 * currently handling queries and the setting should then be done in
5683 * VBoxSVC.
5684 */
5685HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5686 std::vector<com::Utf8Str> &aNames,
5687 std::vector<com::Utf8Str> &aValues,
5688 std::vector<LONG64> &aTimestamps,
5689 std::vector<com::Utf8Str> &aFlags)
5690{
5691 HRESULT rc;
5692 ComPtr<IInternalSessionControl> directControl;
5693 {
5694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5695 if (mData->mSession.mLockType == LockType_VM)
5696 directControl = mData->mSession.mDirectControl;
5697 }
5698
5699 com::SafeArray<BSTR> bNames;
5700 com::SafeArray<BSTR> bValues;
5701 com::SafeArray<LONG64> bTimestamps;
5702 com::SafeArray<BSTR> bFlags;
5703
5704 if (!directControl)
5705 rc = E_ACCESSDENIED;
5706 else
5707 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5708 ComSafeArrayAsOutParam(bNames),
5709 ComSafeArrayAsOutParam(bValues),
5710 ComSafeArrayAsOutParam(bTimestamps),
5711 ComSafeArrayAsOutParam(bFlags));
5712 size_t i;
5713 aNames.resize(bNames.size());
5714 for (i = 0; i < bNames.size(); ++i)
5715 aNames[i] = Utf8Str(bNames[i]);
5716 aValues.resize(bValues.size());
5717 for (i = 0; i < bValues.size(); ++i)
5718 aValues[i] = Utf8Str(bValues[i]);
5719 aTimestamps.resize(bTimestamps.size());
5720 for (i = 0; i < bTimestamps.size(); ++i)
5721 aTimestamps[i] = bTimestamps[i];
5722 aFlags.resize(bFlags.size());
5723 for (i = 0; i < bFlags.size(); ++i)
5724 aFlags[i] = Utf8Str(bFlags[i]);
5725
5726 return rc;
5727}
5728#endif // VBOX_WITH_GUEST_PROPS
5729HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5730 std::vector<com::Utf8Str> &aNames,
5731 std::vector<com::Utf8Str> &aValues,
5732 std::vector<LONG64> &aTimestamps,
5733 std::vector<com::Utf8Str> &aFlags)
5734{
5735#ifndef VBOX_WITH_GUEST_PROPS
5736 ReturnComNotImplemented();
5737#else // VBOX_WITH_GUEST_PROPS
5738
5739 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5740
5741 if (rc == E_ACCESSDENIED)
5742 /* The VM is not running or the service is not (yet) accessible */
5743 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5744 return rc;
5745#endif // VBOX_WITH_GUEST_PROPS
5746}
5747
5748HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5749 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5750{
5751 MediumAttachmentList atts;
5752
5753 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5754 if (FAILED(rc)) return rc;
5755
5756 aMediumAttachments.resize(atts.size());
5757 size_t i = 0;
5758 for (MediumAttachmentList::const_iterator
5759 it = atts.begin();
5760 it != atts.end();
5761 ++it, ++i)
5762 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5763
5764 return S_OK;
5765}
5766
5767HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5768 LONG aControllerPort,
5769 LONG aDevice,
5770 ComPtr<IMediumAttachment> &aAttachment)
5771{
5772 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5773 aName.c_str(), aControllerPort, aDevice));
5774
5775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5776
5777 aAttachment = NULL;
5778
5779 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5780 aName,
5781 aControllerPort,
5782 aDevice);
5783 if (pAttach.isNull())
5784 return setError(VBOX_E_OBJECT_NOT_FOUND,
5785 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5786 aDevice, aControllerPort, aName.c_str());
5787
5788 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5789
5790 return S_OK;
5791}
5792
5793
5794HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5795 StorageBus_T aConnectionType,
5796 ComPtr<IStorageController> &aController)
5797{
5798 if ( (aConnectionType <= StorageBus_Null)
5799 || (aConnectionType > StorageBus_VirtioSCSI))
5800 return setError(E_INVALIDARG,
5801 tr("Invalid connection type: %d"),
5802 aConnectionType);
5803
5804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5805
5806 HRESULT rc = i_checkStateDependency(MutableStateDep);
5807 if (FAILED(rc)) return rc;
5808
5809 /* try to find one with the name first. */
5810 ComObjPtr<StorageController> ctrl;
5811
5812 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5813 if (SUCCEEDED(rc))
5814 return setError(VBOX_E_OBJECT_IN_USE,
5815 tr("Storage controller named '%s' already exists"),
5816 aName.c_str());
5817
5818 ctrl.createObject();
5819
5820 /* get a new instance number for the storage controller */
5821 ULONG ulInstance = 0;
5822 bool fBootable = true;
5823 for (StorageControllerList::const_iterator
5824 it = mStorageControllers->begin();
5825 it != mStorageControllers->end();
5826 ++it)
5827 {
5828 if ((*it)->i_getStorageBus() == aConnectionType)
5829 {
5830 ULONG ulCurInst = (*it)->i_getInstance();
5831
5832 if (ulCurInst >= ulInstance)
5833 ulInstance = ulCurInst + 1;
5834
5835 /* Only one controller of each type can be marked as bootable. */
5836 if ((*it)->i_getBootable())
5837 fBootable = false;
5838 }
5839 }
5840
5841 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5842 if (FAILED(rc)) return rc;
5843
5844 i_setModified(IsModified_Storage);
5845 mStorageControllers.backup();
5846 mStorageControllers->push_back(ctrl);
5847
5848 ctrl.queryInterfaceTo(aController.asOutParam());
5849
5850 /* inform the direct session if any */
5851 alock.release();
5852 i_onStorageControllerChange(i_getId(), aName);
5853
5854 return S_OK;
5855}
5856
5857HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5858 ComPtr<IStorageController> &aStorageController)
5859{
5860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5861
5862 ComObjPtr<StorageController> ctrl;
5863
5864 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5865 if (SUCCEEDED(rc))
5866 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5867
5868 return rc;
5869}
5870
5871HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5872 ULONG aInstance,
5873 ComPtr<IStorageController> &aStorageController)
5874{
5875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5876
5877 for (StorageControllerList::const_iterator
5878 it = mStorageControllers->begin();
5879 it != mStorageControllers->end();
5880 ++it)
5881 {
5882 if ( (*it)->i_getStorageBus() == aConnectionType
5883 && (*it)->i_getInstance() == aInstance)
5884 {
5885 (*it).queryInterfaceTo(aStorageController.asOutParam());
5886 return S_OK;
5887 }
5888 }
5889
5890 return setError(VBOX_E_OBJECT_NOT_FOUND,
5891 tr("Could not find a storage controller with instance number '%lu'"),
5892 aInstance);
5893}
5894
5895HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5896{
5897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5898
5899 HRESULT rc = i_checkStateDependency(MutableStateDep);
5900 if (FAILED(rc)) return rc;
5901
5902 ComObjPtr<StorageController> ctrl;
5903
5904 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5905 if (SUCCEEDED(rc))
5906 {
5907 /* Ensure that only one controller of each type is marked as bootable. */
5908 if (aBootable == TRUE)
5909 {
5910 for (StorageControllerList::const_iterator
5911 it = mStorageControllers->begin();
5912 it != mStorageControllers->end();
5913 ++it)
5914 {
5915 ComObjPtr<StorageController> aCtrl = (*it);
5916
5917 if ( (aCtrl->i_getName() != aName)
5918 && aCtrl->i_getBootable() == TRUE
5919 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5920 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5921 {
5922 aCtrl->i_setBootable(FALSE);
5923 break;
5924 }
5925 }
5926 }
5927
5928 if (SUCCEEDED(rc))
5929 {
5930 ctrl->i_setBootable(aBootable);
5931 i_setModified(IsModified_Storage);
5932 }
5933 }
5934
5935 if (SUCCEEDED(rc))
5936 {
5937 /* inform the direct session if any */
5938 alock.release();
5939 i_onStorageControllerChange(i_getId(), aName);
5940 }
5941
5942 return rc;
5943}
5944
5945HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5946{
5947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5948
5949 HRESULT rc = i_checkStateDependency(MutableStateDep);
5950 if (FAILED(rc)) return rc;
5951
5952 ComObjPtr<StorageController> ctrl;
5953 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5954 if (FAILED(rc)) return rc;
5955
5956 MediumAttachmentList llDetachedAttachments;
5957 {
5958 /* find all attached devices to the appropriate storage controller and detach them all */
5959 // make a temporary list because detachDevice invalidates iterators into
5960 // mMediumAttachments
5961 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5962
5963 for (MediumAttachmentList::const_iterator
5964 it = llAttachments2.begin();
5965 it != llAttachments2.end();
5966 ++it)
5967 {
5968 MediumAttachment *pAttachTemp = *it;
5969
5970 AutoCaller localAutoCaller(pAttachTemp);
5971 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5972
5973 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5974
5975 if (pAttachTemp->i_getControllerName() == aName)
5976 {
5977 llDetachedAttachments.push_back(pAttachTemp);
5978 rc = i_detachDevice(pAttachTemp, alock, NULL);
5979 if (FAILED(rc)) return rc;
5980 }
5981 }
5982 }
5983
5984 /* send event about detached devices before removing parent controller */
5985 for (MediumAttachmentList::const_iterator
5986 it = llDetachedAttachments.begin();
5987 it != llDetachedAttachments.end();
5988 ++it)
5989 {
5990 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5991 }
5992
5993 /* We can remove it now. */
5994 i_setModified(IsModified_Storage);
5995 mStorageControllers.backup();
5996
5997 ctrl->i_unshare();
5998
5999 mStorageControllers->remove(ctrl);
6000
6001 /* inform the direct session if any */
6002 alock.release();
6003 i_onStorageControllerChange(i_getId(), aName);
6004
6005 return S_OK;
6006}
6007
6008HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6009 ComPtr<IUSBController> &aController)
6010{
6011 if ( (aType <= USBControllerType_Null)
6012 || (aType >= USBControllerType_Last))
6013 return setError(E_INVALIDARG,
6014 tr("Invalid USB controller type: %d"),
6015 aType);
6016
6017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6018
6019 HRESULT rc = i_checkStateDependency(MutableStateDep);
6020 if (FAILED(rc)) return rc;
6021
6022 /* try to find one with the same type first. */
6023 ComObjPtr<USBController> ctrl;
6024
6025 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6026 if (SUCCEEDED(rc))
6027 return setError(VBOX_E_OBJECT_IN_USE,
6028 tr("USB controller named '%s' already exists"),
6029 aName.c_str());
6030
6031 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6032 ULONG maxInstances;
6033 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6034 if (FAILED(rc))
6035 return rc;
6036
6037 ULONG cInstances = i_getUSBControllerCountByType(aType);
6038 if (cInstances >= maxInstances)
6039 return setError(E_INVALIDARG,
6040 tr("Too many USB controllers of this type"));
6041
6042 ctrl.createObject();
6043
6044 rc = ctrl->init(this, aName, aType);
6045 if (FAILED(rc)) return rc;
6046
6047 i_setModified(IsModified_USB);
6048 mUSBControllers.backup();
6049 mUSBControllers->push_back(ctrl);
6050
6051 ctrl.queryInterfaceTo(aController.asOutParam());
6052
6053 /* inform the direct session if any */
6054 alock.release();
6055 i_onUSBControllerChange();
6056
6057 return S_OK;
6058}
6059
6060HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6061{
6062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6063
6064 ComObjPtr<USBController> ctrl;
6065
6066 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6067 if (SUCCEEDED(rc))
6068 ctrl.queryInterfaceTo(aController.asOutParam());
6069
6070 return rc;
6071}
6072
6073HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6074 ULONG *aControllers)
6075{
6076 if ( (aType <= USBControllerType_Null)
6077 || (aType >= USBControllerType_Last))
6078 return setError(E_INVALIDARG,
6079 tr("Invalid USB controller type: %d"),
6080 aType);
6081
6082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6083
6084 ComObjPtr<USBController> ctrl;
6085
6086 *aControllers = i_getUSBControllerCountByType(aType);
6087
6088 return S_OK;
6089}
6090
6091HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6092{
6093
6094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6095
6096 HRESULT rc = i_checkStateDependency(MutableStateDep);
6097 if (FAILED(rc)) return rc;
6098
6099 ComObjPtr<USBController> ctrl;
6100 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6101 if (FAILED(rc)) return rc;
6102
6103 i_setModified(IsModified_USB);
6104 mUSBControllers.backup();
6105
6106 ctrl->i_unshare();
6107
6108 mUSBControllers->remove(ctrl);
6109
6110 /* inform the direct session if any */
6111 alock.release();
6112 i_onUSBControllerChange();
6113
6114 return S_OK;
6115}
6116
6117HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6118 ULONG *aOriginX,
6119 ULONG *aOriginY,
6120 ULONG *aWidth,
6121 ULONG *aHeight,
6122 BOOL *aEnabled)
6123{
6124 uint32_t u32OriginX= 0;
6125 uint32_t u32OriginY= 0;
6126 uint32_t u32Width = 0;
6127 uint32_t u32Height = 0;
6128 uint16_t u16Flags = 0;
6129
6130 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6131 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6132 if (RT_FAILURE(vrc))
6133 {
6134#ifdef RT_OS_WINDOWS
6135 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6136 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6137 * So just assign fEnable to TRUE again.
6138 * The right fix would be to change GUI API wrappers to make sure that parameters
6139 * are changed only if API succeeds.
6140 */
6141 *aEnabled = TRUE;
6142#endif
6143 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6144 tr("Saved guest size is not available (%Rrc)"),
6145 vrc);
6146 }
6147
6148 *aOriginX = u32OriginX;
6149 *aOriginY = u32OriginY;
6150 *aWidth = u32Width;
6151 *aHeight = u32Height;
6152 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6153
6154 return S_OK;
6155}
6156
6157HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6158 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6159{
6160 if (aScreenId != 0)
6161 return E_NOTIMPL;
6162
6163 if ( aBitmapFormat != BitmapFormat_BGR0
6164 && aBitmapFormat != BitmapFormat_BGRA
6165 && aBitmapFormat != BitmapFormat_RGBA
6166 && aBitmapFormat != BitmapFormat_PNG)
6167 return setError(E_NOTIMPL,
6168 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6169
6170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6171
6172 uint8_t *pu8Data = NULL;
6173 uint32_t cbData = 0;
6174 uint32_t u32Width = 0;
6175 uint32_t u32Height = 0;
6176
6177 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6178
6179 if (RT_FAILURE(vrc))
6180 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6181 tr("Saved thumbnail data is not available (%Rrc)"),
6182 vrc);
6183
6184 HRESULT hr = S_OK;
6185
6186 *aWidth = u32Width;
6187 *aHeight = u32Height;
6188
6189 if (cbData > 0)
6190 {
6191 /* Convert pixels to the format expected by the API caller. */
6192 if (aBitmapFormat == BitmapFormat_BGR0)
6193 {
6194 /* [0] B, [1] G, [2] R, [3] 0. */
6195 aData.resize(cbData);
6196 memcpy(&aData.front(), pu8Data, cbData);
6197 }
6198 else if (aBitmapFormat == BitmapFormat_BGRA)
6199 {
6200 /* [0] B, [1] G, [2] R, [3] A. */
6201 aData.resize(cbData);
6202 for (uint32_t i = 0; i < cbData; i += 4)
6203 {
6204 aData[i] = pu8Data[i];
6205 aData[i + 1] = pu8Data[i + 1];
6206 aData[i + 2] = pu8Data[i + 2];
6207 aData[i + 3] = 0xff;
6208 }
6209 }
6210 else if (aBitmapFormat == BitmapFormat_RGBA)
6211 {
6212 /* [0] R, [1] G, [2] B, [3] A. */
6213 aData.resize(cbData);
6214 for (uint32_t i = 0; i < cbData; i += 4)
6215 {
6216 aData[i] = pu8Data[i + 2];
6217 aData[i + 1] = pu8Data[i + 1];
6218 aData[i + 2] = pu8Data[i];
6219 aData[i + 3] = 0xff;
6220 }
6221 }
6222 else if (aBitmapFormat == BitmapFormat_PNG)
6223 {
6224 uint8_t *pu8PNG = NULL;
6225 uint32_t cbPNG = 0;
6226 uint32_t cxPNG = 0;
6227 uint32_t cyPNG = 0;
6228
6229 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6230
6231 if (RT_SUCCESS(vrc))
6232 {
6233 aData.resize(cbPNG);
6234 if (cbPNG)
6235 memcpy(&aData.front(), pu8PNG, cbPNG);
6236 }
6237 else
6238 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6239 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6240 vrc);
6241
6242 RTMemFree(pu8PNG);
6243 }
6244 }
6245
6246 freeSavedDisplayScreenshot(pu8Data);
6247
6248 return hr;
6249}
6250
6251HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6252 ULONG *aWidth,
6253 ULONG *aHeight,
6254 std::vector<BitmapFormat_T> &aBitmapFormats)
6255{
6256 if (aScreenId != 0)
6257 return E_NOTIMPL;
6258
6259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6260
6261 uint8_t *pu8Data = NULL;
6262 uint32_t cbData = 0;
6263 uint32_t u32Width = 0;
6264 uint32_t u32Height = 0;
6265
6266 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6267
6268 if (RT_FAILURE(vrc))
6269 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6270 tr("Saved screenshot data is not available (%Rrc)"),
6271 vrc);
6272
6273 *aWidth = u32Width;
6274 *aHeight = u32Height;
6275 aBitmapFormats.resize(1);
6276 aBitmapFormats[0] = BitmapFormat_PNG;
6277
6278 freeSavedDisplayScreenshot(pu8Data);
6279
6280 return S_OK;
6281}
6282
6283HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6284 BitmapFormat_T aBitmapFormat,
6285 ULONG *aWidth,
6286 ULONG *aHeight,
6287 std::vector<BYTE> &aData)
6288{
6289 if (aScreenId != 0)
6290 return E_NOTIMPL;
6291
6292 if (aBitmapFormat != BitmapFormat_PNG)
6293 return E_NOTIMPL;
6294
6295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6296
6297 uint8_t *pu8Data = NULL;
6298 uint32_t cbData = 0;
6299 uint32_t u32Width = 0;
6300 uint32_t u32Height = 0;
6301
6302 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6303
6304 if (RT_FAILURE(vrc))
6305 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6306 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6307 vrc);
6308
6309 *aWidth = u32Width;
6310 *aHeight = u32Height;
6311
6312 aData.resize(cbData);
6313 if (cbData)
6314 memcpy(&aData.front(), pu8Data, cbData);
6315
6316 freeSavedDisplayScreenshot(pu8Data);
6317
6318 return S_OK;
6319}
6320
6321HRESULT Machine::hotPlugCPU(ULONG aCpu)
6322{
6323 HRESULT rc = S_OK;
6324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6325
6326 if (!mHWData->mCPUHotPlugEnabled)
6327 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6328
6329 if (aCpu >= mHWData->mCPUCount)
6330 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6331
6332 if (mHWData->mCPUAttached[aCpu])
6333 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6334
6335 alock.release();
6336 rc = i_onCPUChange(aCpu, false);
6337 alock.acquire();
6338 if (FAILED(rc)) return rc;
6339
6340 i_setModified(IsModified_MachineData);
6341 mHWData.backup();
6342 mHWData->mCPUAttached[aCpu] = true;
6343
6344 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6345 if (Global::IsOnline(mData->mMachineState))
6346 i_saveSettings(NULL);
6347
6348 return S_OK;
6349}
6350
6351HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6352{
6353 HRESULT rc = S_OK;
6354
6355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6356
6357 if (!mHWData->mCPUHotPlugEnabled)
6358 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6359
6360 if (aCpu >= SchemaDefs::MaxCPUCount)
6361 return setError(E_INVALIDARG,
6362 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6363 SchemaDefs::MaxCPUCount);
6364
6365 if (!mHWData->mCPUAttached[aCpu])
6366 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6367
6368 /* CPU 0 can't be detached */
6369 if (aCpu == 0)
6370 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6371
6372 alock.release();
6373 rc = i_onCPUChange(aCpu, true);
6374 alock.acquire();
6375 if (FAILED(rc)) return rc;
6376
6377 i_setModified(IsModified_MachineData);
6378 mHWData.backup();
6379 mHWData->mCPUAttached[aCpu] = false;
6380
6381 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6382 if (Global::IsOnline(mData->mMachineState))
6383 i_saveSettings(NULL);
6384
6385 return S_OK;
6386}
6387
6388HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6389{
6390 *aAttached = false;
6391
6392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6393
6394 /* If hotplug is enabled the CPU is always enabled. */
6395 if (!mHWData->mCPUHotPlugEnabled)
6396 {
6397 if (aCpu < mHWData->mCPUCount)
6398 *aAttached = true;
6399 }
6400 else
6401 {
6402 if (aCpu < SchemaDefs::MaxCPUCount)
6403 *aAttached = mHWData->mCPUAttached[aCpu];
6404 }
6405
6406 return S_OK;
6407}
6408
6409HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6410{
6411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6412
6413 Utf8Str log = i_getLogFilename(aIdx);
6414 if (!RTFileExists(log.c_str()))
6415 log.setNull();
6416 aFilename = log;
6417
6418 return S_OK;
6419}
6420
6421HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6422{
6423 if (aSize < 0)
6424 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6425
6426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6427
6428 HRESULT rc = S_OK;
6429 Utf8Str log = i_getLogFilename(aIdx);
6430
6431 /* do not unnecessarily hold the lock while doing something which does
6432 * not need the lock and potentially takes a long time. */
6433 alock.release();
6434
6435 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6436 * keeps the SOAP reply size under 1M for the webservice (we're using
6437 * base64 encoded strings for binary data for years now, avoiding the
6438 * expansion of each byte array element to approx. 25 bytes of XML. */
6439 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6440 aData.resize(cbData);
6441
6442 RTFILE LogFile;
6443 int vrc = RTFileOpen(&LogFile, log.c_str(),
6444 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6445 if (RT_SUCCESS(vrc))
6446 {
6447 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6448 if (RT_SUCCESS(vrc))
6449 aData.resize(cbData);
6450 else
6451 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6452 tr("Could not read log file '%s' (%Rrc)"),
6453 log.c_str(), vrc);
6454 RTFileClose(LogFile);
6455 }
6456 else
6457 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6458 tr("Could not open log file '%s' (%Rrc)"),
6459 log.c_str(), vrc);
6460
6461 if (FAILED(rc))
6462 aData.resize(0);
6463
6464 return rc;
6465}
6466
6467
6468/**
6469 * Currently this method doesn't attach device to the running VM,
6470 * just makes sure it's plugged on next VM start.
6471 */
6472HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6473{
6474 // lock scope
6475 {
6476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6477
6478 HRESULT rc = i_checkStateDependency(MutableStateDep);
6479 if (FAILED(rc)) return rc;
6480
6481 ChipsetType_T aChipset = ChipsetType_PIIX3;
6482 COMGETTER(ChipsetType)(&aChipset);
6483
6484 if (aChipset != ChipsetType_ICH9)
6485 {
6486 return setError(E_INVALIDARG,
6487 tr("Host PCI attachment only supported with ICH9 chipset"));
6488 }
6489
6490 // check if device with this host PCI address already attached
6491 for (HWData::PCIDeviceAssignmentList::const_iterator
6492 it = mHWData->mPCIDeviceAssignments.begin();
6493 it != mHWData->mPCIDeviceAssignments.end();
6494 ++it)
6495 {
6496 LONG iHostAddress = -1;
6497 ComPtr<PCIDeviceAttachment> pAttach;
6498 pAttach = *it;
6499 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6500 if (iHostAddress == aHostAddress)
6501 return setError(E_INVALIDARG,
6502 tr("Device with host PCI address already attached to this VM"));
6503 }
6504
6505 ComObjPtr<PCIDeviceAttachment> pda;
6506 char name[32];
6507
6508 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6509 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6510 pda.createObject();
6511 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6512 i_setModified(IsModified_MachineData);
6513 mHWData.backup();
6514 mHWData->mPCIDeviceAssignments.push_back(pda);
6515 }
6516
6517 return S_OK;
6518}
6519
6520/**
6521 * Currently this method doesn't detach device from the running VM,
6522 * just makes sure it's not plugged on next VM start.
6523 */
6524HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6525{
6526 ComObjPtr<PCIDeviceAttachment> pAttach;
6527 bool fRemoved = false;
6528 HRESULT rc;
6529
6530 // lock scope
6531 {
6532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6533
6534 rc = i_checkStateDependency(MutableStateDep);
6535 if (FAILED(rc)) return rc;
6536
6537 for (HWData::PCIDeviceAssignmentList::const_iterator
6538 it = mHWData->mPCIDeviceAssignments.begin();
6539 it != mHWData->mPCIDeviceAssignments.end();
6540 ++it)
6541 {
6542 LONG iHostAddress = -1;
6543 pAttach = *it;
6544 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6545 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6546 {
6547 i_setModified(IsModified_MachineData);
6548 mHWData.backup();
6549 mHWData->mPCIDeviceAssignments.remove(pAttach);
6550 fRemoved = true;
6551 break;
6552 }
6553 }
6554 }
6555
6556
6557 /* Fire event outside of the lock */
6558 if (fRemoved)
6559 {
6560 Assert(!pAttach.isNull());
6561 ComPtr<IEventSource> es;
6562 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6563 Assert(SUCCEEDED(rc));
6564 Bstr mid;
6565 rc = this->COMGETTER(Id)(mid.asOutParam());
6566 Assert(SUCCEEDED(rc));
6567 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6568 }
6569
6570 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6571 tr("No host PCI device %08x attached"),
6572 aHostAddress
6573 );
6574}
6575
6576HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6577{
6578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6579
6580 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6581 size_t i = 0;
6582 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6583 it = mHWData->mPCIDeviceAssignments.begin();
6584 it != mHWData->mPCIDeviceAssignments.end();
6585 ++it, ++i)
6586 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6587
6588 return S_OK;
6589}
6590
6591HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6592{
6593 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6594
6595 return S_OK;
6596}
6597
6598HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6599{
6600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6601
6602 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6603
6604 return S_OK;
6605}
6606
6607HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6608{
6609 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6610 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6611 if (SUCCEEDED(hrc))
6612 {
6613 hrc = mHWData.backupEx();
6614 if (SUCCEEDED(hrc))
6615 {
6616 i_setModified(IsModified_MachineData);
6617 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6618 }
6619 }
6620 return hrc;
6621}
6622
6623HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6624{
6625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6626 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6627 return S_OK;
6628}
6629
6630HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6631{
6632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6633 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6634 if (SUCCEEDED(hrc))
6635 {
6636 hrc = mHWData.backupEx();
6637 if (SUCCEEDED(hrc))
6638 {
6639 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6640 if (SUCCEEDED(hrc))
6641 i_setModified(IsModified_MachineData);
6642 }
6643 }
6644 return hrc;
6645}
6646
6647HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6648{
6649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6650
6651 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6652
6653 return S_OK;
6654}
6655
6656HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6657{
6658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6659 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6660 if (SUCCEEDED(hrc))
6661 {
6662 hrc = mHWData.backupEx();
6663 if (SUCCEEDED(hrc))
6664 {
6665 i_setModified(IsModified_MachineData);
6666 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6667 }
6668 }
6669 return hrc;
6670}
6671
6672HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6673{
6674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6675
6676 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6677
6678 return S_OK;
6679}
6680
6681HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6682{
6683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6684
6685 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6686 if ( SUCCEEDED(hrc)
6687 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6688 {
6689 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6690 int vrc;
6691
6692 if (aAutostartEnabled)
6693 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6694 else
6695 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6696
6697 if (RT_SUCCESS(vrc))
6698 {
6699 hrc = mHWData.backupEx();
6700 if (SUCCEEDED(hrc))
6701 {
6702 i_setModified(IsModified_MachineData);
6703 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6704 }
6705 }
6706 else if (vrc == VERR_NOT_SUPPORTED)
6707 hrc = setError(VBOX_E_NOT_SUPPORTED,
6708 tr("The VM autostart feature is not supported on this platform"));
6709 else if (vrc == VERR_PATH_NOT_FOUND)
6710 hrc = setError(E_FAIL,
6711 tr("The path to the autostart database is not set"));
6712 else
6713 hrc = setError(E_UNEXPECTED,
6714 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6715 aAutostartEnabled ? "Adding" : "Removing",
6716 mUserData->s.strName.c_str(), vrc);
6717 }
6718 return hrc;
6719}
6720
6721HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6722{
6723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6724
6725 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6726
6727 return S_OK;
6728}
6729
6730HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6731{
6732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6733 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6734 if (SUCCEEDED(hrc))
6735 {
6736 hrc = mHWData.backupEx();
6737 if (SUCCEEDED(hrc))
6738 {
6739 i_setModified(IsModified_MachineData);
6740 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6741 }
6742 }
6743 return hrc;
6744}
6745
6746HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6747{
6748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6749
6750 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6751
6752 return S_OK;
6753}
6754
6755HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6756{
6757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6758 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6759 if ( SUCCEEDED(hrc)
6760 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6761 {
6762 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6763 int vrc;
6764
6765 if (aAutostopType != AutostopType_Disabled)
6766 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6767 else
6768 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6769
6770 if (RT_SUCCESS(vrc))
6771 {
6772 hrc = mHWData.backupEx();
6773 if (SUCCEEDED(hrc))
6774 {
6775 i_setModified(IsModified_MachineData);
6776 mHWData->mAutostart.enmAutostopType = aAutostopType;
6777 }
6778 }
6779 else if (vrc == VERR_NOT_SUPPORTED)
6780 hrc = setError(VBOX_E_NOT_SUPPORTED,
6781 tr("The VM autostop feature is not supported on this platform"));
6782 else if (vrc == VERR_PATH_NOT_FOUND)
6783 hrc = setError(E_FAIL,
6784 tr("The path to the autostart database is not set"));
6785 else
6786 hrc = setError(E_UNEXPECTED,
6787 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6788 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6789 mUserData->s.strName.c_str(), vrc);
6790 }
6791 return hrc;
6792}
6793
6794HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6795{
6796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6797
6798 aDefaultFrontend = mHWData->mDefaultFrontend;
6799
6800 return S_OK;
6801}
6802
6803HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6804{
6805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6806 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6807 if (SUCCEEDED(hrc))
6808 {
6809 hrc = mHWData.backupEx();
6810 if (SUCCEEDED(hrc))
6811 {
6812 i_setModified(IsModified_MachineData);
6813 mHWData->mDefaultFrontend = aDefaultFrontend;
6814 }
6815 }
6816 return hrc;
6817}
6818
6819HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6820{
6821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6822 size_t cbIcon = mUserData->s.ovIcon.size();
6823 aIcon.resize(cbIcon);
6824 if (cbIcon)
6825 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6826 return S_OK;
6827}
6828
6829HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6830{
6831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6832 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6833 if (SUCCEEDED(hrc))
6834 {
6835 i_setModified(IsModified_MachineData);
6836 mUserData.backup();
6837 size_t cbIcon = aIcon.size();
6838 mUserData->s.ovIcon.resize(cbIcon);
6839 if (cbIcon)
6840 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6841 }
6842 return hrc;
6843}
6844
6845HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6846{
6847#ifdef VBOX_WITH_USB
6848 *aUSBProxyAvailable = true;
6849#else
6850 *aUSBProxyAvailable = false;
6851#endif
6852 return S_OK;
6853}
6854
6855HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6856{
6857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6858
6859 *aVMProcessPriority = mUserData->s.enmVMPriority;
6860
6861 return S_OK;
6862}
6863
6864HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6865{
6866 RT_NOREF(aVMProcessPriority);
6867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6868 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6869 if (SUCCEEDED(hrc))
6870 {
6871 hrc = mUserData.backupEx();
6872 if (SUCCEEDED(hrc))
6873 {
6874 i_setModified(IsModified_MachineData);
6875 mUserData->s.enmVMPriority = aVMProcessPriority;
6876 }
6877 }
6878 alock.release();
6879 if (SUCCEEDED(hrc))
6880 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6881 return hrc;
6882}
6883
6884HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6885 ComPtr<IProgress> &aProgress)
6886{
6887 ComObjPtr<Progress> pP;
6888 Progress *ppP = pP;
6889 IProgress *iP = static_cast<IProgress *>(ppP);
6890 IProgress **pProgress = &iP;
6891
6892 IMachine *pTarget = aTarget;
6893
6894 /* Convert the options. */
6895 RTCList<CloneOptions_T> optList;
6896 if (aOptions.size())
6897 for (size_t i = 0; i < aOptions.size(); ++i)
6898 optList.append(aOptions[i]);
6899
6900 if (optList.contains(CloneOptions_Link))
6901 {
6902 if (!i_isSnapshotMachine())
6903 return setError(E_INVALIDARG,
6904 tr("Linked clone can only be created from a snapshot"));
6905 if (aMode != CloneMode_MachineState)
6906 return setError(E_INVALIDARG,
6907 tr("Linked clone can only be created for a single machine state"));
6908 }
6909 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6910
6911 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6912
6913 HRESULT rc = pWorker->start(pProgress);
6914
6915 pP = static_cast<Progress *>(*pProgress);
6916 pP.queryInterfaceTo(aProgress.asOutParam());
6917
6918 return rc;
6919
6920}
6921
6922HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6923 const com::Utf8Str &aType,
6924 ComPtr<IProgress> &aProgress)
6925{
6926 LogFlowThisFuncEnter();
6927
6928 ComObjPtr<Progress> ptrProgress;
6929 HRESULT hrc = ptrProgress.createObject();
6930 if (SUCCEEDED(hrc))
6931 {
6932 /* Initialize our worker task */
6933 MachineMoveVM *pTask = NULL;
6934 try
6935 {
6936 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6937 }
6938 catch (std::bad_alloc &)
6939 {
6940 return E_OUTOFMEMORY;
6941 }
6942
6943 hrc = pTask->init();//no exceptions are thrown
6944
6945 if (SUCCEEDED(hrc))
6946 {
6947 hrc = pTask->createThread();
6948 pTask = NULL; /* Consumed by createThread(). */
6949 if (SUCCEEDED(hrc))
6950 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6951 else
6952 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6953 }
6954 else
6955 delete pTask;
6956 }
6957
6958 LogFlowThisFuncLeave();
6959 return hrc;
6960
6961}
6962
6963HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6964{
6965 NOREF(aProgress);
6966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6967
6968 // This check should always fail.
6969 HRESULT rc = i_checkStateDependency(MutableStateDep);
6970 if (FAILED(rc)) return rc;
6971
6972 AssertFailedReturn(E_NOTIMPL);
6973}
6974
6975HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6976{
6977 NOREF(aSavedStateFile);
6978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6979
6980 // This check should always fail.
6981 HRESULT rc = i_checkStateDependency(MutableStateDep);
6982 if (FAILED(rc)) return rc;
6983
6984 AssertFailedReturn(E_NOTIMPL);
6985}
6986
6987HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6988{
6989 NOREF(aFRemoveFile);
6990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6991
6992 // This check should always fail.
6993 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
6994 if (FAILED(rc)) return rc;
6995
6996 AssertFailedReturn(E_NOTIMPL);
6997}
6998
6999// public methods for internal purposes
7000/////////////////////////////////////////////////////////////////////////////
7001
7002/**
7003 * Adds the given IsModified_* flag to the dirty flags of the machine.
7004 * This must be called either during i_loadSettings or under the machine write lock.
7005 * @param fl Flag
7006 * @param fAllowStateModification If state modifications are allowed.
7007 */
7008void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7009{
7010 mData->flModifications |= fl;
7011 if (fAllowStateModification && i_isStateModificationAllowed())
7012 mData->mCurrentStateModified = true;
7013}
7014
7015/**
7016 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7017 * care of the write locking.
7018 *
7019 * @param fModification The flag to add.
7020 * @param fAllowStateModification If state modifications are allowed.
7021 */
7022void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7023{
7024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7025 i_setModified(fModification, fAllowStateModification);
7026}
7027
7028/**
7029 * Saves the registry entry of this machine to the given configuration node.
7030 *
7031 * @param data Machine registry data.
7032 *
7033 * @note locks this object for reading.
7034 */
7035HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7036{
7037 AutoLimitedCaller autoCaller(this);
7038 AssertComRCReturnRC(autoCaller.rc());
7039
7040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7041
7042 data.uuid = mData->mUuid;
7043 data.strSettingsFile = mData->m_strConfigFile;
7044
7045 return S_OK;
7046}
7047
7048/**
7049 * Calculates the absolute path of the given path taking the directory of the
7050 * machine settings file as the current directory.
7051 *
7052 * @param strPath Path to calculate the absolute path for.
7053 * @param aResult Where to put the result (used only on success, can be the
7054 * same Utf8Str instance as passed in @a aPath).
7055 * @return IPRT result.
7056 *
7057 * @note Locks this object for reading.
7058 */
7059int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7060{
7061 AutoCaller autoCaller(this);
7062 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7063
7064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7065
7066 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7067
7068 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7069
7070 strSettingsDir.stripFilename();
7071 char szFolder[RTPATH_MAX];
7072 size_t cbFolder = sizeof(szFolder);
7073 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7074 if (RT_SUCCESS(vrc))
7075 aResult = szFolder;
7076
7077 return vrc;
7078}
7079
7080/**
7081 * Copies strSource to strTarget, making it relative to the machine folder
7082 * if it is a subdirectory thereof, or simply copying it otherwise.
7083 *
7084 * @param strSource Path to evaluate and copy.
7085 * @param strTarget Buffer to receive target path.
7086 *
7087 * @note Locks this object for reading.
7088 */
7089void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7090 Utf8Str &strTarget)
7091{
7092 AutoCaller autoCaller(this);
7093 AssertComRCReturn(autoCaller.rc(), (void)0);
7094
7095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7096
7097 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7098 // use strTarget as a temporary buffer to hold the machine settings dir
7099 strTarget = mData->m_strConfigFileFull;
7100 strTarget.stripFilename();
7101 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7102 {
7103 // is relative: then append what's left
7104 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7105 // for empty paths (only possible for subdirs) use "." to avoid
7106 // triggering default settings for not present config attributes.
7107 if (strTarget.isEmpty())
7108 strTarget = ".";
7109 }
7110 else
7111 // is not relative: then overwrite
7112 strTarget = strSource;
7113}
7114
7115/**
7116 * Returns the full path to the machine's log folder in the
7117 * \a aLogFolder argument.
7118 */
7119void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7120{
7121 AutoCaller autoCaller(this);
7122 AssertComRCReturnVoid(autoCaller.rc());
7123
7124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7125
7126 char szTmp[RTPATH_MAX];
7127 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7128 if (RT_SUCCESS(vrc))
7129 {
7130 if (szTmp[0] && !mUserData.isNull())
7131 {
7132 char szTmp2[RTPATH_MAX];
7133 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7134 if (RT_SUCCESS(vrc))
7135 aLogFolder.printf("%s%c%s",
7136 szTmp2,
7137 RTPATH_DELIMITER,
7138 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7139 }
7140 else
7141 vrc = VERR_PATH_IS_RELATIVE;
7142 }
7143
7144 if (RT_FAILURE(vrc))
7145 {
7146 // fallback if VBOX_USER_LOGHOME is not set or invalid
7147 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7148 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7149 aLogFolder.append(RTPATH_DELIMITER);
7150 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7151 }
7152}
7153
7154/**
7155 * Returns the full path to the machine's log file for an given index.
7156 */
7157Utf8Str Machine::i_getLogFilename(ULONG idx)
7158{
7159 Utf8Str logFolder;
7160 getLogFolder(logFolder);
7161 Assert(logFolder.length());
7162
7163 Utf8Str log;
7164 if (idx == 0)
7165 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7166#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7167 else if (idx == 1)
7168 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7169 else
7170 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7171#else
7172 else
7173 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7174#endif
7175 return log;
7176}
7177
7178/**
7179 * Returns the full path to the machine's hardened log file.
7180 */
7181Utf8Str Machine::i_getHardeningLogFilename(void)
7182{
7183 Utf8Str strFilename;
7184 getLogFolder(strFilename);
7185 Assert(strFilename.length());
7186 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7187 return strFilename;
7188}
7189
7190/**
7191 * Returns the default NVRAM filename based on the location of the VM config.
7192 * Note that this is a relative path.
7193 */
7194Utf8Str Machine::i_getDefaultNVRAMFilename()
7195{
7196 AutoCaller autoCaller(this);
7197 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7198
7199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7200
7201 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7202 || i_isSnapshotMachine())
7203 return Utf8Str::Empty;
7204
7205 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7206 strNVRAMFilePath.stripPath();
7207 strNVRAMFilePath.stripSuffix();
7208 strNVRAMFilePath += ".nvram";
7209
7210 return strNVRAMFilePath;
7211}
7212
7213/**
7214 * Returns the NVRAM filename for a new snapshot. This intentionally works
7215 * similarly to the saved state file naming. Note that this is usually
7216 * a relative path, unless the snapshot folder is absolute.
7217 */
7218Utf8Str Machine::i_getSnapshotNVRAMFilename()
7219{
7220 AutoCaller autoCaller(this);
7221 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7222
7223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7224
7225 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7226 return Utf8Str::Empty;
7227
7228 RTTIMESPEC ts;
7229 RTTimeNow(&ts);
7230 RTTIME time;
7231 RTTimeExplode(&time, &ts);
7232
7233 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7234 strNVRAMFilePath += RTPATH_DELIMITER;
7235 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7236 time.i32Year, time.u8Month, time.u8MonthDay,
7237 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7238
7239 return strNVRAMFilePath;
7240}
7241
7242/**
7243 * Composes a unique saved state filename based on the current system time. The filename is
7244 * granular to the second so this will work so long as no more than one snapshot is taken on
7245 * a machine per second.
7246 *
7247 * Before version 4.1, we used this formula for saved state files:
7248 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7249 * which no longer works because saved state files can now be shared between the saved state of the
7250 * "saved" machine and an online snapshot, and the following would cause problems:
7251 * 1) save machine
7252 * 2) create online snapshot from that machine state --> reusing saved state file
7253 * 3) save machine again --> filename would be reused, breaking the online snapshot
7254 *
7255 * So instead we now use a timestamp.
7256 *
7257 * @param strStateFilePath
7258 */
7259
7260void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7261{
7262 AutoCaller autoCaller(this);
7263 AssertComRCReturnVoid(autoCaller.rc());
7264
7265 {
7266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7267 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7268 }
7269
7270 RTTIMESPEC ts;
7271 RTTimeNow(&ts);
7272 RTTIME time;
7273 RTTimeExplode(&time, &ts);
7274
7275 strStateFilePath += RTPATH_DELIMITER;
7276 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7277 time.i32Year, time.u8Month, time.u8MonthDay,
7278 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7279}
7280
7281/**
7282 * Returns whether at least one USB controller is present for the VM.
7283 */
7284bool Machine::i_isUSBControllerPresent()
7285{
7286 AutoCaller autoCaller(this);
7287 AssertComRCReturn(autoCaller.rc(), false);
7288
7289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7290
7291 return (mUSBControllers->size() > 0);
7292}
7293
7294
7295/**
7296 * @note Locks this object for writing, calls the client process
7297 * (inside the lock).
7298 */
7299HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7300 const Utf8Str &strFrontend,
7301 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7302 ProgressProxy *aProgress)
7303{
7304 LogFlowThisFuncEnter();
7305
7306 AssertReturn(aControl, E_FAIL);
7307 AssertReturn(aProgress, E_FAIL);
7308 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7309
7310 AutoCaller autoCaller(this);
7311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7312
7313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7314
7315 if (!mData->mRegistered)
7316 return setError(E_UNEXPECTED,
7317 tr("The machine '%s' is not registered"),
7318 mUserData->s.strName.c_str());
7319
7320 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7321
7322 /* The process started when launching a VM with separate UI/VM processes is always
7323 * the UI process, i.e. needs special handling as it won't claim the session. */
7324 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7325
7326 if (fSeparate)
7327 {
7328 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7329 return setError(VBOX_E_INVALID_OBJECT_STATE,
7330 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7331 mUserData->s.strName.c_str());
7332 }
7333 else
7334 {
7335 if ( mData->mSession.mState == SessionState_Locked
7336 || mData->mSession.mState == SessionState_Spawning
7337 || mData->mSession.mState == SessionState_Unlocking)
7338 return setError(VBOX_E_INVALID_OBJECT_STATE,
7339 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7340 mUserData->s.strName.c_str());
7341
7342 /* may not be busy */
7343 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7344 }
7345
7346 /* Hardening logging */
7347#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7348 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7349 {
7350 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7351 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7352 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7353 {
7354 Utf8Str strStartupLogDir = strHardeningLogFile;
7355 strStartupLogDir.stripFilename();
7356 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7357 file without stripping the file. */
7358 }
7359 strSupHardeningLogArg.append(strHardeningLogFile);
7360
7361 /* Remove legacy log filename to avoid confusion. */
7362 Utf8Str strOldStartupLogFile;
7363 getLogFolder(strOldStartupLogFile);
7364 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7365 RTFileDelete(strOldStartupLogFile.c_str());
7366 }
7367#else
7368 Utf8Str strSupHardeningLogArg;
7369#endif
7370
7371 Utf8Str strAppOverride;
7372#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7373 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7374#endif
7375
7376 bool fUseVBoxSDS = false;
7377 Utf8Str strCanonicalName;
7378 if (false)
7379 { }
7380#ifdef VBOX_WITH_QTGUI
7381 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7382 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7383 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7384 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7385 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7386 {
7387 strCanonicalName = "GUI/Qt";
7388 fUseVBoxSDS = true;
7389 }
7390#endif
7391#ifdef VBOX_WITH_VBOXSDL
7392 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7393 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7394 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7395 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7396 {
7397 strCanonicalName = "GUI/SDL";
7398 fUseVBoxSDS = true;
7399 }
7400#endif
7401#ifdef VBOX_WITH_HEADLESS
7402 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7403 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7404 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7405 {
7406 strCanonicalName = "headless";
7407 }
7408#endif
7409 else
7410 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7411
7412 Utf8Str idStr = mData->mUuid.toString();
7413 Utf8Str const &strMachineName = mUserData->s.strName;
7414 RTPROCESS pid = NIL_RTPROCESS;
7415
7416#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7417 RT_NOREF(fUseVBoxSDS);
7418#else
7419 DWORD idCallerSession = ~(DWORD)0;
7420 if (fUseVBoxSDS)
7421 {
7422 /*
7423 * The VBoxSDS should be used for process launching the VM with
7424 * GUI only if the caller and the VBoxSDS are in different Windows
7425 * sessions and the caller in the interactive one.
7426 */
7427 fUseVBoxSDS = false;
7428
7429 /* Get windows session of the current process. The process token used
7430 due to several reasons:
7431 1. The token is absent for the current thread except someone set it
7432 for us.
7433 2. Needs to get the id of the session where the process is started.
7434 We only need to do this once, though. */
7435 static DWORD s_idCurrentSession = ~(DWORD)0;
7436 DWORD idCurrentSession = s_idCurrentSession;
7437 if (idCurrentSession == ~(DWORD)0)
7438 {
7439 HANDLE hCurrentProcessToken = NULL;
7440 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7441 {
7442 DWORD cbIgn = 0;
7443 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7444 s_idCurrentSession = idCurrentSession;
7445 else
7446 {
7447 idCurrentSession = ~(DWORD)0;
7448 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7449 }
7450 CloseHandle(hCurrentProcessToken);
7451 }
7452 else
7453 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7454 }
7455
7456 /* get the caller's session */
7457 HRESULT hrc = CoImpersonateClient();
7458 if (SUCCEEDED(hrc))
7459 {
7460 HANDLE hCallerThreadToken;
7461 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7462 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7463 &hCallerThreadToken))
7464 {
7465 SetLastError(NO_ERROR);
7466 DWORD cbIgn = 0;
7467 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7468 {
7469 /* Only need to use SDS if the session ID differs: */
7470 if (idCurrentSession != idCallerSession)
7471 {
7472 fUseVBoxSDS = false;
7473
7474 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7475 DWORD cbTokenGroups = 0;
7476 PTOKEN_GROUPS pTokenGroups = NULL;
7477 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7478 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7479 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7480 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7481 {
7482 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7483 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7484 PSID pInteractiveSid = NULL;
7485 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7486 {
7487 /* Iterate over the groups looking for the interactive SID: */
7488 fUseVBoxSDS = false;
7489 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7490 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7491 {
7492 fUseVBoxSDS = true;
7493 break;
7494 }
7495 FreeSid(pInteractiveSid);
7496 }
7497 }
7498 else
7499 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7500 RTMemTmpFree(pTokenGroups);
7501 }
7502 }
7503 else
7504 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7505 CloseHandle(hCallerThreadToken);
7506 }
7507 else
7508 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7509 CoRevertToSelf();
7510 }
7511 else
7512 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7513 }
7514 if (fUseVBoxSDS)
7515 {
7516 /* connect to VBoxSDS */
7517 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7518 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7519 if (FAILED(rc))
7520 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7521 strMachineName.c_str());
7522
7523 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7524 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7525 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7526 service to access the files. */
7527 rc = CoSetProxyBlanket(pVBoxSDS,
7528 RPC_C_AUTHN_DEFAULT,
7529 RPC_C_AUTHZ_DEFAULT,
7530 COLE_DEFAULT_PRINCIPAL,
7531 RPC_C_AUTHN_LEVEL_DEFAULT,
7532 RPC_C_IMP_LEVEL_IMPERSONATE,
7533 NULL,
7534 EOAC_DEFAULT);
7535 if (FAILED(rc))
7536 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7537
7538 size_t const cEnvVars = aEnvironmentChanges.size();
7539 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7540 for (size_t i = 0; i < cEnvVars; i++)
7541 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7542
7543 ULONG uPid = 0;
7544 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7545 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7546 idCallerSession, &uPid);
7547 if (FAILED(rc))
7548 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7549 pid = (RTPROCESS)uPid;
7550 }
7551 else
7552#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7553 {
7554 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7555 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7556 if (RT_FAILURE(vrc))
7557 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7558 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7559 }
7560
7561 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7562 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7563
7564 if (!fSeparate)
7565 {
7566 /*
7567 * Note that we don't release the lock here before calling the client,
7568 * because it doesn't need to call us back if called with a NULL argument.
7569 * Releasing the lock here is dangerous because we didn't prepare the
7570 * launch data yet, but the client we've just started may happen to be
7571 * too fast and call LockMachine() that will fail (because of PID, etc.),
7572 * so that the Machine will never get out of the Spawning session state.
7573 */
7574
7575 /* inform the session that it will be a remote one */
7576 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7577#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7578 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7579#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7580 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7581#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7582 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7583
7584 if (FAILED(rc))
7585 {
7586 /* restore the session state */
7587 mData->mSession.mState = SessionState_Unlocked;
7588 alock.release();
7589 mParent->i_addProcessToReap(pid);
7590 /* The failure may occur w/o any error info (from RPC), so provide one */
7591 return setError(VBOX_E_VM_ERROR,
7592 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7593 }
7594
7595 /* attach launch data to the machine */
7596 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7597 mData->mSession.mRemoteControls.push_back(aControl);
7598 mData->mSession.mProgress = aProgress;
7599 mData->mSession.mPID = pid;
7600 mData->mSession.mState = SessionState_Spawning;
7601 Assert(strCanonicalName.isNotEmpty());
7602 mData->mSession.mName = strCanonicalName;
7603 }
7604 else
7605 {
7606 /* For separate UI process we declare the launch as completed instantly, as the
7607 * actual headless VM start may or may not come. No point in remembering anything
7608 * yet, as what matters for us is when the headless VM gets started. */
7609 aProgress->i_notifyComplete(S_OK);
7610 }
7611
7612 alock.release();
7613 mParent->i_addProcessToReap(pid);
7614
7615 LogFlowThisFuncLeave();
7616 return S_OK;
7617}
7618
7619/**
7620 * Returns @c true if the given session machine instance has an open direct
7621 * session (and optionally also for direct sessions which are closing) and
7622 * returns the session control machine instance if so.
7623 *
7624 * Note that when the method returns @c false, the arguments remain unchanged.
7625 *
7626 * @param aMachine Session machine object.
7627 * @param aControl Direct session control object (optional).
7628 * @param aRequireVM If true then only allow VM sessions.
7629 * @param aAllowClosing If true then additionally a session which is currently
7630 * being closed will also be allowed.
7631 *
7632 * @note locks this object for reading.
7633 */
7634bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7635 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7636 bool aRequireVM /*= false*/,
7637 bool aAllowClosing /*= false*/)
7638{
7639 AutoLimitedCaller autoCaller(this);
7640 AssertComRCReturn(autoCaller.rc(), false);
7641
7642 /* just return false for inaccessible machines */
7643 if (getObjectState().getState() != ObjectState::Ready)
7644 return false;
7645
7646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7647
7648 if ( ( mData->mSession.mState == SessionState_Locked
7649 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7650 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7651 )
7652 {
7653 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7654
7655 aMachine = mData->mSession.mMachine;
7656
7657 if (aControl != NULL)
7658 *aControl = mData->mSession.mDirectControl;
7659
7660 return true;
7661 }
7662
7663 return false;
7664}
7665
7666/**
7667 * Returns @c true if the given machine has an spawning direct session.
7668 *
7669 * @note locks this object for reading.
7670 */
7671bool Machine::i_isSessionSpawning()
7672{
7673 AutoLimitedCaller autoCaller(this);
7674 AssertComRCReturn(autoCaller.rc(), false);
7675
7676 /* just return false for inaccessible machines */
7677 if (getObjectState().getState() != ObjectState::Ready)
7678 return false;
7679
7680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7681
7682 if (mData->mSession.mState == SessionState_Spawning)
7683 return true;
7684
7685 return false;
7686}
7687
7688/**
7689 * Called from the client watcher thread to check for unexpected client process
7690 * death during Session_Spawning state (e.g. before it successfully opened a
7691 * direct session).
7692 *
7693 * On Win32 and on OS/2, this method is called only when we've got the
7694 * direct client's process termination notification, so it always returns @c
7695 * true.
7696 *
7697 * On other platforms, this method returns @c true if the client process is
7698 * terminated and @c false if it's still alive.
7699 *
7700 * @note Locks this object for writing.
7701 */
7702bool Machine::i_checkForSpawnFailure()
7703{
7704 AutoCaller autoCaller(this);
7705 if (!autoCaller.isOk())
7706 {
7707 /* nothing to do */
7708 LogFlowThisFunc(("Already uninitialized!\n"));
7709 return true;
7710 }
7711
7712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7713
7714 if (mData->mSession.mState != SessionState_Spawning)
7715 {
7716 /* nothing to do */
7717 LogFlowThisFunc(("Not spawning any more!\n"));
7718 return true;
7719 }
7720
7721 HRESULT rc = S_OK;
7722
7723 /* PID not yet initialized, skip check. */
7724 if (mData->mSession.mPID == NIL_RTPROCESS)
7725 return false;
7726
7727 RTPROCSTATUS status;
7728 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7729
7730 if (vrc != VERR_PROCESS_RUNNING)
7731 {
7732 Utf8Str strExtraInfo;
7733
7734#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7735 /* If the startup logfile exists and is of non-zero length, tell the
7736 user to look there for more details to encourage them to attach it
7737 when reporting startup issues. */
7738 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7739 uint64_t cbStartupLogFile = 0;
7740 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7741 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7742 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7743#endif
7744
7745 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7746 rc = setError(E_FAIL,
7747 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7748 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7749 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7750 rc = setError(E_FAIL,
7751 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7752 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7753 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7754 rc = setError(E_FAIL,
7755 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7756 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7757 else
7758 rc = setErrorBoth(E_FAIL, vrc,
7759 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7760 i_getName().c_str(), vrc, strExtraInfo.c_str());
7761 }
7762
7763 if (FAILED(rc))
7764 {
7765 /* Close the remote session, remove the remote control from the list
7766 * and reset session state to Closed (@note keep the code in sync with
7767 * the relevant part in LockMachine()). */
7768
7769 Assert(mData->mSession.mRemoteControls.size() == 1);
7770 if (mData->mSession.mRemoteControls.size() == 1)
7771 {
7772 ErrorInfoKeeper eik;
7773 mData->mSession.mRemoteControls.front()->Uninitialize();
7774 }
7775
7776 mData->mSession.mRemoteControls.clear();
7777 mData->mSession.mState = SessionState_Unlocked;
7778
7779 /* finalize the progress after setting the state */
7780 if (!mData->mSession.mProgress.isNull())
7781 {
7782 mData->mSession.mProgress->notifyComplete(rc);
7783 mData->mSession.mProgress.setNull();
7784 }
7785
7786 mData->mSession.mPID = NIL_RTPROCESS;
7787
7788 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7789 return true;
7790 }
7791
7792 return false;
7793}
7794
7795/**
7796 * Checks whether the machine can be registered. If so, commits and saves
7797 * all settings.
7798 *
7799 * @note Must be called from mParent's write lock. Locks this object and
7800 * children for writing.
7801 */
7802HRESULT Machine::i_prepareRegister()
7803{
7804 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7805
7806 AutoLimitedCaller autoCaller(this);
7807 AssertComRCReturnRC(autoCaller.rc());
7808
7809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7810
7811 /* wait for state dependents to drop to zero */
7812 i_ensureNoStateDependencies();
7813
7814 if (!mData->mAccessible)
7815 return setError(VBOX_E_INVALID_OBJECT_STATE,
7816 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7817 mUserData->s.strName.c_str(),
7818 mData->mUuid.toString().c_str());
7819
7820 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7821
7822 if (mData->mRegistered)
7823 return setError(VBOX_E_INVALID_OBJECT_STATE,
7824 tr("The machine '%s' with UUID {%s} is already registered"),
7825 mUserData->s.strName.c_str(),
7826 mData->mUuid.toString().c_str());
7827
7828 HRESULT rc = S_OK;
7829
7830 // Ensure the settings are saved. If we are going to be registered and
7831 // no config file exists yet, create it by calling i_saveSettings() too.
7832 if ( (mData->flModifications)
7833 || (!mData->pMachineConfigFile->fileExists())
7834 )
7835 {
7836 rc = i_saveSettings(NULL);
7837 // no need to check whether VirtualBox.xml needs saving too since
7838 // we can't have a machine XML file rename pending
7839 if (FAILED(rc)) return rc;
7840 }
7841
7842 /* more config checking goes here */
7843
7844 if (SUCCEEDED(rc))
7845 {
7846 /* we may have had implicit modifications we want to fix on success */
7847 i_commit();
7848
7849 mData->mRegistered = true;
7850 }
7851 else
7852 {
7853 /* we may have had implicit modifications we want to cancel on failure*/
7854 i_rollback(false /* aNotify */);
7855 }
7856
7857 return rc;
7858}
7859
7860/**
7861 * Increases the number of objects dependent on the machine state or on the
7862 * registered state. Guarantees that these two states will not change at least
7863 * until #i_releaseStateDependency() is called.
7864 *
7865 * Depending on the @a aDepType value, additional state checks may be made.
7866 * These checks will set extended error info on failure. See
7867 * #i_checkStateDependency() for more info.
7868 *
7869 * If this method returns a failure, the dependency is not added and the caller
7870 * is not allowed to rely on any particular machine state or registration state
7871 * value and may return the failed result code to the upper level.
7872 *
7873 * @param aDepType Dependency type to add.
7874 * @param aState Current machine state (NULL if not interested).
7875 * @param aRegistered Current registered state (NULL if not interested).
7876 *
7877 * @note Locks this object for writing.
7878 */
7879HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7880 MachineState_T *aState /* = NULL */,
7881 BOOL *aRegistered /* = NULL */)
7882{
7883 AutoCaller autoCaller(this);
7884 AssertComRCReturnRC(autoCaller.rc());
7885
7886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7887
7888 HRESULT rc = i_checkStateDependency(aDepType);
7889 if (FAILED(rc)) return rc;
7890
7891 {
7892 if (mData->mMachineStateChangePending != 0)
7893 {
7894 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7895 * drop to zero so don't add more. It may make sense to wait a bit
7896 * and retry before reporting an error (since the pending state
7897 * transition should be really quick) but let's just assert for
7898 * now to see if it ever happens on practice. */
7899
7900 AssertFailed();
7901
7902 return setError(E_ACCESSDENIED,
7903 tr("Machine state change is in progress. Please retry the operation later."));
7904 }
7905
7906 ++mData->mMachineStateDeps;
7907 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7908 }
7909
7910 if (aState)
7911 *aState = mData->mMachineState;
7912 if (aRegistered)
7913 *aRegistered = mData->mRegistered;
7914
7915 return S_OK;
7916}
7917
7918/**
7919 * Decreases the number of objects dependent on the machine state.
7920 * Must always complete the #i_addStateDependency() call after the state
7921 * dependency is no more necessary.
7922 */
7923void Machine::i_releaseStateDependency()
7924{
7925 AutoCaller autoCaller(this);
7926 AssertComRCReturnVoid(autoCaller.rc());
7927
7928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7929
7930 /* releaseStateDependency() w/o addStateDependency()? */
7931 AssertReturnVoid(mData->mMachineStateDeps != 0);
7932 -- mData->mMachineStateDeps;
7933
7934 if (mData->mMachineStateDeps == 0)
7935 {
7936 /* inform i_ensureNoStateDependencies() that there are no more deps */
7937 if (mData->mMachineStateChangePending != 0)
7938 {
7939 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7940 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7941 }
7942 }
7943}
7944
7945Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7946{
7947 /* start with nothing found */
7948 Utf8Str strResult("");
7949
7950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7951
7952 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7953 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7954 // found:
7955 strResult = it->second; // source is a Utf8Str
7956
7957 return strResult;
7958}
7959
7960// protected methods
7961/////////////////////////////////////////////////////////////////////////////
7962
7963/**
7964 * Performs machine state checks based on the @a aDepType value. If a check
7965 * fails, this method will set extended error info, otherwise it will return
7966 * S_OK. It is supposed, that on failure, the caller will immediately return
7967 * the return value of this method to the upper level.
7968 *
7969 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7970 *
7971 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7972 * current state of this machine object allows to change settings of the
7973 * machine (i.e. the machine is not registered, or registered but not running
7974 * and not saved). It is useful to call this method from Machine setters
7975 * before performing any change.
7976 *
7977 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7978 * as for MutableStateDep except that if the machine is saved, S_OK is also
7979 * returned. This is useful in setters which allow changing machine
7980 * properties when it is in the saved state.
7981 *
7982 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7983 * if the current state of this machine object allows to change runtime
7984 * changeable settings of the machine (i.e. the machine is not registered, or
7985 * registered but either running or not running and not saved). It is useful
7986 * to call this method from Machine setters before performing any changes to
7987 * runtime changeable settings.
7988 *
7989 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7990 * the same as for MutableOrRunningStateDep except that if the machine is
7991 * saved, S_OK is also returned. This is useful in setters which allow
7992 * changing runtime and saved state changeable machine properties.
7993 *
7994 * @param aDepType Dependency type to check.
7995 *
7996 * @note Non Machine based classes should use #i_addStateDependency() and
7997 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7998 * template.
7999 *
8000 * @note This method must be called from under this object's read or write
8001 * lock.
8002 */
8003HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8004{
8005 switch (aDepType)
8006 {
8007 case AnyStateDep:
8008 {
8009 break;
8010 }
8011 case MutableStateDep:
8012 {
8013 if ( mData->mRegistered
8014 && ( !i_isSessionMachine()
8015 || ( mData->mMachineState != MachineState_Aborted
8016 && mData->mMachineState != MachineState_Teleported
8017 && mData->mMachineState != MachineState_PoweredOff
8018 )
8019 )
8020 )
8021 return setError(VBOX_E_INVALID_VM_STATE,
8022 tr("The machine is not mutable (state is %s)"),
8023 Global::stringifyMachineState(mData->mMachineState));
8024 break;
8025 }
8026 case MutableOrSavedStateDep:
8027 {
8028 if ( mData->mRegistered
8029 && ( !i_isSessionMachine()
8030 || ( mData->mMachineState != MachineState_Aborted
8031 && mData->mMachineState != MachineState_Teleported
8032 && mData->mMachineState != MachineState_Saved
8033 && mData->mMachineState != MachineState_PoweredOff
8034 )
8035 )
8036 )
8037 return setError(VBOX_E_INVALID_VM_STATE,
8038 tr("The machine is not mutable or saved (state is %s)"),
8039 Global::stringifyMachineState(mData->mMachineState));
8040 break;
8041 }
8042 case MutableOrRunningStateDep:
8043 {
8044 if ( mData->mRegistered
8045 && ( !i_isSessionMachine()
8046 || ( mData->mMachineState != MachineState_Aborted
8047 && mData->mMachineState != MachineState_Teleported
8048 && mData->mMachineState != MachineState_PoweredOff
8049 && !Global::IsOnline(mData->mMachineState)
8050 )
8051 )
8052 )
8053 return setError(VBOX_E_INVALID_VM_STATE,
8054 tr("The machine is not mutable or running (state is %s)"),
8055 Global::stringifyMachineState(mData->mMachineState));
8056 break;
8057 }
8058 case MutableOrSavedOrRunningStateDep:
8059 {
8060 if ( mData->mRegistered
8061 && ( !i_isSessionMachine()
8062 || ( mData->mMachineState != MachineState_Aborted
8063 && mData->mMachineState != MachineState_Teleported
8064 && mData->mMachineState != MachineState_Saved
8065 && mData->mMachineState != MachineState_PoweredOff
8066 && !Global::IsOnline(mData->mMachineState)
8067 )
8068 )
8069 )
8070 return setError(VBOX_E_INVALID_VM_STATE,
8071 tr("The machine is not mutable, saved or running (state is %s)"),
8072 Global::stringifyMachineState(mData->mMachineState));
8073 break;
8074 }
8075 }
8076
8077 return S_OK;
8078}
8079
8080/**
8081 * Helper to initialize all associated child objects and allocate data
8082 * structures.
8083 *
8084 * This method must be called as a part of the object's initialization procedure
8085 * (usually done in the #init() method).
8086 *
8087 * @note Must be called only from #init() or from #i_registeredInit().
8088 */
8089HRESULT Machine::initDataAndChildObjects()
8090{
8091 AutoCaller autoCaller(this);
8092 AssertComRCReturnRC(autoCaller.rc());
8093 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8094 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8095
8096 AssertReturn(!mData->mAccessible, E_FAIL);
8097
8098 /* allocate data structures */
8099 mSSData.allocate();
8100 mUserData.allocate();
8101 mHWData.allocate();
8102 mMediumAttachments.allocate();
8103 mStorageControllers.allocate();
8104 mUSBControllers.allocate();
8105
8106 /* initialize mOSTypeId */
8107 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8108
8109/** @todo r=bird: init() methods never fails, right? Why don't we make them
8110 * return void then! */
8111
8112 /* create associated BIOS settings object */
8113 unconst(mBIOSSettings).createObject();
8114 mBIOSSettings->init(this);
8115
8116 /* create associated record settings object */
8117 unconst(mRecordingSettings).createObject();
8118 mRecordingSettings->init(this);
8119
8120 /* create the graphics adapter object (always present) */
8121 unconst(mGraphicsAdapter).createObject();
8122 mGraphicsAdapter->init(this);
8123
8124 /* create an associated VRDE object (default is disabled) */
8125 unconst(mVRDEServer).createObject();
8126 mVRDEServer->init(this);
8127
8128 /* create associated serial port objects */
8129 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8130 {
8131 unconst(mSerialPorts[slot]).createObject();
8132 mSerialPorts[slot]->init(this, slot);
8133 }
8134
8135 /* create associated parallel port objects */
8136 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8137 {
8138 unconst(mParallelPorts[slot]).createObject();
8139 mParallelPorts[slot]->init(this, slot);
8140 }
8141
8142 /* create the audio adapter object (always present, default is disabled) */
8143 unconst(mAudioAdapter).createObject();
8144 mAudioAdapter->init(this);
8145
8146 /* create the USB device filters object (always present) */
8147 unconst(mUSBDeviceFilters).createObject();
8148 mUSBDeviceFilters->init(this);
8149
8150 /* create associated network adapter objects */
8151 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8152 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8153 {
8154 unconst(mNetworkAdapters[slot]).createObject();
8155 mNetworkAdapters[slot]->init(this, slot);
8156 }
8157
8158 /* create the bandwidth control */
8159 unconst(mBandwidthControl).createObject();
8160 mBandwidthControl->init(this);
8161
8162 return S_OK;
8163}
8164
8165/**
8166 * Helper to uninitialize all associated child objects and to free all data
8167 * structures.
8168 *
8169 * This method must be called as a part of the object's uninitialization
8170 * procedure (usually done in the #uninit() method).
8171 *
8172 * @note Must be called only from #uninit() or from #i_registeredInit().
8173 */
8174void Machine::uninitDataAndChildObjects()
8175{
8176 AutoCaller autoCaller(this);
8177 AssertComRCReturnVoid(autoCaller.rc());
8178 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8179 || getObjectState().getState() == ObjectState::Limited);
8180
8181 /* tell all our other child objects we've been uninitialized */
8182 if (mBandwidthControl)
8183 {
8184 mBandwidthControl->uninit();
8185 unconst(mBandwidthControl).setNull();
8186 }
8187
8188 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8189 {
8190 if (mNetworkAdapters[slot])
8191 {
8192 mNetworkAdapters[slot]->uninit();
8193 unconst(mNetworkAdapters[slot]).setNull();
8194 }
8195 }
8196
8197 if (mUSBDeviceFilters)
8198 {
8199 mUSBDeviceFilters->uninit();
8200 unconst(mUSBDeviceFilters).setNull();
8201 }
8202
8203 if (mAudioAdapter)
8204 {
8205 mAudioAdapter->uninit();
8206 unconst(mAudioAdapter).setNull();
8207 }
8208
8209 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8210 {
8211 if (mParallelPorts[slot])
8212 {
8213 mParallelPorts[slot]->uninit();
8214 unconst(mParallelPorts[slot]).setNull();
8215 }
8216 }
8217
8218 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8219 {
8220 if (mSerialPorts[slot])
8221 {
8222 mSerialPorts[slot]->uninit();
8223 unconst(mSerialPorts[slot]).setNull();
8224 }
8225 }
8226
8227 if (mVRDEServer)
8228 {
8229 mVRDEServer->uninit();
8230 unconst(mVRDEServer).setNull();
8231 }
8232
8233 if (mGraphicsAdapter)
8234 {
8235 mGraphicsAdapter->uninit();
8236 unconst(mGraphicsAdapter).setNull();
8237 }
8238
8239 if (mBIOSSettings)
8240 {
8241 mBIOSSettings->uninit();
8242 unconst(mBIOSSettings).setNull();
8243 }
8244
8245 if (mRecordingSettings)
8246 {
8247 mRecordingSettings->uninit();
8248 unconst(mRecordingSettings).setNull();
8249 }
8250
8251 /* Deassociate media (only when a real Machine or a SnapshotMachine
8252 * instance is uninitialized; SessionMachine instances refer to real
8253 * Machine media). This is necessary for a clean re-initialization of
8254 * the VM after successfully re-checking the accessibility state. Note
8255 * that in case of normal Machine or SnapshotMachine uninitialization (as
8256 * a result of unregistering or deleting the snapshot), outdated media
8257 * attachments will already be uninitialized and deleted, so this
8258 * code will not affect them. */
8259 if ( !mMediumAttachments.isNull()
8260 && !i_isSessionMachine()
8261 )
8262 {
8263 for (MediumAttachmentList::const_iterator
8264 it = mMediumAttachments->begin();
8265 it != mMediumAttachments->end();
8266 ++it)
8267 {
8268 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8269 if (pMedium.isNull())
8270 continue;
8271 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8272 AssertComRC(rc);
8273 }
8274 }
8275
8276 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8277 {
8278 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8279 if (mData->mFirstSnapshot)
8280 {
8281 // snapshots tree is protected by machine write lock; strictly
8282 // this isn't necessary here since we're deleting the entire
8283 // machine, but otherwise we assert in Snapshot::uninit()
8284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8285 mData->mFirstSnapshot->uninit();
8286 mData->mFirstSnapshot.setNull();
8287 }
8288
8289 mData->mCurrentSnapshot.setNull();
8290 }
8291
8292 /* free data structures (the essential mData structure is not freed here
8293 * since it may be still in use) */
8294 mMediumAttachments.free();
8295 mStorageControllers.free();
8296 mUSBControllers.free();
8297 mHWData.free();
8298 mUserData.free();
8299 mSSData.free();
8300}
8301
8302/**
8303 * Returns a pointer to the Machine object for this machine that acts like a
8304 * parent for complex machine data objects such as shared folders, etc.
8305 *
8306 * For primary Machine objects and for SnapshotMachine objects, returns this
8307 * object's pointer itself. For SessionMachine objects, returns the peer
8308 * (primary) machine pointer.
8309 */
8310Machine *Machine::i_getMachine()
8311{
8312 if (i_isSessionMachine())
8313 return (Machine*)mPeer;
8314 return this;
8315}
8316
8317/**
8318 * Makes sure that there are no machine state dependents. If necessary, waits
8319 * for the number of dependents to drop to zero.
8320 *
8321 * Make sure this method is called from under this object's write lock to
8322 * guarantee that no new dependents may be added when this method returns
8323 * control to the caller.
8324 *
8325 * @note Locks this object for writing. The lock will be released while waiting
8326 * (if necessary).
8327 *
8328 * @warning To be used only in methods that change the machine state!
8329 */
8330void Machine::i_ensureNoStateDependencies()
8331{
8332 AssertReturnVoid(isWriteLockOnCurrentThread());
8333
8334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8335
8336 /* Wait for all state dependents if necessary */
8337 if (mData->mMachineStateDeps != 0)
8338 {
8339 /* lazy semaphore creation */
8340 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8341 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8342
8343 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8344 mData->mMachineStateDeps));
8345
8346 ++mData->mMachineStateChangePending;
8347
8348 /* reset the semaphore before waiting, the last dependent will signal
8349 * it */
8350 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8351
8352 alock.release();
8353
8354 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8355
8356 alock.acquire();
8357
8358 -- mData->mMachineStateChangePending;
8359 }
8360}
8361
8362/**
8363 * Changes the machine state and informs callbacks.
8364 *
8365 * This method is not intended to fail so it either returns S_OK or asserts (and
8366 * returns a failure).
8367 *
8368 * @note Locks this object for writing.
8369 */
8370HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8371{
8372 LogFlowThisFuncEnter();
8373 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8374 Assert(aMachineState != MachineState_Null);
8375
8376 AutoCaller autoCaller(this);
8377 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8378
8379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8380
8381 /* wait for state dependents to drop to zero */
8382 i_ensureNoStateDependencies();
8383
8384 MachineState_T const enmOldState = mData->mMachineState;
8385 if (enmOldState != aMachineState)
8386 {
8387 mData->mMachineState = aMachineState;
8388 RTTimeNow(&mData->mLastStateChange);
8389
8390#ifdef VBOX_WITH_DTRACE_R3_MAIN
8391 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8392#endif
8393 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8394 }
8395
8396 LogFlowThisFuncLeave();
8397 return S_OK;
8398}
8399
8400/**
8401 * Searches for a shared folder with the given logical name
8402 * in the collection of shared folders.
8403 *
8404 * @param aName logical name of the shared folder
8405 * @param aSharedFolder where to return the found object
8406 * @param aSetError whether to set the error info if the folder is
8407 * not found
8408 * @return
8409 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8410 *
8411 * @note
8412 * must be called from under the object's lock!
8413 */
8414HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8415 ComObjPtr<SharedFolder> &aSharedFolder,
8416 bool aSetError /* = false */)
8417{
8418 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8419 for (HWData::SharedFolderList::const_iterator
8420 it = mHWData->mSharedFolders.begin();
8421 it != mHWData->mSharedFolders.end();
8422 ++it)
8423 {
8424 SharedFolder *pSF = *it;
8425 AutoCaller autoCaller(pSF);
8426 if (pSF->i_getName() == aName)
8427 {
8428 aSharedFolder = pSF;
8429 rc = S_OK;
8430 break;
8431 }
8432 }
8433
8434 if (aSetError && FAILED(rc))
8435 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8436
8437 return rc;
8438}
8439
8440/**
8441 * Initializes all machine instance data from the given settings structures
8442 * from XML. The exception is the machine UUID which needs special handling
8443 * depending on the caller's use case, so the caller needs to set that herself.
8444 *
8445 * This gets called in several contexts during machine initialization:
8446 *
8447 * -- When machine XML exists on disk already and needs to be loaded into memory,
8448 * for example, from #i_registeredInit() to load all registered machines on
8449 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8450 * attached to the machine should be part of some media registry already.
8451 *
8452 * -- During OVF import, when a machine config has been constructed from an
8453 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8454 * ensure that the media listed as attachments in the config (which have
8455 * been imported from the OVF) receive the correct registry ID.
8456 *
8457 * -- During VM cloning.
8458 *
8459 * @param config Machine settings from XML.
8460 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8461 * for each attached medium in the config.
8462 * @return
8463 */
8464HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8465 const Guid *puuidRegistry)
8466{
8467 // copy name, description, OS type, teleporter, UTC etc.
8468 mUserData->s = config.machineUserData;
8469
8470 // look up the object by Id to check it is valid
8471 ComObjPtr<GuestOSType> pGuestOSType;
8472 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8473 if (!pGuestOSType.isNull())
8474 mUserData->s.strOsType = pGuestOSType->i_id();
8475
8476 // stateFile (optional)
8477 if (config.strStateFile.isEmpty())
8478 mSSData->strStateFilePath.setNull();
8479 else
8480 {
8481 Utf8Str stateFilePathFull(config.strStateFile);
8482 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8483 if (RT_FAILURE(vrc))
8484 return setErrorBoth(E_FAIL, vrc,
8485 tr("Invalid saved state file path '%s' (%Rrc)"),
8486 config.strStateFile.c_str(),
8487 vrc);
8488 mSSData->strStateFilePath = stateFilePathFull;
8489 }
8490
8491 // snapshot folder needs special processing so set it again
8492 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8493 if (FAILED(rc)) return rc;
8494
8495 /* Copy the extra data items (config may or may not be the same as
8496 * mData->pMachineConfigFile) if necessary. When loading the XML files
8497 * from disk they are the same, but not for OVF import. */
8498 if (mData->pMachineConfigFile != &config)
8499 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8500
8501 /* currentStateModified (optional, default is true) */
8502 mData->mCurrentStateModified = config.fCurrentStateModified;
8503
8504 mData->mLastStateChange = config.timeLastStateChange;
8505
8506 /*
8507 * note: all mUserData members must be assigned prior this point because
8508 * we need to commit changes in order to let mUserData be shared by all
8509 * snapshot machine instances.
8510 */
8511 mUserData.commitCopy();
8512
8513 // machine registry, if present (must be loaded before snapshots)
8514 if (config.canHaveOwnMediaRegistry())
8515 {
8516 // determine machine folder
8517 Utf8Str strMachineFolder = i_getSettingsFileFull();
8518 strMachineFolder.stripFilename();
8519 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8520 config.mediaRegistry,
8521 strMachineFolder);
8522 if (FAILED(rc)) return rc;
8523 }
8524
8525 /* Snapshot node (optional) */
8526 size_t cRootSnapshots;
8527 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8528 {
8529 // there must be only one root snapshot
8530 Assert(cRootSnapshots == 1);
8531
8532 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8533
8534 rc = i_loadSnapshot(snap,
8535 config.uuidCurrentSnapshot,
8536 NULL); // no parent == first snapshot
8537 if (FAILED(rc)) return rc;
8538 }
8539
8540 // hardware data
8541 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8542 if (FAILED(rc)) return rc;
8543
8544 /*
8545 * NOTE: the assignment below must be the last thing to do,
8546 * otherwise it will be not possible to change the settings
8547 * somewhere in the code above because all setters will be
8548 * blocked by i_checkStateDependency(MutableStateDep).
8549 */
8550
8551 /* set the machine state to Aborted or Saved when appropriate */
8552 if (config.fAborted)
8553 {
8554 mSSData->strStateFilePath.setNull();
8555
8556 /* no need to use i_setMachineState() during init() */
8557 mData->mMachineState = MachineState_Aborted;
8558 }
8559 else if (!mSSData->strStateFilePath.isEmpty())
8560 {
8561 /* no need to use i_setMachineState() during init() */
8562 mData->mMachineState = MachineState_Saved;
8563 }
8564
8565 // after loading settings, we are no longer different from the XML on disk
8566 mData->flModifications = 0;
8567
8568 return S_OK;
8569}
8570
8571/**
8572 * Recursively loads all snapshots starting from the given.
8573 *
8574 * @param data snapshot settings.
8575 * @param aCurSnapshotId Current snapshot ID from the settings file.
8576 * @param aParentSnapshot Parent snapshot.
8577 */
8578HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8579 const Guid &aCurSnapshotId,
8580 Snapshot *aParentSnapshot)
8581{
8582 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8583 AssertReturn(!i_isSessionMachine(), E_FAIL);
8584
8585 HRESULT rc = S_OK;
8586
8587 Utf8Str strStateFile;
8588 if (!data.strStateFile.isEmpty())
8589 {
8590 /* optional */
8591 strStateFile = data.strStateFile;
8592 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8593 if (RT_FAILURE(vrc))
8594 return setErrorBoth(E_FAIL, vrc,
8595 tr("Invalid saved state file path '%s' (%Rrc)"),
8596 strStateFile.c_str(),
8597 vrc);
8598 }
8599
8600 /* create a snapshot machine object */
8601 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8602 pSnapshotMachine.createObject();
8603 rc = pSnapshotMachine->initFromSettings(this,
8604 data.hardware,
8605 &data.debugging,
8606 &data.autostart,
8607 data.uuid.ref(),
8608 strStateFile);
8609 if (FAILED(rc)) return rc;
8610
8611 /* create a snapshot object */
8612 ComObjPtr<Snapshot> pSnapshot;
8613 pSnapshot.createObject();
8614 /* initialize the snapshot */
8615 rc = pSnapshot->init(mParent, // VirtualBox object
8616 data.uuid,
8617 data.strName,
8618 data.strDescription,
8619 data.timestamp,
8620 pSnapshotMachine,
8621 aParentSnapshot);
8622 if (FAILED(rc)) return rc;
8623
8624 /* memorize the first snapshot if necessary */
8625 if (!mData->mFirstSnapshot)
8626 mData->mFirstSnapshot = pSnapshot;
8627
8628 /* memorize the current snapshot when appropriate */
8629 if ( !mData->mCurrentSnapshot
8630 && pSnapshot->i_getId() == aCurSnapshotId
8631 )
8632 mData->mCurrentSnapshot = pSnapshot;
8633
8634 // now create the children
8635 for (settings::SnapshotsList::const_iterator
8636 it = data.llChildSnapshots.begin();
8637 it != data.llChildSnapshots.end();
8638 ++it)
8639 {
8640 const settings::Snapshot &childData = *it;
8641 // recurse
8642 rc = i_loadSnapshot(childData,
8643 aCurSnapshotId,
8644 pSnapshot); // parent = the one we created above
8645 if (FAILED(rc)) return rc;
8646 }
8647
8648 return rc;
8649}
8650
8651/**
8652 * Loads settings into mHWData.
8653 *
8654 * @param puuidRegistry Registry ID.
8655 * @param puuidSnapshot Snapshot ID
8656 * @param data Reference to the hardware settings.
8657 * @param pDbg Pointer to the debugging settings.
8658 * @param pAutostart Pointer to the autostart settings.
8659 */
8660HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8661 const Guid *puuidSnapshot,
8662 const settings::Hardware &data,
8663 const settings::Debugging *pDbg,
8664 const settings::Autostart *pAutostart)
8665{
8666 AssertReturn(!i_isSessionMachine(), E_FAIL);
8667
8668 HRESULT rc = S_OK;
8669
8670 try
8671 {
8672 ComObjPtr<GuestOSType> pGuestOSType;
8673 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8674
8675 /* The hardware version attribute (optional). */
8676 mHWData->mHWVersion = data.strVersion;
8677 mHWData->mHardwareUUID = data.uuid;
8678
8679 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8680 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8681 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8682 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8683 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8684 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8685 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8686 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8687 mHWData->mPAEEnabled = data.fPAE;
8688 mHWData->mLongMode = data.enmLongMode;
8689 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8690 mHWData->mAPIC = data.fAPIC;
8691 mHWData->mX2APIC = data.fX2APIC;
8692 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8693 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8694 mHWData->mSpecCtrl = data.fSpecCtrl;
8695 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8696 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8697 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8698 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8699 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8700 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8701 mHWData->mCPUCount = data.cCPUs;
8702 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8703 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8704 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8705 mHWData->mCpuProfile = data.strCpuProfile;
8706
8707 // cpu
8708 if (mHWData->mCPUHotPlugEnabled)
8709 {
8710 for (settings::CpuList::const_iterator
8711 it = data.llCpus.begin();
8712 it != data.llCpus.end();
8713 ++it)
8714 {
8715 const settings::Cpu &cpu = *it;
8716
8717 mHWData->mCPUAttached[cpu.ulId] = true;
8718 }
8719 }
8720
8721 // cpuid leafs
8722 for (settings::CpuIdLeafsList::const_iterator
8723 it = data.llCpuIdLeafs.begin();
8724 it != data.llCpuIdLeafs.end();
8725 ++it)
8726 {
8727 const settings::CpuIdLeaf &rLeaf= *it;
8728 if ( rLeaf.idx < UINT32_C(0x20)
8729 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8730 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8731 mHWData->mCpuIdLeafList.push_back(rLeaf);
8732 /* else: just ignore */
8733 }
8734
8735 mHWData->mMemorySize = data.ulMemorySizeMB;
8736 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8737
8738 // boot order
8739 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8740 {
8741 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8742 if (it == data.mapBootOrder.end())
8743 mHWData->mBootOrder[i] = DeviceType_Null;
8744 else
8745 mHWData->mBootOrder[i] = it->second;
8746 }
8747
8748 mHWData->mFirmwareType = data.firmwareType;
8749 mHWData->mPointingHIDType = data.pointingHIDType;
8750 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8751 mHWData->mChipsetType = data.chipsetType;
8752 mHWData->mParavirtProvider = data.paravirtProvider;
8753 mHWData->mParavirtDebug = data.strParavirtDebug;
8754 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8755 mHWData->mHPETEnabled = data.fHPETEnabled;
8756
8757 /* GraphicsAdapter */
8758 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8759 if (FAILED(rc)) return rc;
8760
8761 /* VRDEServer */
8762 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8763 if (FAILED(rc)) return rc;
8764
8765 /* BIOS */
8766 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8767 if (FAILED(rc)) return rc;
8768
8769 /* Recording settings */
8770 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8771 if (FAILED(rc)) return rc;
8772
8773 // Bandwidth control (must come before network adapters)
8774 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8775 if (FAILED(rc)) return rc;
8776
8777 /* USB controllers */
8778 for (settings::USBControllerList::const_iterator
8779 it = data.usbSettings.llUSBControllers.begin();
8780 it != data.usbSettings.llUSBControllers.end();
8781 ++it)
8782 {
8783 const settings::USBController &settingsCtrl = *it;
8784 ComObjPtr<USBController> newCtrl;
8785
8786 newCtrl.createObject();
8787 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8788 mUSBControllers->push_back(newCtrl);
8789 }
8790
8791 /* USB device filters */
8792 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8793 if (FAILED(rc)) return rc;
8794
8795 // network adapters (establish array size first and apply defaults, to
8796 // ensure reading the same settings as we saved, since the list skips
8797 // adapters having defaults)
8798 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8799 size_t oldCount = mNetworkAdapters.size();
8800 if (newCount > oldCount)
8801 {
8802 mNetworkAdapters.resize(newCount);
8803 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8804 {
8805 unconst(mNetworkAdapters[slot]).createObject();
8806 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8807 }
8808 }
8809 else if (newCount < oldCount)
8810 mNetworkAdapters.resize(newCount);
8811 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8812 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8813 for (settings::NetworkAdaptersList::const_iterator
8814 it = data.llNetworkAdapters.begin();
8815 it != data.llNetworkAdapters.end();
8816 ++it)
8817 {
8818 const settings::NetworkAdapter &nic = *it;
8819
8820 /* slot uniqueness is guaranteed by XML Schema */
8821 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8822 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8823 if (FAILED(rc)) return rc;
8824 }
8825
8826 // serial ports (establish defaults first, to ensure reading the same
8827 // settings as we saved, since the list skips ports having defaults)
8828 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8829 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8830 for (settings::SerialPortsList::const_iterator
8831 it = data.llSerialPorts.begin();
8832 it != data.llSerialPorts.end();
8833 ++it)
8834 {
8835 const settings::SerialPort &s = *it;
8836
8837 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8838 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8839 if (FAILED(rc)) return rc;
8840 }
8841
8842 // parallel ports (establish defaults first, to ensure reading the same
8843 // settings as we saved, since the list skips ports having defaults)
8844 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8845 mParallelPorts[i]->i_applyDefaults();
8846 for (settings::ParallelPortsList::const_iterator
8847 it = data.llParallelPorts.begin();
8848 it != data.llParallelPorts.end();
8849 ++it)
8850 {
8851 const settings::ParallelPort &p = *it;
8852
8853 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8854 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8855 if (FAILED(rc)) return rc;
8856 }
8857
8858 /* AudioAdapter */
8859 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8860 if (FAILED(rc)) return rc;
8861
8862 /* storage controllers */
8863 rc = i_loadStorageControllers(data.storage,
8864 puuidRegistry,
8865 puuidSnapshot);
8866 if (FAILED(rc)) return rc;
8867
8868 /* Shared folders */
8869 for (settings::SharedFoldersList::const_iterator
8870 it = data.llSharedFolders.begin();
8871 it != data.llSharedFolders.end();
8872 ++it)
8873 {
8874 const settings::SharedFolder &sf = *it;
8875
8876 ComObjPtr<SharedFolder> sharedFolder;
8877 /* Check for double entries. Not allowed! */
8878 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8879 if (SUCCEEDED(rc))
8880 return setError(VBOX_E_OBJECT_IN_USE,
8881 tr("Shared folder named '%s' already exists"),
8882 sf.strName.c_str());
8883
8884 /* Create the new shared folder. Don't break on error. This will be
8885 * reported when the machine starts. */
8886 sharedFolder.createObject();
8887 rc = sharedFolder->init(i_getMachine(),
8888 sf.strName,
8889 sf.strHostPath,
8890 RT_BOOL(sf.fWritable),
8891 RT_BOOL(sf.fAutoMount),
8892 sf.strAutoMountPoint,
8893 false /* fFailOnError */);
8894 if (FAILED(rc)) return rc;
8895 mHWData->mSharedFolders.push_back(sharedFolder);
8896 }
8897
8898 // Clipboard
8899 mHWData->mClipboardMode = data.clipboardMode;
8900 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8901
8902 // drag'n'drop
8903 mHWData->mDnDMode = data.dndMode;
8904
8905 // guest settings
8906 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8907
8908 // IO settings
8909 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8910 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8911
8912 // Host PCI devices
8913 for (settings::HostPCIDeviceAttachmentList::const_iterator
8914 it = data.pciAttachments.begin();
8915 it != data.pciAttachments.end();
8916 ++it)
8917 {
8918 const settings::HostPCIDeviceAttachment &hpda = *it;
8919 ComObjPtr<PCIDeviceAttachment> pda;
8920
8921 pda.createObject();
8922 pda->i_loadSettings(this, hpda);
8923 mHWData->mPCIDeviceAssignments.push_back(pda);
8924 }
8925
8926 /*
8927 * (The following isn't really real hardware, but it lives in HWData
8928 * for reasons of convenience.)
8929 */
8930
8931#ifdef VBOX_WITH_GUEST_PROPS
8932 /* Guest properties (optional) */
8933
8934 /* Only load transient guest properties for configs which have saved
8935 * state, because there shouldn't be any for powered off VMs. The same
8936 * logic applies for snapshots, as offline snapshots shouldn't have
8937 * any such properties. They confuse the code in various places.
8938 * Note: can't rely on the machine state, as it isn't set yet. */
8939 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8940 /* apologies for the hacky unconst() usage, but this needs hacking
8941 * actually inconsistent settings into consistency, otherwise there
8942 * will be some corner cases where the inconsistency survives
8943 * surprisingly long without getting fixed, especially for snapshots
8944 * as there are no config changes. */
8945 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8946 for (settings::GuestPropertiesList::iterator
8947 it = llGuestProperties.begin();
8948 it != llGuestProperties.end();
8949 /*nothing*/)
8950 {
8951 const settings::GuestProperty &prop = *it;
8952 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8953 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8954 if ( fSkipTransientGuestProperties
8955 && ( fFlags & GUEST_PROP_F_TRANSIENT
8956 || fFlags & GUEST_PROP_F_TRANSRESET))
8957 {
8958 it = llGuestProperties.erase(it);
8959 continue;
8960 }
8961 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8962 mHWData->mGuestProperties[prop.strName] = property;
8963 ++it;
8964 }
8965#endif /* VBOX_WITH_GUEST_PROPS defined */
8966
8967 rc = i_loadDebugging(pDbg);
8968 if (FAILED(rc))
8969 return rc;
8970
8971 mHWData->mAutostart = *pAutostart;
8972
8973 /* default frontend */
8974 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8975 }
8976 catch (std::bad_alloc &)
8977 {
8978 return E_OUTOFMEMORY;
8979 }
8980
8981 AssertComRC(rc);
8982 return rc;
8983}
8984
8985/**
8986 * Called from i_loadHardware() to load the debugging settings of the
8987 * machine.
8988 *
8989 * @param pDbg Pointer to the settings.
8990 */
8991HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8992{
8993 mHWData->mDebugging = *pDbg;
8994 /* no more processing currently required, this will probably change. */
8995 return S_OK;
8996}
8997
8998/**
8999 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9000 *
9001 * @param data storage settings.
9002 * @param puuidRegistry media registry ID to set media to or NULL;
9003 * see Machine::i_loadMachineDataFromSettings()
9004 * @param puuidSnapshot snapshot ID
9005 * @return
9006 */
9007HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9008 const Guid *puuidRegistry,
9009 const Guid *puuidSnapshot)
9010{
9011 AssertReturn(!i_isSessionMachine(), E_FAIL);
9012
9013 HRESULT rc = S_OK;
9014
9015 for (settings::StorageControllersList::const_iterator
9016 it = data.llStorageControllers.begin();
9017 it != data.llStorageControllers.end();
9018 ++it)
9019 {
9020 const settings::StorageController &ctlData = *it;
9021
9022 ComObjPtr<StorageController> pCtl;
9023 /* Try to find one with the name first. */
9024 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9025 if (SUCCEEDED(rc))
9026 return setError(VBOX_E_OBJECT_IN_USE,
9027 tr("Storage controller named '%s' already exists"),
9028 ctlData.strName.c_str());
9029
9030 pCtl.createObject();
9031 rc = pCtl->init(this,
9032 ctlData.strName,
9033 ctlData.storageBus,
9034 ctlData.ulInstance,
9035 ctlData.fBootable);
9036 if (FAILED(rc)) return rc;
9037
9038 mStorageControllers->push_back(pCtl);
9039
9040 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9041 if (FAILED(rc)) return rc;
9042
9043 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9044 if (FAILED(rc)) return rc;
9045
9046 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9047 if (FAILED(rc)) return rc;
9048
9049 /* Load the attached devices now. */
9050 rc = i_loadStorageDevices(pCtl,
9051 ctlData,
9052 puuidRegistry,
9053 puuidSnapshot);
9054 if (FAILED(rc)) return rc;
9055 }
9056
9057 return S_OK;
9058}
9059
9060/**
9061 * Called from i_loadStorageControllers for a controller's devices.
9062 *
9063 * @param aStorageController
9064 * @param data
9065 * @param puuidRegistry media registry ID to set media to or NULL; see
9066 * Machine::i_loadMachineDataFromSettings()
9067 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9068 * @return
9069 */
9070HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9071 const settings::StorageController &data,
9072 const Guid *puuidRegistry,
9073 const Guid *puuidSnapshot)
9074{
9075 HRESULT rc = S_OK;
9076
9077 /* paranoia: detect duplicate attachments */
9078 for (settings::AttachedDevicesList::const_iterator
9079 it = data.llAttachedDevices.begin();
9080 it != data.llAttachedDevices.end();
9081 ++it)
9082 {
9083 const settings::AttachedDevice &ad = *it;
9084
9085 for (settings::AttachedDevicesList::const_iterator it2 = it;
9086 it2 != data.llAttachedDevices.end();
9087 ++it2)
9088 {
9089 if (it == it2)
9090 continue;
9091
9092 const settings::AttachedDevice &ad2 = *it2;
9093
9094 if ( ad.lPort == ad2.lPort
9095 && ad.lDevice == ad2.lDevice)
9096 {
9097 return setError(E_FAIL,
9098 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9099 aStorageController->i_getName().c_str(),
9100 ad.lPort,
9101 ad.lDevice,
9102 mUserData->s.strName.c_str());
9103 }
9104 }
9105 }
9106
9107 for (settings::AttachedDevicesList::const_iterator
9108 it = data.llAttachedDevices.begin();
9109 it != data.llAttachedDevices.end();
9110 ++it)
9111 {
9112 const settings::AttachedDevice &dev = *it;
9113 ComObjPtr<Medium> medium;
9114
9115 switch (dev.deviceType)
9116 {
9117 case DeviceType_Floppy:
9118 case DeviceType_DVD:
9119 if (dev.strHostDriveSrc.isNotEmpty())
9120 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9121 false /* fRefresh */, medium);
9122 else
9123 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9124 dev.uuid,
9125 false /* fRefresh */,
9126 false /* aSetError */,
9127 medium);
9128 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9129 // This is not an error. The host drive or UUID might have vanished, so just go
9130 // ahead without this removeable medium attachment
9131 rc = S_OK;
9132 break;
9133
9134 case DeviceType_HardDisk:
9135 {
9136 /* find a hard disk by UUID */
9137 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9138 if (FAILED(rc))
9139 {
9140 if (i_isSnapshotMachine())
9141 {
9142 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9143 // so the user knows that the bad disk is in a snapshot somewhere
9144 com::ErrorInfo info;
9145 return setError(E_FAIL,
9146 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9147 puuidSnapshot->raw(),
9148 info.getText().raw());
9149 }
9150 else
9151 return rc;
9152 }
9153
9154 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9155
9156 if (medium->i_getType() == MediumType_Immutable)
9157 {
9158 if (i_isSnapshotMachine())
9159 return setError(E_FAIL,
9160 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9161 "of the virtual machine '%s' ('%s')"),
9162 medium->i_getLocationFull().c_str(),
9163 dev.uuid.raw(),
9164 puuidSnapshot->raw(),
9165 mUserData->s.strName.c_str(),
9166 mData->m_strConfigFileFull.c_str());
9167
9168 return setError(E_FAIL,
9169 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9170 medium->i_getLocationFull().c_str(),
9171 dev.uuid.raw(),
9172 mUserData->s.strName.c_str(),
9173 mData->m_strConfigFileFull.c_str());
9174 }
9175
9176 if (medium->i_getType() == MediumType_MultiAttach)
9177 {
9178 if (i_isSnapshotMachine())
9179 return setError(E_FAIL,
9180 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9181 "of the virtual machine '%s' ('%s')"),
9182 medium->i_getLocationFull().c_str(),
9183 dev.uuid.raw(),
9184 puuidSnapshot->raw(),
9185 mUserData->s.strName.c_str(),
9186 mData->m_strConfigFileFull.c_str());
9187
9188 return setError(E_FAIL,
9189 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9190 medium->i_getLocationFull().c_str(),
9191 dev.uuid.raw(),
9192 mUserData->s.strName.c_str(),
9193 mData->m_strConfigFileFull.c_str());
9194 }
9195
9196 if ( !i_isSnapshotMachine()
9197 && medium->i_getChildren().size() != 0
9198 )
9199 return setError(E_FAIL,
9200 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9201 "because it has %d differencing child hard disks"),
9202 medium->i_getLocationFull().c_str(),
9203 dev.uuid.raw(),
9204 mUserData->s.strName.c_str(),
9205 mData->m_strConfigFileFull.c_str(),
9206 medium->i_getChildren().size());
9207
9208 if (i_findAttachment(*mMediumAttachments.data(),
9209 medium))
9210 return setError(E_FAIL,
9211 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9212 medium->i_getLocationFull().c_str(),
9213 dev.uuid.raw(),
9214 mUserData->s.strName.c_str(),
9215 mData->m_strConfigFileFull.c_str());
9216
9217 break;
9218 }
9219
9220 default:
9221 return setError(E_FAIL,
9222 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9223 medium->i_getLocationFull().c_str(),
9224 mUserData->s.strName.c_str(),
9225 mData->m_strConfigFileFull.c_str());
9226 }
9227
9228 if (FAILED(rc))
9229 break;
9230
9231 /* Bandwidth groups are loaded at this point. */
9232 ComObjPtr<BandwidthGroup> pBwGroup;
9233
9234 if (!dev.strBwGroup.isEmpty())
9235 {
9236 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9237 if (FAILED(rc))
9238 return setError(E_FAIL,
9239 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9240 medium->i_getLocationFull().c_str(),
9241 dev.strBwGroup.c_str(),
9242 mUserData->s.strName.c_str(),
9243 mData->m_strConfigFileFull.c_str());
9244 pBwGroup->i_reference();
9245 }
9246
9247 const Utf8Str controllerName = aStorageController->i_getName();
9248 ComObjPtr<MediumAttachment> pAttachment;
9249 pAttachment.createObject();
9250 rc = pAttachment->init(this,
9251 medium,
9252 controllerName,
9253 dev.lPort,
9254 dev.lDevice,
9255 dev.deviceType,
9256 false,
9257 dev.fPassThrough,
9258 dev.fTempEject,
9259 dev.fNonRotational,
9260 dev.fDiscard,
9261 dev.fHotPluggable,
9262 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9263 if (FAILED(rc)) break;
9264
9265 /* associate the medium with this machine and snapshot */
9266 if (!medium.isNull())
9267 {
9268 AutoCaller medCaller(medium);
9269 if (FAILED(medCaller.rc())) return medCaller.rc();
9270 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9271
9272 if (i_isSnapshotMachine())
9273 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9274 else
9275 rc = medium->i_addBackReference(mData->mUuid);
9276 /* If the medium->addBackReference fails it sets an appropriate
9277 * error message, so no need to do any guesswork here. */
9278
9279 if (puuidRegistry)
9280 // caller wants registry ID to be set on all attached media (OVF import case)
9281 medium->i_addRegistry(*puuidRegistry);
9282 }
9283
9284 if (FAILED(rc))
9285 break;
9286
9287 /* back up mMediumAttachments to let registeredInit() properly rollback
9288 * on failure (= limited accessibility) */
9289 i_setModified(IsModified_Storage);
9290 mMediumAttachments.backup();
9291 mMediumAttachments->push_back(pAttachment);
9292 }
9293
9294 return rc;
9295}
9296
9297/**
9298 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9299 *
9300 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9301 * @param aSnapshot where to return the found snapshot
9302 * @param aSetError true to set extended error info on failure
9303 */
9304HRESULT Machine::i_findSnapshotById(const Guid &aId,
9305 ComObjPtr<Snapshot> &aSnapshot,
9306 bool aSetError /* = false */)
9307{
9308 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9309
9310 if (!mData->mFirstSnapshot)
9311 {
9312 if (aSetError)
9313 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9314 return E_FAIL;
9315 }
9316
9317 if (aId.isZero())
9318 aSnapshot = mData->mFirstSnapshot;
9319 else
9320 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9321
9322 if (!aSnapshot)
9323 {
9324 if (aSetError)
9325 return setError(E_FAIL,
9326 tr("Could not find a snapshot with UUID {%s}"),
9327 aId.toString().c_str());
9328 return E_FAIL;
9329 }
9330
9331 return S_OK;
9332}
9333
9334/**
9335 * Returns the snapshot with the given name or fails of no such snapshot.
9336 *
9337 * @param strName snapshot name to find
9338 * @param aSnapshot where to return the found snapshot
9339 * @param aSetError true to set extended error info on failure
9340 */
9341HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9342 ComObjPtr<Snapshot> &aSnapshot,
9343 bool aSetError /* = false */)
9344{
9345 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9346
9347 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9348
9349 if (!mData->mFirstSnapshot)
9350 {
9351 if (aSetError)
9352 return setError(VBOX_E_OBJECT_NOT_FOUND,
9353 tr("This machine does not have any snapshots"));
9354 return VBOX_E_OBJECT_NOT_FOUND;
9355 }
9356
9357 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9358
9359 if (!aSnapshot)
9360 {
9361 if (aSetError)
9362 return setError(VBOX_E_OBJECT_NOT_FOUND,
9363 tr("Could not find a snapshot named '%s'"), strName.c_str());
9364 return VBOX_E_OBJECT_NOT_FOUND;
9365 }
9366
9367 return S_OK;
9368}
9369
9370/**
9371 * Returns a storage controller object with the given name.
9372 *
9373 * @param aName storage controller name to find
9374 * @param aStorageController where to return the found storage controller
9375 * @param aSetError true to set extended error info on failure
9376 */
9377HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9378 ComObjPtr<StorageController> &aStorageController,
9379 bool aSetError /* = false */)
9380{
9381 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9382
9383 for (StorageControllerList::const_iterator
9384 it = mStorageControllers->begin();
9385 it != mStorageControllers->end();
9386 ++it)
9387 {
9388 if ((*it)->i_getName() == aName)
9389 {
9390 aStorageController = (*it);
9391 return S_OK;
9392 }
9393 }
9394
9395 if (aSetError)
9396 return setError(VBOX_E_OBJECT_NOT_FOUND,
9397 tr("Could not find a storage controller named '%s'"),
9398 aName.c_str());
9399 return VBOX_E_OBJECT_NOT_FOUND;
9400}
9401
9402/**
9403 * Returns a USB controller object with the given name.
9404 *
9405 * @param aName USB controller name to find
9406 * @param aUSBController where to return the found USB controller
9407 * @param aSetError true to set extended error info on failure
9408 */
9409HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9410 ComObjPtr<USBController> &aUSBController,
9411 bool aSetError /* = false */)
9412{
9413 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9414
9415 for (USBControllerList::const_iterator
9416 it = mUSBControllers->begin();
9417 it != mUSBControllers->end();
9418 ++it)
9419 {
9420 if ((*it)->i_getName() == aName)
9421 {
9422 aUSBController = (*it);
9423 return S_OK;
9424 }
9425 }
9426
9427 if (aSetError)
9428 return setError(VBOX_E_OBJECT_NOT_FOUND,
9429 tr("Could not find a storage controller named '%s'"),
9430 aName.c_str());
9431 return VBOX_E_OBJECT_NOT_FOUND;
9432}
9433
9434/**
9435 * Returns the number of USB controller instance of the given type.
9436 *
9437 * @param enmType USB controller type.
9438 */
9439ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9440{
9441 ULONG cCtrls = 0;
9442
9443 for (USBControllerList::const_iterator
9444 it = mUSBControllers->begin();
9445 it != mUSBControllers->end();
9446 ++it)
9447 {
9448 if ((*it)->i_getControllerType() == enmType)
9449 cCtrls++;
9450 }
9451
9452 return cCtrls;
9453}
9454
9455HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9456 MediumAttachmentList &atts)
9457{
9458 AutoCaller autoCaller(this);
9459 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9460
9461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9462
9463 for (MediumAttachmentList::const_iterator
9464 it = mMediumAttachments->begin();
9465 it != mMediumAttachments->end();
9466 ++it)
9467 {
9468 const ComObjPtr<MediumAttachment> &pAtt = *it;
9469 // should never happen, but deal with NULL pointers in the list.
9470 AssertContinue(!pAtt.isNull());
9471
9472 // getControllerName() needs caller+read lock
9473 AutoCaller autoAttCaller(pAtt);
9474 if (FAILED(autoAttCaller.rc()))
9475 {
9476 atts.clear();
9477 return autoAttCaller.rc();
9478 }
9479 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9480
9481 if (pAtt->i_getControllerName() == aName)
9482 atts.push_back(pAtt);
9483 }
9484
9485 return S_OK;
9486}
9487
9488
9489/**
9490 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9491 * file if the machine name was changed and about creating a new settings file
9492 * if this is a new machine.
9493 *
9494 * @note Must be never called directly but only from #saveSettings().
9495 */
9496HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9497{
9498 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9499
9500 HRESULT rc = S_OK;
9501
9502 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9503
9504 /// @todo need to handle primary group change, too
9505
9506 /* attempt to rename the settings file if machine name is changed */
9507 if ( mUserData->s.fNameSync
9508 && mUserData.isBackedUp()
9509 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9510 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9511 )
9512 {
9513 bool dirRenamed = false;
9514 bool fileRenamed = false;
9515
9516 Utf8Str configFile, newConfigFile;
9517 Utf8Str configFilePrev, newConfigFilePrev;
9518 Utf8Str NVRAMFile, newNVRAMFile;
9519 Utf8Str configDir, newConfigDir;
9520
9521 do
9522 {
9523 int vrc = VINF_SUCCESS;
9524
9525 Utf8Str name = mUserData.backedUpData()->s.strName;
9526 Utf8Str newName = mUserData->s.strName;
9527 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9528 if (group == "/")
9529 group.setNull();
9530 Utf8Str newGroup = mUserData->s.llGroups.front();
9531 if (newGroup == "/")
9532 newGroup.setNull();
9533
9534 configFile = mData->m_strConfigFileFull;
9535
9536 /* first, rename the directory if it matches the group and machine name */
9537 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9538 /** @todo hack, make somehow use of ComposeMachineFilename */
9539 if (mUserData->s.fDirectoryIncludesUUID)
9540 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9541 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9542 /** @todo hack, make somehow use of ComposeMachineFilename */
9543 if (mUserData->s.fDirectoryIncludesUUID)
9544 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9545 configDir = configFile;
9546 configDir.stripFilename();
9547 newConfigDir = configDir;
9548 if ( configDir.length() >= groupPlusName.length()
9549 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9550 groupPlusName.c_str()))
9551 {
9552 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9553 Utf8Str newConfigBaseDir(newConfigDir);
9554 newConfigDir.append(newGroupPlusName);
9555 /* consistency: use \ if appropriate on the platform */
9556 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9557 /* new dir and old dir cannot be equal here because of 'if'
9558 * above and because name != newName */
9559 Assert(configDir != newConfigDir);
9560 if (!fSettingsFileIsNew)
9561 {
9562 /* perform real rename only if the machine is not new */
9563 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9564 if ( vrc == VERR_FILE_NOT_FOUND
9565 || vrc == VERR_PATH_NOT_FOUND)
9566 {
9567 /* create the parent directory, then retry renaming */
9568 Utf8Str parent(newConfigDir);
9569 parent.stripFilename();
9570 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9571 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9572 }
9573 if (RT_FAILURE(vrc))
9574 {
9575 rc = setErrorBoth(E_FAIL, vrc,
9576 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9577 configDir.c_str(),
9578 newConfigDir.c_str(),
9579 vrc);
9580 break;
9581 }
9582 /* delete subdirectories which are no longer needed */
9583 Utf8Str dir(configDir);
9584 dir.stripFilename();
9585 while (dir != newConfigBaseDir && dir != ".")
9586 {
9587 vrc = RTDirRemove(dir.c_str());
9588 if (RT_FAILURE(vrc))
9589 break;
9590 dir.stripFilename();
9591 }
9592 dirRenamed = true;
9593 }
9594 }
9595
9596 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9597
9598 /* then try to rename the settings file itself */
9599 if (newConfigFile != configFile)
9600 {
9601 /* get the path to old settings file in renamed directory */
9602 configFile.printf("%s%c%s",
9603 newConfigDir.c_str(),
9604 RTPATH_DELIMITER,
9605 RTPathFilename(configFile.c_str()));
9606 if (!fSettingsFileIsNew)
9607 {
9608 /* perform real rename only if the machine is not new */
9609 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9610 if (RT_FAILURE(vrc))
9611 {
9612 rc = setErrorBoth(E_FAIL, vrc,
9613 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9614 configFile.c_str(),
9615 newConfigFile.c_str(),
9616 vrc);
9617 break;
9618 }
9619 fileRenamed = true;
9620 configFilePrev = configFile;
9621 configFilePrev += "-prev";
9622 newConfigFilePrev = newConfigFile;
9623 newConfigFilePrev += "-prev";
9624 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9625 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9626 if (NVRAMFile.isNotEmpty())
9627 {
9628 // in the NVRAM file path, replace the old directory with the new directory
9629 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9630 {
9631 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9632 NVRAMFile = newConfigDir + strNVRAMFile;
9633 }
9634 newNVRAMFile = newConfigFile;
9635 newNVRAMFile.stripSuffix();
9636 newNVRAMFile += ".nvram";
9637 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9638 }
9639 }
9640 }
9641
9642 // update m_strConfigFileFull amd mConfigFile
9643 mData->m_strConfigFileFull = newConfigFile;
9644 // compute the relative path too
9645 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9646
9647 // store the old and new so that VirtualBox::i_saveSettings() can update
9648 // the media registry
9649 if ( mData->mRegistered
9650 && (configDir != newConfigDir || configFile != newConfigFile))
9651 {
9652 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9653
9654 if (pfNeedsGlobalSaveSettings)
9655 *pfNeedsGlobalSaveSettings = true;
9656 }
9657
9658 // in the saved state file path, replace the old directory with the new directory
9659 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9660 {
9661 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9662 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9663 }
9664 if (newNVRAMFile.isNotEmpty())
9665 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9666
9667 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9668 if (mData->mFirstSnapshot)
9669 {
9670 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9671 newConfigDir.c_str());
9672 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9673 newConfigDir.c_str());
9674 }
9675 }
9676 while (0);
9677
9678 if (FAILED(rc))
9679 {
9680 /* silently try to rename everything back */
9681 if (fileRenamed)
9682 {
9683 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9684 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9685 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9686 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9687 }
9688 if (dirRenamed)
9689 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9690 }
9691
9692 if (FAILED(rc)) return rc;
9693 }
9694
9695 if (fSettingsFileIsNew)
9696 {
9697 /* create a virgin config file */
9698 int vrc = VINF_SUCCESS;
9699
9700 /* ensure the settings directory exists */
9701 Utf8Str path(mData->m_strConfigFileFull);
9702 path.stripFilename();
9703 if (!RTDirExists(path.c_str()))
9704 {
9705 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9706 if (RT_FAILURE(vrc))
9707 {
9708 return setErrorBoth(E_FAIL, vrc,
9709 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9710 path.c_str(),
9711 vrc);
9712 }
9713 }
9714
9715 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9716 path = Utf8Str(mData->m_strConfigFileFull);
9717 RTFILE f = NIL_RTFILE;
9718 vrc = RTFileOpen(&f, path.c_str(),
9719 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9720 if (RT_FAILURE(vrc))
9721 return setErrorBoth(E_FAIL, vrc,
9722 tr("Could not create the settings file '%s' (%Rrc)"),
9723 path.c_str(),
9724 vrc);
9725 RTFileClose(f);
9726 }
9727
9728 return rc;
9729}
9730
9731/**
9732 * Saves and commits machine data, user data and hardware data.
9733 *
9734 * Note that on failure, the data remains uncommitted.
9735 *
9736 * @a aFlags may combine the following flags:
9737 *
9738 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9739 * Used when saving settings after an operation that makes them 100%
9740 * correspond to the settings from the current snapshot.
9741 * - SaveS_Force: settings will be saved without doing a deep compare of the
9742 * settings structures. This is used when this is called because snapshots
9743 * have changed to avoid the overhead of the deep compare.
9744 *
9745 * @note Must be called from under this object's write lock. Locks children for
9746 * writing.
9747 *
9748 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9749 * initialized to false and that will be set to true by this function if
9750 * the caller must invoke VirtualBox::i_saveSettings() because the global
9751 * settings have changed. This will happen if a machine rename has been
9752 * saved and the global machine and media registries will therefore need
9753 * updating.
9754 * @param aFlags Flags.
9755 */
9756HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9757 int aFlags /*= 0*/)
9758{
9759 LogFlowThisFuncEnter();
9760
9761 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9762
9763 /* make sure child objects are unable to modify the settings while we are
9764 * saving them */
9765 i_ensureNoStateDependencies();
9766
9767 AssertReturn(!i_isSnapshotMachine(),
9768 E_FAIL);
9769
9770 if (!mData->mAccessible)
9771 return setError(VBOX_E_INVALID_VM_STATE,
9772 tr("The machine is not accessible, so cannot save settings"));
9773
9774 HRESULT rc = S_OK;
9775 bool fNeedsWrite = false;
9776
9777 /* First, prepare to save settings. It will care about renaming the
9778 * settings directory and file if the machine name was changed and about
9779 * creating a new settings file if this is a new machine. */
9780 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9781 if (FAILED(rc)) return rc;
9782
9783 // keep a pointer to the current settings structures
9784 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9785 settings::MachineConfigFile *pNewConfig = NULL;
9786
9787 try
9788 {
9789 // make a fresh one to have everyone write stuff into
9790 pNewConfig = new settings::MachineConfigFile(NULL);
9791 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9792
9793 // now go and copy all the settings data from COM to the settings structures
9794 // (this calls i_saveSettings() on all the COM objects in the machine)
9795 i_copyMachineDataToSettings(*pNewConfig);
9796
9797 if (aFlags & SaveS_ResetCurStateModified)
9798 {
9799 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9800 mData->mCurrentStateModified = FALSE;
9801 fNeedsWrite = true; // always, no need to compare
9802 }
9803 else if (aFlags & SaveS_Force)
9804 {
9805 fNeedsWrite = true; // always, no need to compare
9806 }
9807 else
9808 {
9809 if (!mData->mCurrentStateModified)
9810 {
9811 // do a deep compare of the settings that we just saved with the settings
9812 // previously stored in the config file; this invokes MachineConfigFile::operator==
9813 // which does a deep compare of all the settings, which is expensive but less expensive
9814 // than writing out XML in vain
9815 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9816
9817 // could still be modified if any settings changed
9818 mData->mCurrentStateModified = fAnySettingsChanged;
9819
9820 fNeedsWrite = fAnySettingsChanged;
9821 }
9822 else
9823 fNeedsWrite = true;
9824 }
9825
9826 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9827
9828 if (fNeedsWrite)
9829 // now spit it all out!
9830 pNewConfig->write(mData->m_strConfigFileFull);
9831
9832 mData->pMachineConfigFile = pNewConfig;
9833 delete pOldConfig;
9834 i_commit();
9835
9836 // after saving settings, we are no longer different from the XML on disk
9837 mData->flModifications = 0;
9838 }
9839 catch (HRESULT err)
9840 {
9841 // we assume that error info is set by the thrower
9842 rc = err;
9843
9844 // restore old config
9845 delete pNewConfig;
9846 mData->pMachineConfigFile = pOldConfig;
9847 }
9848 catch (...)
9849 {
9850 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9851 }
9852
9853 if (fNeedsWrite)
9854 {
9855 /* Fire the data change event, even on failure (since we've already
9856 * committed all data). This is done only for SessionMachines because
9857 * mutable Machine instances are always not registered (i.e. private
9858 * to the client process that creates them) and thus don't need to
9859 * inform callbacks. */
9860 if (i_isSessionMachine())
9861 mParent->i_onMachineDataChanged(mData->mUuid);
9862 }
9863
9864 LogFlowThisFunc(("rc=%08X\n", rc));
9865 LogFlowThisFuncLeave();
9866 return rc;
9867}
9868
9869/**
9870 * Implementation for saving the machine settings into the given
9871 * settings::MachineConfigFile instance. This copies machine extradata
9872 * from the previous machine config file in the instance data, if any.
9873 *
9874 * This gets called from two locations:
9875 *
9876 * -- Machine::i_saveSettings(), during the regular XML writing;
9877 *
9878 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9879 * exported to OVF and we write the VirtualBox proprietary XML
9880 * into a <vbox:Machine> tag.
9881 *
9882 * This routine fills all the fields in there, including snapshots, *except*
9883 * for the following:
9884 *
9885 * -- fCurrentStateModified. There is some special logic associated with that.
9886 *
9887 * The caller can then call MachineConfigFile::write() or do something else
9888 * with it.
9889 *
9890 * Caller must hold the machine lock!
9891 *
9892 * This throws XML errors and HRESULT, so the caller must have a catch block!
9893 */
9894void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9895{
9896 // deep copy extradata, being extra careful with self assignment (the STL
9897 // map assignment on Mac OS X clang based Xcode isn't checking)
9898 if (&config != mData->pMachineConfigFile)
9899 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9900
9901 config.uuid = mData->mUuid;
9902
9903 // copy name, description, OS type, teleport, UTC etc.
9904 config.machineUserData = mUserData->s;
9905
9906 if ( mData->mMachineState == MachineState_Saved
9907 || mData->mMachineState == MachineState_Restoring
9908 // when doing certain snapshot operations we may or may not have
9909 // a saved state in the current state, so keep everything as is
9910 || ( ( mData->mMachineState == MachineState_Snapshotting
9911 || mData->mMachineState == MachineState_DeletingSnapshot
9912 || mData->mMachineState == MachineState_RestoringSnapshot)
9913 && (!mSSData->strStateFilePath.isEmpty())
9914 )
9915 )
9916 {
9917 Assert(!mSSData->strStateFilePath.isEmpty());
9918 /* try to make the file name relative to the settings file dir */
9919 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9920 }
9921 else
9922 {
9923 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9924 config.strStateFile.setNull();
9925 }
9926
9927 if (mData->mCurrentSnapshot)
9928 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9929 else
9930 config.uuidCurrentSnapshot.clear();
9931
9932 config.timeLastStateChange = mData->mLastStateChange;
9933 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9934 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9935
9936 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9937 if (FAILED(rc)) throw rc;
9938
9939 // save machine's media registry if this is VirtualBox 4.0 or later
9940 if (config.canHaveOwnMediaRegistry())
9941 {
9942 // determine machine folder
9943 Utf8Str strMachineFolder = i_getSettingsFileFull();
9944 strMachineFolder.stripFilename();
9945 mParent->i_saveMediaRegistry(config.mediaRegistry,
9946 i_getId(), // only media with registry ID == machine UUID
9947 strMachineFolder);
9948 // this throws HRESULT
9949 }
9950
9951 // save snapshots
9952 rc = i_saveAllSnapshots(config);
9953 if (FAILED(rc)) throw rc;
9954}
9955
9956/**
9957 * Saves all snapshots of the machine into the given machine config file. Called
9958 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9959 * @param config
9960 * @return
9961 */
9962HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9963{
9964 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9965
9966 HRESULT rc = S_OK;
9967
9968 try
9969 {
9970 config.llFirstSnapshot.clear();
9971
9972 if (mData->mFirstSnapshot)
9973 {
9974 // the settings use a list for "the first snapshot"
9975 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9976
9977 // get reference to the snapshot on the list and work on that
9978 // element straight in the list to avoid excessive copying later
9979 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9980 if (FAILED(rc)) throw rc;
9981 }
9982
9983// if (mType == IsSessionMachine)
9984// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9985
9986 }
9987 catch (HRESULT err)
9988 {
9989 /* we assume that error info is set by the thrower */
9990 rc = err;
9991 }
9992 catch (...)
9993 {
9994 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9995 }
9996
9997 return rc;
9998}
9999
10000/**
10001 * Saves the VM hardware configuration. It is assumed that the
10002 * given node is empty.
10003 *
10004 * @param data Reference to the settings object for the hardware config.
10005 * @param pDbg Pointer to the settings object for the debugging config
10006 * which happens to live in mHWData.
10007 * @param pAutostart Pointer to the settings object for the autostart config
10008 * which happens to live in mHWData.
10009 */
10010HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10011 settings::Autostart *pAutostart)
10012{
10013 HRESULT rc = S_OK;
10014
10015 try
10016 {
10017 /* The hardware version attribute (optional).
10018 Automatically upgrade from 1 to current default hardware version
10019 when there is no saved state. (ugly!) */
10020 if ( mHWData->mHWVersion == "1"
10021 && mSSData->strStateFilePath.isEmpty()
10022 )
10023 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10024
10025 data.strVersion = mHWData->mHWVersion;
10026 data.uuid = mHWData->mHardwareUUID;
10027
10028 // CPU
10029 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10030 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10031 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10032 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10033 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10034 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10035 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10036 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10037 data.fPAE = !!mHWData->mPAEEnabled;
10038 data.enmLongMode = mHWData->mLongMode;
10039 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10040 data.fAPIC = !!mHWData->mAPIC;
10041 data.fX2APIC = !!mHWData->mX2APIC;
10042 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10043 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10044 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10045 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10046 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10047 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10048 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10049 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10050 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10051 data.cCPUs = mHWData->mCPUCount;
10052 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10053 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10054 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10055 data.strCpuProfile = mHWData->mCpuProfile;
10056
10057 data.llCpus.clear();
10058 if (data.fCpuHotPlug)
10059 {
10060 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10061 {
10062 if (mHWData->mCPUAttached[idx])
10063 {
10064 settings::Cpu cpu;
10065 cpu.ulId = idx;
10066 data.llCpus.push_back(cpu);
10067 }
10068 }
10069 }
10070
10071 /* Standard and Extended CPUID leafs. */
10072 data.llCpuIdLeafs.clear();
10073 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10074
10075 // memory
10076 data.ulMemorySizeMB = mHWData->mMemorySize;
10077 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10078
10079 // firmware
10080 data.firmwareType = mHWData->mFirmwareType;
10081
10082 // HID
10083 data.pointingHIDType = mHWData->mPointingHIDType;
10084 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10085
10086 // chipset
10087 data.chipsetType = mHWData->mChipsetType;
10088
10089 // paravirt
10090 data.paravirtProvider = mHWData->mParavirtProvider;
10091 data.strParavirtDebug = mHWData->mParavirtDebug;
10092
10093 // emulated USB card reader
10094 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10095
10096 // HPET
10097 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10098
10099 // boot order
10100 data.mapBootOrder.clear();
10101 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10102 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10103
10104 /* VRDEServer settings (optional) */
10105 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10106 if (FAILED(rc)) throw rc;
10107
10108 /* BIOS settings (required) */
10109 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10110 if (FAILED(rc)) throw rc;
10111
10112 /* Recording settings (required) */
10113 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10114 if (FAILED(rc)) throw rc;
10115
10116 /* GraphicsAdapter settings (required) */
10117 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10118 if (FAILED(rc)) throw rc;
10119
10120 /* USB Controller (required) */
10121 data.usbSettings.llUSBControllers.clear();
10122 for (USBControllerList::const_iterator
10123 it = mUSBControllers->begin();
10124 it != mUSBControllers->end();
10125 ++it)
10126 {
10127 ComObjPtr<USBController> ctrl = *it;
10128 settings::USBController settingsCtrl;
10129
10130 settingsCtrl.strName = ctrl->i_getName();
10131 settingsCtrl.enmType = ctrl->i_getControllerType();
10132
10133 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10134 }
10135
10136 /* USB device filters (required) */
10137 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10138 if (FAILED(rc)) throw rc;
10139
10140 /* Network adapters (required) */
10141 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10142 data.llNetworkAdapters.clear();
10143 /* Write out only the nominal number of network adapters for this
10144 * chipset type. Since Machine::commit() hasn't been called there
10145 * may be extra NIC settings in the vector. */
10146 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10147 {
10148 settings::NetworkAdapter nic;
10149 nic.ulSlot = (uint32_t)slot;
10150 /* paranoia check... must not be NULL, but must not crash either. */
10151 if (mNetworkAdapters[slot])
10152 {
10153 if (mNetworkAdapters[slot]->i_hasDefaults())
10154 continue;
10155
10156 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10157 if (FAILED(rc)) throw rc;
10158
10159 data.llNetworkAdapters.push_back(nic);
10160 }
10161 }
10162
10163 /* Serial ports */
10164 data.llSerialPorts.clear();
10165 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10166 {
10167 if (mSerialPorts[slot]->i_hasDefaults())
10168 continue;
10169
10170 settings::SerialPort s;
10171 s.ulSlot = slot;
10172 rc = mSerialPorts[slot]->i_saveSettings(s);
10173 if (FAILED(rc)) return rc;
10174
10175 data.llSerialPorts.push_back(s);
10176 }
10177
10178 /* Parallel ports */
10179 data.llParallelPorts.clear();
10180 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10181 {
10182 if (mParallelPorts[slot]->i_hasDefaults())
10183 continue;
10184
10185 settings::ParallelPort p;
10186 p.ulSlot = slot;
10187 rc = mParallelPorts[slot]->i_saveSettings(p);
10188 if (FAILED(rc)) return rc;
10189
10190 data.llParallelPorts.push_back(p);
10191 }
10192
10193 /* Audio adapter */
10194 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10195 if (FAILED(rc)) return rc;
10196
10197 rc = i_saveStorageControllers(data.storage);
10198 if (FAILED(rc)) return rc;
10199
10200 /* Shared folders */
10201 data.llSharedFolders.clear();
10202 for (HWData::SharedFolderList::const_iterator
10203 it = mHWData->mSharedFolders.begin();
10204 it != mHWData->mSharedFolders.end();
10205 ++it)
10206 {
10207 SharedFolder *pSF = *it;
10208 AutoCaller sfCaller(pSF);
10209 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10210 settings::SharedFolder sf;
10211 sf.strName = pSF->i_getName();
10212 sf.strHostPath = pSF->i_getHostPath();
10213 sf.fWritable = !!pSF->i_isWritable();
10214 sf.fAutoMount = !!pSF->i_isAutoMounted();
10215 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10216
10217 data.llSharedFolders.push_back(sf);
10218 }
10219
10220 // clipboard
10221 data.clipboardMode = mHWData->mClipboardMode;
10222 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10223
10224 // drag'n'drop
10225 data.dndMode = mHWData->mDnDMode;
10226
10227 /* Guest */
10228 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10229
10230 // IO settings
10231 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10232 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10233
10234 /* BandwidthControl (required) */
10235 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10236 if (FAILED(rc)) throw rc;
10237
10238 /* Host PCI devices */
10239 data.pciAttachments.clear();
10240 for (HWData::PCIDeviceAssignmentList::const_iterator
10241 it = mHWData->mPCIDeviceAssignments.begin();
10242 it != mHWData->mPCIDeviceAssignments.end();
10243 ++it)
10244 {
10245 ComObjPtr<PCIDeviceAttachment> pda = *it;
10246 settings::HostPCIDeviceAttachment hpda;
10247
10248 rc = pda->i_saveSettings(hpda);
10249 if (FAILED(rc)) throw rc;
10250
10251 data.pciAttachments.push_back(hpda);
10252 }
10253
10254 // guest properties
10255 data.llGuestProperties.clear();
10256#ifdef VBOX_WITH_GUEST_PROPS
10257 for (HWData::GuestPropertyMap::const_iterator
10258 it = mHWData->mGuestProperties.begin();
10259 it != mHWData->mGuestProperties.end();
10260 ++it)
10261 {
10262 HWData::GuestProperty property = it->second;
10263
10264 /* Remove transient guest properties at shutdown unless we
10265 * are saving state. Note that restoring snapshot intentionally
10266 * keeps them, they will be removed if appropriate once the final
10267 * machine state is set (as crashes etc. need to work). */
10268 if ( ( mData->mMachineState == MachineState_PoweredOff
10269 || mData->mMachineState == MachineState_Aborted
10270 || mData->mMachineState == MachineState_Teleported)
10271 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10272 continue;
10273 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10274 prop.strName = it->first;
10275 prop.strValue = property.strValue;
10276 prop.timestamp = (uint64_t)property.mTimestamp;
10277 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10278 GuestPropWriteFlags(property.mFlags, szFlags);
10279 prop.strFlags = szFlags;
10280
10281 data.llGuestProperties.push_back(prop);
10282 }
10283
10284 /* I presume this doesn't require a backup(). */
10285 mData->mGuestPropertiesModified = FALSE;
10286#endif /* VBOX_WITH_GUEST_PROPS defined */
10287
10288 *pDbg = mHWData->mDebugging;
10289 *pAutostart = mHWData->mAutostart;
10290
10291 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10292 }
10293 catch (std::bad_alloc &)
10294 {
10295 return E_OUTOFMEMORY;
10296 }
10297
10298 AssertComRC(rc);
10299 return rc;
10300}
10301
10302/**
10303 * Saves the storage controller configuration.
10304 *
10305 * @param data storage settings.
10306 */
10307HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10308{
10309 data.llStorageControllers.clear();
10310
10311 for (StorageControllerList::const_iterator
10312 it = mStorageControllers->begin();
10313 it != mStorageControllers->end();
10314 ++it)
10315 {
10316 HRESULT rc;
10317 ComObjPtr<StorageController> pCtl = *it;
10318
10319 settings::StorageController ctl;
10320 ctl.strName = pCtl->i_getName();
10321 ctl.controllerType = pCtl->i_getControllerType();
10322 ctl.storageBus = pCtl->i_getStorageBus();
10323 ctl.ulInstance = pCtl->i_getInstance();
10324 ctl.fBootable = pCtl->i_getBootable();
10325
10326 /* Save the port count. */
10327 ULONG portCount;
10328 rc = pCtl->COMGETTER(PortCount)(&portCount);
10329 ComAssertComRCRet(rc, rc);
10330 ctl.ulPortCount = portCount;
10331
10332 /* Save fUseHostIOCache */
10333 BOOL fUseHostIOCache;
10334 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10335 ComAssertComRCRet(rc, rc);
10336 ctl.fUseHostIOCache = !!fUseHostIOCache;
10337
10338 /* save the devices now. */
10339 rc = i_saveStorageDevices(pCtl, ctl);
10340 ComAssertComRCRet(rc, rc);
10341
10342 data.llStorageControllers.push_back(ctl);
10343 }
10344
10345 return S_OK;
10346}
10347
10348/**
10349 * Saves the hard disk configuration.
10350 */
10351HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10352 settings::StorageController &data)
10353{
10354 MediumAttachmentList atts;
10355
10356 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10357 if (FAILED(rc)) return rc;
10358
10359 data.llAttachedDevices.clear();
10360 for (MediumAttachmentList::const_iterator
10361 it = atts.begin();
10362 it != atts.end();
10363 ++it)
10364 {
10365 settings::AttachedDevice dev;
10366 IMediumAttachment *iA = *it;
10367 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10368 Medium *pMedium = pAttach->i_getMedium();
10369
10370 dev.deviceType = pAttach->i_getType();
10371 dev.lPort = pAttach->i_getPort();
10372 dev.lDevice = pAttach->i_getDevice();
10373 dev.fPassThrough = pAttach->i_getPassthrough();
10374 dev.fHotPluggable = pAttach->i_getHotPluggable();
10375 if (pMedium)
10376 {
10377 if (pMedium->i_isHostDrive())
10378 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10379 else
10380 dev.uuid = pMedium->i_getId();
10381 dev.fTempEject = pAttach->i_getTempEject();
10382 dev.fNonRotational = pAttach->i_getNonRotational();
10383 dev.fDiscard = pAttach->i_getDiscard();
10384 }
10385
10386 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10387
10388 data.llAttachedDevices.push_back(dev);
10389 }
10390
10391 return S_OK;
10392}
10393
10394/**
10395 * Saves machine state settings as defined by aFlags
10396 * (SaveSTS_* values).
10397 *
10398 * @param aFlags Combination of SaveSTS_* flags.
10399 *
10400 * @note Locks objects for writing.
10401 */
10402HRESULT Machine::i_saveStateSettings(int aFlags)
10403{
10404 if (aFlags == 0)
10405 return S_OK;
10406
10407 AutoCaller autoCaller(this);
10408 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10409
10410 /* This object's write lock is also necessary to serialize file access
10411 * (prevent concurrent reads and writes) */
10412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10413
10414 HRESULT rc = S_OK;
10415
10416 Assert(mData->pMachineConfigFile);
10417
10418 try
10419 {
10420 if (aFlags & SaveSTS_CurStateModified)
10421 mData->pMachineConfigFile->fCurrentStateModified = true;
10422
10423 if (aFlags & SaveSTS_StateFilePath)
10424 {
10425 if (!mSSData->strStateFilePath.isEmpty())
10426 /* try to make the file name relative to the settings file dir */
10427 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10428 else
10429 mData->pMachineConfigFile->strStateFile.setNull();
10430 }
10431
10432 if (aFlags & SaveSTS_StateTimeStamp)
10433 {
10434 Assert( mData->mMachineState != MachineState_Aborted
10435 || mSSData->strStateFilePath.isEmpty());
10436
10437 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10438
10439 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10440/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10441 }
10442
10443 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10444 }
10445 catch (...)
10446 {
10447 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10448 }
10449
10450 return rc;
10451}
10452
10453/**
10454 * Ensures that the given medium is added to a media registry. If this machine
10455 * was created with 4.0 or later, then the machine registry is used. Otherwise
10456 * the global VirtualBox media registry is used.
10457 *
10458 * Caller must NOT hold machine lock, media tree or any medium locks!
10459 *
10460 * @param pMedium
10461 */
10462void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10463{
10464 /* Paranoia checks: do not hold machine or media tree locks. */
10465 AssertReturnVoid(!isWriteLockOnCurrentThread());
10466 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10467
10468 ComObjPtr<Medium> pBase;
10469 {
10470 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10471 pBase = pMedium->i_getBase();
10472 }
10473
10474 /* Paranoia checks: do not hold medium locks. */
10475 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10476 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10477
10478 // decide which medium registry to use now that the medium is attached:
10479 Guid uuid;
10480 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10481 if (fCanHaveOwnMediaRegistry)
10482 // machine XML is VirtualBox 4.0 or higher:
10483 uuid = i_getId(); // machine UUID
10484 else
10485 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10486
10487 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10488 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10489 if (pMedium->i_addRegistry(uuid))
10490 mParent->i_markRegistryModified(uuid);
10491
10492 /* For more complex hard disk structures it can happen that the base
10493 * medium isn't yet associated with any medium registry. Do that now. */
10494 if (pMedium != pBase)
10495 {
10496 /* Tree lock needed by Medium::addRegistry when recursing. */
10497 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10498 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10499 {
10500 treeLock.release();
10501 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10502 treeLock.acquire();
10503 }
10504 if (pBase->i_addRegistryRecursive(uuid))
10505 {
10506 treeLock.release();
10507 mParent->i_markRegistryModified(uuid);
10508 }
10509 }
10510}
10511
10512/**
10513 * Creates differencing hard disks for all normal hard disks attached to this
10514 * machine and a new set of attachments to refer to created disks.
10515 *
10516 * Used when taking a snapshot or when deleting the current state. Gets called
10517 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10518 *
10519 * This method assumes that mMediumAttachments contains the original hard disk
10520 * attachments it needs to create diffs for. On success, these attachments will
10521 * be replaced with the created diffs.
10522 *
10523 * Attachments with non-normal hard disks are left as is.
10524 *
10525 * If @a aOnline is @c false then the original hard disks that require implicit
10526 * diffs will be locked for reading. Otherwise it is assumed that they are
10527 * already locked for writing (when the VM was started). Note that in the latter
10528 * case it is responsibility of the caller to lock the newly created diffs for
10529 * writing if this method succeeds.
10530 *
10531 * @param aProgress Progress object to run (must contain at least as
10532 * many operations left as the number of hard disks
10533 * attached).
10534 * @param aWeight Weight of this operation.
10535 * @param aOnline Whether the VM was online prior to this operation.
10536 *
10537 * @note The progress object is not marked as completed, neither on success nor
10538 * on failure. This is a responsibility of the caller.
10539 *
10540 * @note Locks this object and the media tree for writing.
10541 */
10542HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10543 ULONG aWeight,
10544 bool aOnline)
10545{
10546 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10547
10548 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10549 AssertReturn(!!pProgressControl, E_INVALIDARG);
10550
10551 AutoCaller autoCaller(this);
10552 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10553
10554 AutoMultiWriteLock2 alock(this->lockHandle(),
10555 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10556
10557 /* must be in a protective state because we release the lock below */
10558 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10559 || mData->mMachineState == MachineState_OnlineSnapshotting
10560 || mData->mMachineState == MachineState_LiveSnapshotting
10561 || mData->mMachineState == MachineState_RestoringSnapshot
10562 || mData->mMachineState == MachineState_DeletingSnapshot
10563 , E_FAIL);
10564
10565 HRESULT rc = S_OK;
10566
10567 // use appropriate locked media map (online or offline)
10568 MediumLockListMap lockedMediaOffline;
10569 MediumLockListMap *lockedMediaMap;
10570 if (aOnline)
10571 lockedMediaMap = &mData->mSession.mLockedMedia;
10572 else
10573 lockedMediaMap = &lockedMediaOffline;
10574
10575 try
10576 {
10577 if (!aOnline)
10578 {
10579 /* lock all attached hard disks early to detect "in use"
10580 * situations before creating actual diffs */
10581 for (MediumAttachmentList::const_iterator
10582 it = mMediumAttachments->begin();
10583 it != mMediumAttachments->end();
10584 ++it)
10585 {
10586 MediumAttachment *pAtt = *it;
10587 if (pAtt->i_getType() == DeviceType_HardDisk)
10588 {
10589 Medium *pMedium = pAtt->i_getMedium();
10590 Assert(pMedium);
10591
10592 MediumLockList *pMediumLockList(new MediumLockList());
10593 alock.release();
10594 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10595 NULL /* pToLockWrite */,
10596 false /* fMediumLockWriteAll */,
10597 NULL,
10598 *pMediumLockList);
10599 alock.acquire();
10600 if (FAILED(rc))
10601 {
10602 delete pMediumLockList;
10603 throw rc;
10604 }
10605 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10606 if (FAILED(rc))
10607 {
10608 throw setError(rc,
10609 tr("Collecting locking information for all attached media failed"));
10610 }
10611 }
10612 }
10613
10614 /* Now lock all media. If this fails, nothing is locked. */
10615 alock.release();
10616 rc = lockedMediaMap->Lock();
10617 alock.acquire();
10618 if (FAILED(rc))
10619 {
10620 throw setError(rc,
10621 tr("Locking of attached media failed"));
10622 }
10623 }
10624
10625 /* remember the current list (note that we don't use backup() since
10626 * mMediumAttachments may be already backed up) */
10627 MediumAttachmentList atts = *mMediumAttachments.data();
10628
10629 /* start from scratch */
10630 mMediumAttachments->clear();
10631
10632 /* go through remembered attachments and create diffs for normal hard
10633 * disks and attach them */
10634 for (MediumAttachmentList::const_iterator
10635 it = atts.begin();
10636 it != atts.end();
10637 ++it)
10638 {
10639 MediumAttachment *pAtt = *it;
10640
10641 DeviceType_T devType = pAtt->i_getType();
10642 Medium *pMedium = pAtt->i_getMedium();
10643
10644 if ( devType != DeviceType_HardDisk
10645 || pMedium == NULL
10646 || pMedium->i_getType() != MediumType_Normal)
10647 {
10648 /* copy the attachment as is */
10649
10650 /** @todo the progress object created in SessionMachine::TakeSnaphot
10651 * only expects operations for hard disks. Later other
10652 * device types need to show up in the progress as well. */
10653 if (devType == DeviceType_HardDisk)
10654 {
10655 if (pMedium == NULL)
10656 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10657 aWeight); // weight
10658 else
10659 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10660 pMedium->i_getBase()->i_getName().c_str()).raw(),
10661 aWeight); // weight
10662 }
10663
10664 mMediumAttachments->push_back(pAtt);
10665 continue;
10666 }
10667
10668 /* need a diff */
10669 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10670 pMedium->i_getBase()->i_getName().c_str()).raw(),
10671 aWeight); // weight
10672
10673 Utf8Str strFullSnapshotFolder;
10674 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10675
10676 ComObjPtr<Medium> diff;
10677 diff.createObject();
10678 // store the diff in the same registry as the parent
10679 // (this cannot fail here because we can't create implicit diffs for
10680 // unregistered images)
10681 Guid uuidRegistryParent;
10682 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10683 Assert(fInRegistry); NOREF(fInRegistry);
10684 rc = diff->init(mParent,
10685 pMedium->i_getPreferredDiffFormat(),
10686 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10687 uuidRegistryParent,
10688 DeviceType_HardDisk);
10689 if (FAILED(rc)) throw rc;
10690
10691 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10692 * the push_back? Looks like we're going to release medium with the
10693 * wrong kind of lock (general issue with if we fail anywhere at all)
10694 * and an orphaned VDI in the snapshots folder. */
10695
10696 /* update the appropriate lock list */
10697 MediumLockList *pMediumLockList;
10698 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10699 AssertComRCThrowRC(rc);
10700 if (aOnline)
10701 {
10702 alock.release();
10703 /* The currently attached medium will be read-only, change
10704 * the lock type to read. */
10705 rc = pMediumLockList->Update(pMedium, false);
10706 alock.acquire();
10707 AssertComRCThrowRC(rc);
10708 }
10709
10710 /* release the locks before the potentially lengthy operation */
10711 alock.release();
10712 rc = pMedium->i_createDiffStorage(diff,
10713 pMedium->i_getPreferredDiffVariant(),
10714 pMediumLockList,
10715 NULL /* aProgress */,
10716 true /* aWait */,
10717 false /* aNotify */);
10718 alock.acquire();
10719 if (FAILED(rc)) throw rc;
10720
10721 /* actual lock list update is done in Machine::i_commitMedia */
10722
10723 rc = diff->i_addBackReference(mData->mUuid);
10724 AssertComRCThrowRC(rc);
10725
10726 /* add a new attachment */
10727 ComObjPtr<MediumAttachment> attachment;
10728 attachment.createObject();
10729 rc = attachment->init(this,
10730 diff,
10731 pAtt->i_getControllerName(),
10732 pAtt->i_getPort(),
10733 pAtt->i_getDevice(),
10734 DeviceType_HardDisk,
10735 true /* aImplicit */,
10736 false /* aPassthrough */,
10737 false /* aTempEject */,
10738 pAtt->i_getNonRotational(),
10739 pAtt->i_getDiscard(),
10740 pAtt->i_getHotPluggable(),
10741 pAtt->i_getBandwidthGroup());
10742 if (FAILED(rc)) throw rc;
10743
10744 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10745 AssertComRCThrowRC(rc);
10746 mMediumAttachments->push_back(attachment);
10747 }
10748 }
10749 catch (HRESULT aRC) { rc = aRC; }
10750
10751 /* unlock all hard disks we locked when there is no VM */
10752 if (!aOnline)
10753 {
10754 ErrorInfoKeeper eik;
10755
10756 HRESULT rc1 = lockedMediaMap->Clear();
10757 AssertComRC(rc1);
10758 }
10759
10760 return rc;
10761}
10762
10763/**
10764 * Deletes implicit differencing hard disks created either by
10765 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10766 * mMediumAttachments.
10767 *
10768 * Note that to delete hard disks created by #attachDevice() this method is
10769 * called from #i_rollbackMedia() when the changes are rolled back.
10770 *
10771 * @note Locks this object and the media tree for writing.
10772 */
10773HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10774{
10775 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10776
10777 AutoCaller autoCaller(this);
10778 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10779
10780 AutoMultiWriteLock2 alock(this->lockHandle(),
10781 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10782
10783 /* We absolutely must have backed up state. */
10784 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10785
10786 /* Check if there are any implicitly created diff images. */
10787 bool fImplicitDiffs = false;
10788 for (MediumAttachmentList::const_iterator
10789 it = mMediumAttachments->begin();
10790 it != mMediumAttachments->end();
10791 ++it)
10792 {
10793 const ComObjPtr<MediumAttachment> &pAtt = *it;
10794 if (pAtt->i_isImplicit())
10795 {
10796 fImplicitDiffs = true;
10797 break;
10798 }
10799 }
10800 /* If there is nothing to do, leave early. This saves lots of image locking
10801 * effort. It also avoids a MachineStateChanged event without real reason.
10802 * This is important e.g. when loading a VM config, because there should be
10803 * no events. Otherwise API clients can become thoroughly confused for
10804 * inaccessible VMs (the code for loading VM configs uses this method for
10805 * cleanup if the config makes no sense), as they take such events as an
10806 * indication that the VM is alive, and they would force the VM config to
10807 * be reread, leading to an endless loop. */
10808 if (!fImplicitDiffs)
10809 return S_OK;
10810
10811 HRESULT rc = S_OK;
10812 MachineState_T oldState = mData->mMachineState;
10813
10814 /* will release the lock before the potentially lengthy operation,
10815 * so protect with the special state (unless already protected) */
10816 if ( oldState != MachineState_Snapshotting
10817 && oldState != MachineState_OnlineSnapshotting
10818 && oldState != MachineState_LiveSnapshotting
10819 && oldState != MachineState_RestoringSnapshot
10820 && oldState != MachineState_DeletingSnapshot
10821 && oldState != MachineState_DeletingSnapshotOnline
10822 && oldState != MachineState_DeletingSnapshotPaused
10823 )
10824 i_setMachineState(MachineState_SettingUp);
10825
10826 // use appropriate locked media map (online or offline)
10827 MediumLockListMap lockedMediaOffline;
10828 MediumLockListMap *lockedMediaMap;
10829 if (aOnline)
10830 lockedMediaMap = &mData->mSession.mLockedMedia;
10831 else
10832 lockedMediaMap = &lockedMediaOffline;
10833
10834 try
10835 {
10836 if (!aOnline)
10837 {
10838 /* lock all attached hard disks early to detect "in use"
10839 * situations before deleting actual diffs */
10840 for (MediumAttachmentList::const_iterator
10841 it = mMediumAttachments->begin();
10842 it != mMediumAttachments->end();
10843 ++it)
10844 {
10845 MediumAttachment *pAtt = *it;
10846 if (pAtt->i_getType() == DeviceType_HardDisk)
10847 {
10848 Medium *pMedium = pAtt->i_getMedium();
10849 Assert(pMedium);
10850
10851 MediumLockList *pMediumLockList(new MediumLockList());
10852 alock.release();
10853 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10854 NULL /* pToLockWrite */,
10855 false /* fMediumLockWriteAll */,
10856 NULL,
10857 *pMediumLockList);
10858 alock.acquire();
10859
10860 if (FAILED(rc))
10861 {
10862 delete pMediumLockList;
10863 throw rc;
10864 }
10865
10866 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10867 if (FAILED(rc))
10868 throw rc;
10869 }
10870 }
10871
10872 if (FAILED(rc))
10873 throw rc;
10874 } // end of offline
10875
10876 /* Lock lists are now up to date and include implicitly created media */
10877
10878 /* Go through remembered attachments and delete all implicitly created
10879 * diffs and fix up the attachment information */
10880 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10881 MediumAttachmentList implicitAtts;
10882 for (MediumAttachmentList::const_iterator
10883 it = mMediumAttachments->begin();
10884 it != mMediumAttachments->end();
10885 ++it)
10886 {
10887 ComObjPtr<MediumAttachment> pAtt = *it;
10888 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10889 if (pMedium.isNull())
10890 continue;
10891
10892 // Implicit attachments go on the list for deletion and back references are removed.
10893 if (pAtt->i_isImplicit())
10894 {
10895 /* Deassociate and mark for deletion */
10896 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10897 rc = pMedium->i_removeBackReference(mData->mUuid);
10898 if (FAILED(rc))
10899 throw rc;
10900 implicitAtts.push_back(pAtt);
10901 continue;
10902 }
10903
10904 /* Was this medium attached before? */
10905 if (!i_findAttachment(oldAtts, pMedium))
10906 {
10907 /* no: de-associate */
10908 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10909 rc = pMedium->i_removeBackReference(mData->mUuid);
10910 if (FAILED(rc))
10911 throw rc;
10912 continue;
10913 }
10914 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10915 }
10916
10917 /* If there are implicit attachments to delete, throw away the lock
10918 * map contents (which will unlock all media) since the medium
10919 * attachments will be rolled back. Below we need to completely
10920 * recreate the lock map anyway since it is infinitely complex to
10921 * do this incrementally (would need reconstructing each attachment
10922 * change, which would be extremely hairy). */
10923 if (implicitAtts.size() != 0)
10924 {
10925 ErrorInfoKeeper eik;
10926
10927 HRESULT rc1 = lockedMediaMap->Clear();
10928 AssertComRC(rc1);
10929 }
10930
10931 /* rollback hard disk changes */
10932 mMediumAttachments.rollback();
10933
10934 MultiResult mrc(S_OK);
10935
10936 // Delete unused implicit diffs.
10937 if (implicitAtts.size() != 0)
10938 {
10939 alock.release();
10940
10941 for (MediumAttachmentList::const_iterator
10942 it = implicitAtts.begin();
10943 it != implicitAtts.end();
10944 ++it)
10945 {
10946 // Remove medium associated with this attachment.
10947 ComObjPtr<MediumAttachment> pAtt = *it;
10948 Assert(pAtt);
10949 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10950 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10951 Assert(pMedium);
10952
10953 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10954 // continue on delete failure, just collect error messages
10955 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10956 pMedium->i_getLocationFull().c_str() ));
10957 mrc = rc;
10958 }
10959 // Clear the list of deleted implicit attachments now, while not
10960 // holding the lock, as it will ultimately trigger Medium::uninit()
10961 // calls which assume that the media tree lock isn't held.
10962 implicitAtts.clear();
10963
10964 alock.acquire();
10965
10966 /* if there is a VM recreate media lock map as mentioned above,
10967 * otherwise it is a waste of time and we leave things unlocked */
10968 if (aOnline)
10969 {
10970 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10971 /* must never be NULL, but better safe than sorry */
10972 if (!pMachine.isNull())
10973 {
10974 alock.release();
10975 rc = mData->mSession.mMachine->i_lockMedia();
10976 alock.acquire();
10977 if (FAILED(rc))
10978 throw rc;
10979 }
10980 }
10981 }
10982 }
10983 catch (HRESULT aRC) {rc = aRC;}
10984
10985 if (mData->mMachineState == MachineState_SettingUp)
10986 i_setMachineState(oldState);
10987
10988 /* unlock all hard disks we locked when there is no VM */
10989 if (!aOnline)
10990 {
10991 ErrorInfoKeeper eik;
10992
10993 HRESULT rc1 = lockedMediaMap->Clear();
10994 AssertComRC(rc1);
10995 }
10996
10997 return rc;
10998}
10999
11000
11001/**
11002 * Looks through the given list of media attachments for one with the given parameters
11003 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11004 * can be searched as well if needed.
11005 *
11006 * @param ll
11007 * @param aControllerName
11008 * @param aControllerPort
11009 * @param aDevice
11010 * @return
11011 */
11012MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11013 const Utf8Str &aControllerName,
11014 LONG aControllerPort,
11015 LONG aDevice)
11016{
11017 for (MediumAttachmentList::const_iterator
11018 it = ll.begin();
11019 it != ll.end();
11020 ++it)
11021 {
11022 MediumAttachment *pAttach = *it;
11023 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11024 return pAttach;
11025 }
11026
11027 return NULL;
11028}
11029
11030/**
11031 * Looks through the given list of media attachments for one with the given parameters
11032 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11033 * can be searched as well if needed.
11034 *
11035 * @param ll
11036 * @param pMedium
11037 * @return
11038 */
11039MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11040 ComObjPtr<Medium> pMedium)
11041{
11042 for (MediumAttachmentList::const_iterator
11043 it = ll.begin();
11044 it != ll.end();
11045 ++it)
11046 {
11047 MediumAttachment *pAttach = *it;
11048 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11049 if (pMediumThis == pMedium)
11050 return pAttach;
11051 }
11052
11053 return NULL;
11054}
11055
11056/**
11057 * Looks through the given list of media attachments for one with the given parameters
11058 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11059 * can be searched as well if needed.
11060 *
11061 * @param ll
11062 * @param id
11063 * @return
11064 */
11065MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11066 Guid &id)
11067{
11068 for (MediumAttachmentList::const_iterator
11069 it = ll.begin();
11070 it != ll.end();
11071 ++it)
11072 {
11073 MediumAttachment *pAttach = *it;
11074 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11075 if (pMediumThis->i_getId() == id)
11076 return pAttach;
11077 }
11078
11079 return NULL;
11080}
11081
11082/**
11083 * Main implementation for Machine::DetachDevice. This also gets called
11084 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11085 *
11086 * @param pAttach Medium attachment to detach.
11087 * @param writeLock Machine write lock which the caller must have locked once.
11088 * This may be released temporarily in here.
11089 * @param pSnapshot If NULL, then the detachment is for the current machine.
11090 * Otherwise this is for a SnapshotMachine, and this must be
11091 * its snapshot.
11092 * @return
11093 */
11094HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11095 AutoWriteLock &writeLock,
11096 Snapshot *pSnapshot)
11097{
11098 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11099 DeviceType_T mediumType = pAttach->i_getType();
11100
11101 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11102
11103 if (pAttach->i_isImplicit())
11104 {
11105 /* attempt to implicitly delete the implicitly created diff */
11106
11107 /// @todo move the implicit flag from MediumAttachment to Medium
11108 /// and forbid any hard disk operation when it is implicit. Or maybe
11109 /// a special media state for it to make it even more simple.
11110
11111 Assert(mMediumAttachments.isBackedUp());
11112
11113 /* will release the lock before the potentially lengthy operation, so
11114 * protect with the special state */
11115 MachineState_T oldState = mData->mMachineState;
11116 i_setMachineState(MachineState_SettingUp);
11117
11118 writeLock.release();
11119
11120 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11121 true /*aWait*/,
11122 false /*aNotify*/);
11123
11124 writeLock.acquire();
11125
11126 i_setMachineState(oldState);
11127
11128 if (FAILED(rc)) return rc;
11129 }
11130
11131 i_setModified(IsModified_Storage);
11132 mMediumAttachments.backup();
11133 mMediumAttachments->remove(pAttach);
11134
11135 if (!oldmedium.isNull())
11136 {
11137 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11138 if (pSnapshot)
11139 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11140 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11141 else if (mediumType != DeviceType_HardDisk)
11142 oldmedium->i_removeBackReference(mData->mUuid);
11143 }
11144
11145 return S_OK;
11146}
11147
11148/**
11149 * Goes thru all media of the given list and
11150 *
11151 * 1) calls i_detachDevice() on each of them for this machine and
11152 * 2) adds all Medium objects found in the process to the given list,
11153 * depending on cleanupMode.
11154 *
11155 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11156 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11157 * media to the list.
11158 *
11159 * This gets called from Machine::Unregister, both for the actual Machine and
11160 * the SnapshotMachine objects that might be found in the snapshots.
11161 *
11162 * Requires caller and locking. The machine lock must be passed in because it
11163 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11164 *
11165 * @param writeLock Machine lock from top-level caller; this gets passed to
11166 * i_detachDevice.
11167 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11168 * object if called for a SnapshotMachine.
11169 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11170 * added to llMedia; if Full, then all media get added;
11171 * otherwise no media get added.
11172 * @param llMedia Caller's list to receive Medium objects which got detached so
11173 * caller can close() them, depending on cleanupMode.
11174 * @return
11175 */
11176HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11177 Snapshot *pSnapshot,
11178 CleanupMode_T cleanupMode,
11179 MediaList &llMedia)
11180{
11181 Assert(isWriteLockOnCurrentThread());
11182
11183 HRESULT rc;
11184
11185 // make a temporary list because i_detachDevice invalidates iterators into
11186 // mMediumAttachments
11187 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11188
11189 for (MediumAttachmentList::iterator
11190 it = llAttachments2.begin();
11191 it != llAttachments2.end();
11192 ++it)
11193 {
11194 ComObjPtr<MediumAttachment> &pAttach = *it;
11195 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11196
11197 if (!pMedium.isNull())
11198 {
11199 AutoCaller mac(pMedium);
11200 if (FAILED(mac.rc())) return mac.rc();
11201 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11202 DeviceType_T devType = pMedium->i_getDeviceType();
11203 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11204 && devType == DeviceType_HardDisk)
11205 || (cleanupMode == CleanupMode_Full)
11206 )
11207 {
11208 llMedia.push_back(pMedium);
11209 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11210 /* Not allowed to keep this lock as below we need the parent
11211 * medium lock, and the lock order is parent to child. */
11212 lock.release();
11213 /*
11214 * Search for medias which are not attached to any machine, but
11215 * in the chain to an attached disk. Mediums are only consided
11216 * if they are:
11217 * - have only one child
11218 * - no references to any machines
11219 * - are of normal medium type
11220 */
11221 while (!pParent.isNull())
11222 {
11223 AutoCaller mac1(pParent);
11224 if (FAILED(mac1.rc())) return mac1.rc();
11225 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11226 if (pParent->i_getChildren().size() == 1)
11227 {
11228 if ( pParent->i_getMachineBackRefCount() == 0
11229 && pParent->i_getType() == MediumType_Normal
11230 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11231 llMedia.push_back(pParent);
11232 }
11233 else
11234 break;
11235 pParent = pParent->i_getParent();
11236 }
11237 }
11238 }
11239
11240 // real machine: then we need to use the proper method
11241 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11242
11243 if (FAILED(rc))
11244 return rc;
11245 }
11246
11247 return S_OK;
11248}
11249
11250/**
11251 * Perform deferred hard disk detachments.
11252 *
11253 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11254 * changed (not backed up).
11255 *
11256 * If @a aOnline is @c true then this method will also unlock the old hard
11257 * disks for which the new implicit diffs were created and will lock these new
11258 * diffs for writing.
11259 *
11260 * @param aOnline Whether the VM was online prior to this operation.
11261 *
11262 * @note Locks this object for writing!
11263 */
11264void Machine::i_commitMedia(bool aOnline /*= false*/)
11265{
11266 AutoCaller autoCaller(this);
11267 AssertComRCReturnVoid(autoCaller.rc());
11268
11269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11270
11271 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11272
11273 HRESULT rc = S_OK;
11274
11275 /* no attach/detach operations -- nothing to do */
11276 if (!mMediumAttachments.isBackedUp())
11277 return;
11278
11279 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11280 bool fMediaNeedsLocking = false;
11281
11282 /* enumerate new attachments */
11283 for (MediumAttachmentList::const_iterator
11284 it = mMediumAttachments->begin();
11285 it != mMediumAttachments->end();
11286 ++it)
11287 {
11288 MediumAttachment *pAttach = *it;
11289
11290 pAttach->i_commit();
11291
11292 Medium *pMedium = pAttach->i_getMedium();
11293 bool fImplicit = pAttach->i_isImplicit();
11294
11295 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11296 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11297 fImplicit));
11298
11299 /** @todo convert all this Machine-based voodoo to MediumAttachment
11300 * based commit logic. */
11301 if (fImplicit)
11302 {
11303 /* convert implicit attachment to normal */
11304 pAttach->i_setImplicit(false);
11305
11306 if ( aOnline
11307 && pMedium
11308 && pAttach->i_getType() == DeviceType_HardDisk
11309 )
11310 {
11311 /* update the appropriate lock list */
11312 MediumLockList *pMediumLockList;
11313 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11314 AssertComRC(rc);
11315 if (pMediumLockList)
11316 {
11317 /* unlock if there's a need to change the locking */
11318 if (!fMediaNeedsLocking)
11319 {
11320 rc = mData->mSession.mLockedMedia.Unlock();
11321 AssertComRC(rc);
11322 fMediaNeedsLocking = true;
11323 }
11324 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11325 AssertComRC(rc);
11326 rc = pMediumLockList->Append(pMedium, true);
11327 AssertComRC(rc);
11328 }
11329 }
11330
11331 continue;
11332 }
11333
11334 if (pMedium)
11335 {
11336 /* was this medium attached before? */
11337 for (MediumAttachmentList::iterator
11338 oldIt = oldAtts.begin();
11339 oldIt != oldAtts.end();
11340 ++oldIt)
11341 {
11342 MediumAttachment *pOldAttach = *oldIt;
11343 if (pOldAttach->i_getMedium() == pMedium)
11344 {
11345 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11346
11347 /* yes: remove from old to avoid de-association */
11348 oldAtts.erase(oldIt);
11349 break;
11350 }
11351 }
11352 }
11353 }
11354
11355 /* enumerate remaining old attachments and de-associate from the
11356 * current machine state */
11357 for (MediumAttachmentList::const_iterator
11358 it = oldAtts.begin();
11359 it != oldAtts.end();
11360 ++it)
11361 {
11362 MediumAttachment *pAttach = *it;
11363 Medium *pMedium = pAttach->i_getMedium();
11364
11365 /* Detach only hard disks, since DVD/floppy media is detached
11366 * instantly in MountMedium. */
11367 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11368 {
11369 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11370
11371 /* now de-associate from the current machine state */
11372 rc = pMedium->i_removeBackReference(mData->mUuid);
11373 AssertComRC(rc);
11374
11375 if (aOnline)
11376 {
11377 /* unlock since medium is not used anymore */
11378 MediumLockList *pMediumLockList;
11379 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11380 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11381 {
11382 /* this happens for online snapshots, there the attachment
11383 * is changing, but only to a diff image created under
11384 * the old one, so there is no separate lock list */
11385 Assert(!pMediumLockList);
11386 }
11387 else
11388 {
11389 AssertComRC(rc);
11390 if (pMediumLockList)
11391 {
11392 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11393 AssertComRC(rc);
11394 }
11395 }
11396 }
11397 }
11398 }
11399
11400 /* take media locks again so that the locking state is consistent */
11401 if (fMediaNeedsLocking)
11402 {
11403 Assert(aOnline);
11404 rc = mData->mSession.mLockedMedia.Lock();
11405 AssertComRC(rc);
11406 }
11407
11408 /* commit the hard disk changes */
11409 mMediumAttachments.commit();
11410
11411 if (i_isSessionMachine())
11412 {
11413 /*
11414 * Update the parent machine to point to the new owner.
11415 * This is necessary because the stored parent will point to the
11416 * session machine otherwise and cause crashes or errors later
11417 * when the session machine gets invalid.
11418 */
11419 /** @todo Change the MediumAttachment class to behave like any other
11420 * class in this regard by creating peer MediumAttachment
11421 * objects for session machines and share the data with the peer
11422 * machine.
11423 */
11424 for (MediumAttachmentList::const_iterator
11425 it = mMediumAttachments->begin();
11426 it != mMediumAttachments->end();
11427 ++it)
11428 (*it)->i_updateParentMachine(mPeer);
11429
11430 /* attach new data to the primary machine and reshare it */
11431 mPeer->mMediumAttachments.attach(mMediumAttachments);
11432 }
11433
11434 return;
11435}
11436
11437/**
11438 * Perform deferred deletion of implicitly created diffs.
11439 *
11440 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11441 * changed (not backed up).
11442 *
11443 * @note Locks this object for writing!
11444 */
11445void Machine::i_rollbackMedia()
11446{
11447 AutoCaller autoCaller(this);
11448 AssertComRCReturnVoid(autoCaller.rc());
11449
11450 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11451 LogFlowThisFunc(("Entering rollbackMedia\n"));
11452
11453 HRESULT rc = S_OK;
11454
11455 /* no attach/detach operations -- nothing to do */
11456 if (!mMediumAttachments.isBackedUp())
11457 return;
11458
11459 /* enumerate new attachments */
11460 for (MediumAttachmentList::const_iterator
11461 it = mMediumAttachments->begin();
11462 it != mMediumAttachments->end();
11463 ++it)
11464 {
11465 MediumAttachment *pAttach = *it;
11466 /* Fix up the backrefs for DVD/floppy media. */
11467 if (pAttach->i_getType() != DeviceType_HardDisk)
11468 {
11469 Medium *pMedium = pAttach->i_getMedium();
11470 if (pMedium)
11471 {
11472 rc = pMedium->i_removeBackReference(mData->mUuid);
11473 AssertComRC(rc);
11474 }
11475 }
11476
11477 (*it)->i_rollback();
11478
11479 pAttach = *it;
11480 /* Fix up the backrefs for DVD/floppy media. */
11481 if (pAttach->i_getType() != DeviceType_HardDisk)
11482 {
11483 Medium *pMedium = pAttach->i_getMedium();
11484 if (pMedium)
11485 {
11486 rc = pMedium->i_addBackReference(mData->mUuid);
11487 AssertComRC(rc);
11488 }
11489 }
11490 }
11491
11492 /** @todo convert all this Machine-based voodoo to MediumAttachment
11493 * based rollback logic. */
11494 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11495
11496 return;
11497}
11498
11499/**
11500 * Returns true if the settings file is located in the directory named exactly
11501 * as the machine; this means, among other things, that the machine directory
11502 * should be auto-renamed.
11503 *
11504 * @param aSettingsDir if not NULL, the full machine settings file directory
11505 * name will be assigned there.
11506 *
11507 * @note Doesn't lock anything.
11508 * @note Not thread safe (must be called from this object's lock).
11509 */
11510bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11511{
11512 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11513 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11514 if (aSettingsDir)
11515 *aSettingsDir = strMachineDirName;
11516 strMachineDirName.stripPath(); // vmname
11517 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11518 strConfigFileOnly.stripPath() // vmname.vbox
11519 .stripSuffix(); // vmname
11520 /** @todo hack, make somehow use of ComposeMachineFilename */
11521 if (mUserData->s.fDirectoryIncludesUUID)
11522 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11523
11524 AssertReturn(!strMachineDirName.isEmpty(), false);
11525 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11526
11527 return strMachineDirName == strConfigFileOnly;
11528}
11529
11530/**
11531 * Discards all changes to machine settings.
11532 *
11533 * @param aNotify Whether to notify the direct session about changes or not.
11534 *
11535 * @note Locks objects for writing!
11536 */
11537void Machine::i_rollback(bool aNotify)
11538{
11539 AutoCaller autoCaller(this);
11540 AssertComRCReturn(autoCaller.rc(), (void)0);
11541
11542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11543
11544 if (!mStorageControllers.isNull())
11545 {
11546 if (mStorageControllers.isBackedUp())
11547 {
11548 /* unitialize all new devices (absent in the backed up list). */
11549 StorageControllerList *backedList = mStorageControllers.backedUpData();
11550 for (StorageControllerList::const_iterator
11551 it = mStorageControllers->begin();
11552 it != mStorageControllers->end();
11553 ++it)
11554 {
11555 if ( std::find(backedList->begin(), backedList->end(), *it)
11556 == backedList->end()
11557 )
11558 {
11559 (*it)->uninit();
11560 }
11561 }
11562
11563 /* restore the list */
11564 mStorageControllers.rollback();
11565 }
11566
11567 /* rollback any changes to devices after restoring the list */
11568 if (mData->flModifications & IsModified_Storage)
11569 {
11570 for (StorageControllerList::const_iterator
11571 it = mStorageControllers->begin();
11572 it != mStorageControllers->end();
11573 ++it)
11574 {
11575 (*it)->i_rollback();
11576 }
11577 }
11578 }
11579
11580 if (!mUSBControllers.isNull())
11581 {
11582 if (mUSBControllers.isBackedUp())
11583 {
11584 /* unitialize all new devices (absent in the backed up list). */
11585 USBControllerList *backedList = mUSBControllers.backedUpData();
11586 for (USBControllerList::const_iterator
11587 it = mUSBControllers->begin();
11588 it != mUSBControllers->end();
11589 ++it)
11590 {
11591 if ( std::find(backedList->begin(), backedList->end(), *it)
11592 == backedList->end()
11593 )
11594 {
11595 (*it)->uninit();
11596 }
11597 }
11598
11599 /* restore the list */
11600 mUSBControllers.rollback();
11601 }
11602
11603 /* rollback any changes to devices after restoring the list */
11604 if (mData->flModifications & IsModified_USB)
11605 {
11606 for (USBControllerList::const_iterator
11607 it = mUSBControllers->begin();
11608 it != mUSBControllers->end();
11609 ++it)
11610 {
11611 (*it)->i_rollback();
11612 }
11613 }
11614 }
11615
11616 mUserData.rollback();
11617
11618 mHWData.rollback();
11619
11620 if (mData->flModifications & IsModified_Storage)
11621 i_rollbackMedia();
11622
11623 if (mBIOSSettings)
11624 mBIOSSettings->i_rollback();
11625
11626 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11627 mRecordingSettings->i_rollback();
11628
11629 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11630 mGraphicsAdapter->i_rollback();
11631
11632 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11633 mVRDEServer->i_rollback();
11634
11635 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11636 mAudioAdapter->i_rollback();
11637
11638 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11639 mUSBDeviceFilters->i_rollback();
11640
11641 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11642 mBandwidthControl->i_rollback();
11643
11644 if (!mHWData.isNull())
11645 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11646 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11647 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11648 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11649
11650 if (mData->flModifications & IsModified_NetworkAdapters)
11651 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11652 if ( mNetworkAdapters[slot]
11653 && mNetworkAdapters[slot]->i_isModified())
11654 {
11655 mNetworkAdapters[slot]->i_rollback();
11656 networkAdapters[slot] = mNetworkAdapters[slot];
11657 }
11658
11659 if (mData->flModifications & IsModified_SerialPorts)
11660 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11661 if ( mSerialPorts[slot]
11662 && mSerialPorts[slot]->i_isModified())
11663 {
11664 mSerialPorts[slot]->i_rollback();
11665 serialPorts[slot] = mSerialPorts[slot];
11666 }
11667
11668 if (mData->flModifications & IsModified_ParallelPorts)
11669 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11670 if ( mParallelPorts[slot]
11671 && mParallelPorts[slot]->i_isModified())
11672 {
11673 mParallelPorts[slot]->i_rollback();
11674 parallelPorts[slot] = mParallelPorts[slot];
11675 }
11676
11677 if (aNotify)
11678 {
11679 /* inform the direct session about changes */
11680
11681 ComObjPtr<Machine> that = this;
11682 uint32_t flModifications = mData->flModifications;
11683 alock.release();
11684
11685 if (flModifications & IsModified_SharedFolders)
11686 that->i_onSharedFolderChange();
11687
11688 if (flModifications & IsModified_VRDEServer)
11689 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11690 if (flModifications & IsModified_USB)
11691 that->i_onUSBControllerChange();
11692
11693 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11694 if (networkAdapters[slot])
11695 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11696 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11697 if (serialPorts[slot])
11698 that->i_onSerialPortChange(serialPorts[slot]);
11699 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11700 if (parallelPorts[slot])
11701 that->i_onParallelPortChange(parallelPorts[slot]);
11702
11703 if (flModifications & IsModified_Storage)
11704 {
11705 for (StorageControllerList::const_iterator
11706 it = mStorageControllers->begin();
11707 it != mStorageControllers->end();
11708 ++it)
11709 {
11710 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11711 }
11712 }
11713
11714
11715#if 0
11716 if (flModifications & IsModified_BandwidthControl)
11717 that->onBandwidthControlChange();
11718#endif
11719 }
11720}
11721
11722/**
11723 * Commits all the changes to machine settings.
11724 *
11725 * Note that this operation is supposed to never fail.
11726 *
11727 * @note Locks this object and children for writing.
11728 */
11729void Machine::i_commit()
11730{
11731 AutoCaller autoCaller(this);
11732 AssertComRCReturnVoid(autoCaller.rc());
11733
11734 AutoCaller peerCaller(mPeer);
11735 AssertComRCReturnVoid(peerCaller.rc());
11736
11737 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11738
11739 /*
11740 * use safe commit to ensure Snapshot machines (that share mUserData)
11741 * will still refer to a valid memory location
11742 */
11743 mUserData.commitCopy();
11744
11745 mHWData.commit();
11746
11747 if (mMediumAttachments.isBackedUp())
11748 i_commitMedia(Global::IsOnline(mData->mMachineState));
11749
11750 mBIOSSettings->i_commit();
11751 mRecordingSettings->i_commit();
11752 mGraphicsAdapter->i_commit();
11753 mVRDEServer->i_commit();
11754 mAudioAdapter->i_commit();
11755 mUSBDeviceFilters->i_commit();
11756 mBandwidthControl->i_commit();
11757
11758 /* Since mNetworkAdapters is a list which might have been changed (resized)
11759 * without using the Backupable<> template we need to handle the copying
11760 * of the list entries manually, including the creation of peers for the
11761 * new objects. */
11762 bool commitNetworkAdapters = false;
11763 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11764 if (mPeer)
11765 {
11766 /* commit everything, even the ones which will go away */
11767 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11768 mNetworkAdapters[slot]->i_commit();
11769 /* copy over the new entries, creating a peer and uninit the original */
11770 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11771 for (size_t slot = 0; slot < newSize; slot++)
11772 {
11773 /* look if this adapter has a peer device */
11774 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11775 if (!peer)
11776 {
11777 /* no peer means the adapter is a newly created one;
11778 * create a peer owning data this data share it with */
11779 peer.createObject();
11780 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11781 }
11782 mPeer->mNetworkAdapters[slot] = peer;
11783 }
11784 /* uninit any no longer needed network adapters */
11785 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11786 mNetworkAdapters[slot]->uninit();
11787 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11788 {
11789 if (mPeer->mNetworkAdapters[slot])
11790 mPeer->mNetworkAdapters[slot]->uninit();
11791 }
11792 /* Keep the original network adapter count until this point, so that
11793 * discarding a chipset type change will not lose settings. */
11794 mNetworkAdapters.resize(newSize);
11795 mPeer->mNetworkAdapters.resize(newSize);
11796 }
11797 else
11798 {
11799 /* we have no peer (our parent is the newly created machine);
11800 * just commit changes to the network adapters */
11801 commitNetworkAdapters = true;
11802 }
11803 if (commitNetworkAdapters)
11804 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11805 mNetworkAdapters[slot]->i_commit();
11806
11807 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11808 mSerialPorts[slot]->i_commit();
11809 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11810 mParallelPorts[slot]->i_commit();
11811
11812 bool commitStorageControllers = false;
11813
11814 if (mStorageControllers.isBackedUp())
11815 {
11816 mStorageControllers.commit();
11817
11818 if (mPeer)
11819 {
11820 /* Commit all changes to new controllers (this will reshare data with
11821 * peers for those who have peers) */
11822 StorageControllerList *newList = new StorageControllerList();
11823 for (StorageControllerList::const_iterator
11824 it = mStorageControllers->begin();
11825 it != mStorageControllers->end();
11826 ++it)
11827 {
11828 (*it)->i_commit();
11829
11830 /* look if this controller has a peer device */
11831 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11832 if (!peer)
11833 {
11834 /* no peer means the device is a newly created one;
11835 * create a peer owning data this device share it with */
11836 peer.createObject();
11837 peer->init(mPeer, *it, true /* aReshare */);
11838 }
11839 else
11840 {
11841 /* remove peer from the old list */
11842 mPeer->mStorageControllers->remove(peer);
11843 }
11844 /* and add it to the new list */
11845 newList->push_back(peer);
11846 }
11847
11848 /* uninit old peer's controllers that are left */
11849 for (StorageControllerList::const_iterator
11850 it = mPeer->mStorageControllers->begin();
11851 it != mPeer->mStorageControllers->end();
11852 ++it)
11853 {
11854 (*it)->uninit();
11855 }
11856
11857 /* attach new list of controllers to our peer */
11858 mPeer->mStorageControllers.attach(newList);
11859 }
11860 else
11861 {
11862 /* we have no peer (our parent is the newly created machine);
11863 * just commit changes to devices */
11864 commitStorageControllers = true;
11865 }
11866 }
11867 else
11868 {
11869 /* the list of controllers itself is not changed,
11870 * just commit changes to controllers themselves */
11871 commitStorageControllers = true;
11872 }
11873
11874 if (commitStorageControllers)
11875 {
11876 for (StorageControllerList::const_iterator
11877 it = mStorageControllers->begin();
11878 it != mStorageControllers->end();
11879 ++it)
11880 {
11881 (*it)->i_commit();
11882 }
11883 }
11884
11885 bool commitUSBControllers = false;
11886
11887 if (mUSBControllers.isBackedUp())
11888 {
11889 mUSBControllers.commit();
11890
11891 if (mPeer)
11892 {
11893 /* Commit all changes to new controllers (this will reshare data with
11894 * peers for those who have peers) */
11895 USBControllerList *newList = new USBControllerList();
11896 for (USBControllerList::const_iterator
11897 it = mUSBControllers->begin();
11898 it != mUSBControllers->end();
11899 ++it)
11900 {
11901 (*it)->i_commit();
11902
11903 /* look if this controller has a peer device */
11904 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11905 if (!peer)
11906 {
11907 /* no peer means the device is a newly created one;
11908 * create a peer owning data this device share it with */
11909 peer.createObject();
11910 peer->init(mPeer, *it, true /* aReshare */);
11911 }
11912 else
11913 {
11914 /* remove peer from the old list */
11915 mPeer->mUSBControllers->remove(peer);
11916 }
11917 /* and add it to the new list */
11918 newList->push_back(peer);
11919 }
11920
11921 /* uninit old peer's controllers that are left */
11922 for (USBControllerList::const_iterator
11923 it = mPeer->mUSBControllers->begin();
11924 it != mPeer->mUSBControllers->end();
11925 ++it)
11926 {
11927 (*it)->uninit();
11928 }
11929
11930 /* attach new list of controllers to our peer */
11931 mPeer->mUSBControllers.attach(newList);
11932 }
11933 else
11934 {
11935 /* we have no peer (our parent is the newly created machine);
11936 * just commit changes to devices */
11937 commitUSBControllers = true;
11938 }
11939 }
11940 else
11941 {
11942 /* the list of controllers itself is not changed,
11943 * just commit changes to controllers themselves */
11944 commitUSBControllers = true;
11945 }
11946
11947 if (commitUSBControllers)
11948 {
11949 for (USBControllerList::const_iterator
11950 it = mUSBControllers->begin();
11951 it != mUSBControllers->end();
11952 ++it)
11953 {
11954 (*it)->i_commit();
11955 }
11956 }
11957
11958 if (i_isSessionMachine())
11959 {
11960 /* attach new data to the primary machine and reshare it */
11961 mPeer->mUserData.attach(mUserData);
11962 mPeer->mHWData.attach(mHWData);
11963 /* mmMediumAttachments is reshared by fixupMedia */
11964 // mPeer->mMediumAttachments.attach(mMediumAttachments);
11965 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
11966 }
11967}
11968
11969/**
11970 * Copies all the hardware data from the given machine.
11971 *
11972 * Currently, only called when the VM is being restored from a snapshot. In
11973 * particular, this implies that the VM is not running during this method's
11974 * call.
11975 *
11976 * @note This method must be called from under this object's lock.
11977 *
11978 * @note This method doesn't call #i_commit(), so all data remains backed up and
11979 * unsaved.
11980 */
11981void Machine::i_copyFrom(Machine *aThat)
11982{
11983 AssertReturnVoid(!i_isSnapshotMachine());
11984 AssertReturnVoid(aThat->i_isSnapshotMachine());
11985
11986 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11987
11988 mHWData.assignCopy(aThat->mHWData);
11989
11990 // create copies of all shared folders (mHWData after attaching a copy
11991 // contains just references to original objects)
11992 for (HWData::SharedFolderList::iterator
11993 it = mHWData->mSharedFolders.begin();
11994 it != mHWData->mSharedFolders.end();
11995 ++it)
11996 {
11997 ComObjPtr<SharedFolder> folder;
11998 folder.createObject();
11999 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12000 AssertComRC(rc);
12001 *it = folder;
12002 }
12003
12004 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12005 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12006 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12007 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12008 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12009 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12010 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12011
12012 /* create private copies of all controllers */
12013 mStorageControllers.backup();
12014 mStorageControllers->clear();
12015 for (StorageControllerList::const_iterator
12016 it = aThat->mStorageControllers->begin();
12017 it != aThat->mStorageControllers->end();
12018 ++it)
12019 {
12020 ComObjPtr<StorageController> ctrl;
12021 ctrl.createObject();
12022 ctrl->initCopy(this, *it);
12023 mStorageControllers->push_back(ctrl);
12024 }
12025
12026 /* create private copies of all USB controllers */
12027 mUSBControllers.backup();
12028 mUSBControllers->clear();
12029 for (USBControllerList::const_iterator
12030 it = aThat->mUSBControllers->begin();
12031 it != aThat->mUSBControllers->end();
12032 ++it)
12033 {
12034 ComObjPtr<USBController> ctrl;
12035 ctrl.createObject();
12036 ctrl->initCopy(this, *it);
12037 mUSBControllers->push_back(ctrl);
12038 }
12039
12040 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12041 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12042 {
12043 if (mNetworkAdapters[slot].isNotNull())
12044 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12045 else
12046 {
12047 unconst(mNetworkAdapters[slot]).createObject();
12048 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12049 }
12050 }
12051 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12052 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12053 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12054 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12055}
12056
12057/**
12058 * Returns whether the given storage controller is hotplug capable.
12059 *
12060 * @returns true if the controller supports hotplugging
12061 * false otherwise.
12062 * @param enmCtrlType The controller type to check for.
12063 */
12064bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12065{
12066 ComPtr<ISystemProperties> systemProperties;
12067 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12068 if (FAILED(rc))
12069 return false;
12070
12071 BOOL aHotplugCapable = FALSE;
12072 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12073
12074 return RT_BOOL(aHotplugCapable);
12075}
12076
12077#ifdef VBOX_WITH_RESOURCE_USAGE_API
12078
12079void Machine::i_getDiskList(MediaList &list)
12080{
12081 for (MediumAttachmentList::const_iterator
12082 it = mMediumAttachments->begin();
12083 it != mMediumAttachments->end();
12084 ++it)
12085 {
12086 MediumAttachment *pAttach = *it;
12087 /* just in case */
12088 AssertContinue(pAttach);
12089
12090 AutoCaller localAutoCallerA(pAttach);
12091 if (FAILED(localAutoCallerA.rc())) continue;
12092
12093 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12094
12095 if (pAttach->i_getType() == DeviceType_HardDisk)
12096 list.push_back(pAttach->i_getMedium());
12097 }
12098}
12099
12100void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12101{
12102 AssertReturnVoid(isWriteLockOnCurrentThread());
12103 AssertPtrReturnVoid(aCollector);
12104
12105 pm::CollectorHAL *hal = aCollector->getHAL();
12106 /* Create sub metrics */
12107 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12108 "Percentage of processor time spent in user mode by the VM process.");
12109 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12110 "Percentage of processor time spent in kernel mode by the VM process.");
12111 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12112 "Size of resident portion of VM process in memory.");
12113 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12114 "Actual size of all VM disks combined.");
12115 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12116 "Network receive rate.");
12117 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12118 "Network transmit rate.");
12119 /* Create and register base metrics */
12120 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12121 cpuLoadUser, cpuLoadKernel);
12122 aCollector->registerBaseMetric(cpuLoad);
12123 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12124 ramUsageUsed);
12125 aCollector->registerBaseMetric(ramUsage);
12126 MediaList disks;
12127 i_getDiskList(disks);
12128 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12129 diskUsageUsed);
12130 aCollector->registerBaseMetric(diskUsage);
12131
12132 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12133 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12134 new pm::AggregateAvg()));
12135 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12136 new pm::AggregateMin()));
12137 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12138 new pm::AggregateMax()));
12139 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12140 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12141 new pm::AggregateAvg()));
12142 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12143 new pm::AggregateMin()));
12144 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12145 new pm::AggregateMax()));
12146
12147 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12148 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12149 new pm::AggregateAvg()));
12150 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12151 new pm::AggregateMin()));
12152 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12153 new pm::AggregateMax()));
12154
12155 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12156 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12157 new pm::AggregateAvg()));
12158 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12159 new pm::AggregateMin()));
12160 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12161 new pm::AggregateMax()));
12162
12163
12164 /* Guest metrics collector */
12165 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12166 aCollector->registerGuest(mCollectorGuest);
12167 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12168
12169 /* Create sub metrics */
12170 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12171 "Percentage of processor time spent in user mode as seen by the guest.");
12172 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12173 "Percentage of processor time spent in kernel mode as seen by the guest.");
12174 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12175 "Percentage of processor time spent idling as seen by the guest.");
12176
12177 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12178 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12179 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12180 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12181 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12182 pm::SubMetric *guestMemCache = new pm::SubMetric(
12183 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12184
12185 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12186 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12187
12188 /* Create and register base metrics */
12189 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12190 machineNetRx, machineNetTx);
12191 aCollector->registerBaseMetric(machineNetRate);
12192
12193 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12194 guestLoadUser, guestLoadKernel, guestLoadIdle);
12195 aCollector->registerBaseMetric(guestCpuLoad);
12196
12197 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12198 guestMemTotal, guestMemFree,
12199 guestMemBalloon, guestMemShared,
12200 guestMemCache, guestPagedTotal);
12201 aCollector->registerBaseMetric(guestCpuMem);
12202
12203 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12204 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12205 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12206 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12207
12208 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12209 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12210 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12211 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12212
12213 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12214 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12215 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12216 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12217
12218 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12219 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12220 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12221 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12222
12223 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12225 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12226 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12227
12228 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12229 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12230 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12231 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12232
12233 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12237
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12242
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12246 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12247
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12251 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12252
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12256 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12257}
12258
12259void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12260{
12261 AssertReturnVoid(isWriteLockOnCurrentThread());
12262
12263 if (aCollector)
12264 {
12265 aCollector->unregisterMetricsFor(aMachine);
12266 aCollector->unregisterBaseMetricsFor(aMachine);
12267 }
12268}
12269
12270#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12271
12272
12273////////////////////////////////////////////////////////////////////////////////
12274
12275DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12276
12277HRESULT SessionMachine::FinalConstruct()
12278{
12279 LogFlowThisFunc(("\n"));
12280
12281 mClientToken = NULL;
12282
12283 return BaseFinalConstruct();
12284}
12285
12286void SessionMachine::FinalRelease()
12287{
12288 LogFlowThisFunc(("\n"));
12289
12290 Assert(!mClientToken);
12291 /* paranoia, should not hang around any more */
12292 if (mClientToken)
12293 {
12294 delete mClientToken;
12295 mClientToken = NULL;
12296 }
12297
12298 uninit(Uninit::Unexpected);
12299
12300 BaseFinalRelease();
12301}
12302
12303/**
12304 * @note Must be called only by Machine::LockMachine() from its own write lock.
12305 */
12306HRESULT SessionMachine::init(Machine *aMachine)
12307{
12308 LogFlowThisFuncEnter();
12309 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12310
12311 AssertReturn(aMachine, E_INVALIDARG);
12312
12313 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12314
12315 /* Enclose the state transition NotReady->InInit->Ready */
12316 AutoInitSpan autoInitSpan(this);
12317 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12318
12319 HRESULT rc = S_OK;
12320
12321 RT_ZERO(mAuthLibCtx);
12322
12323 /* create the machine client token */
12324 try
12325 {
12326 mClientToken = new ClientToken(aMachine, this);
12327 if (!mClientToken->isReady())
12328 {
12329 delete mClientToken;
12330 mClientToken = NULL;
12331 rc = E_FAIL;
12332 }
12333 }
12334 catch (std::bad_alloc &)
12335 {
12336 rc = E_OUTOFMEMORY;
12337 }
12338 if (FAILED(rc))
12339 return rc;
12340
12341 /* memorize the peer Machine */
12342 unconst(mPeer) = aMachine;
12343 /* share the parent pointer */
12344 unconst(mParent) = aMachine->mParent;
12345
12346 /* take the pointers to data to share */
12347 mData.share(aMachine->mData);
12348 mSSData.share(aMachine->mSSData);
12349
12350 mUserData.share(aMachine->mUserData);
12351 mHWData.share(aMachine->mHWData);
12352 mMediumAttachments.share(aMachine->mMediumAttachments);
12353
12354 mStorageControllers.allocate();
12355 for (StorageControllerList::const_iterator
12356 it = aMachine->mStorageControllers->begin();
12357 it != aMachine->mStorageControllers->end();
12358 ++it)
12359 {
12360 ComObjPtr<StorageController> ctl;
12361 ctl.createObject();
12362 ctl->init(this, *it);
12363 mStorageControllers->push_back(ctl);
12364 }
12365
12366 mUSBControllers.allocate();
12367 for (USBControllerList::const_iterator
12368 it = aMachine->mUSBControllers->begin();
12369 it != aMachine->mUSBControllers->end();
12370 ++it)
12371 {
12372 ComObjPtr<USBController> ctl;
12373 ctl.createObject();
12374 ctl->init(this, *it);
12375 mUSBControllers->push_back(ctl);
12376 }
12377
12378 unconst(mBIOSSettings).createObject();
12379 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12380 unconst(mRecordingSettings).createObject();
12381 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12382 /* create another GraphicsAdapter object that will be mutable */
12383 unconst(mGraphicsAdapter).createObject();
12384 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12385 /* create another VRDEServer object that will be mutable */
12386 unconst(mVRDEServer).createObject();
12387 mVRDEServer->init(this, aMachine->mVRDEServer);
12388 /* create another audio adapter object that will be mutable */
12389 unconst(mAudioAdapter).createObject();
12390 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12391 /* create a list of serial ports that will be mutable */
12392 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12393 {
12394 unconst(mSerialPorts[slot]).createObject();
12395 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12396 }
12397 /* create a list of parallel ports that will be mutable */
12398 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12399 {
12400 unconst(mParallelPorts[slot]).createObject();
12401 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12402 }
12403
12404 /* create another USB device filters object that will be mutable */
12405 unconst(mUSBDeviceFilters).createObject();
12406 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12407
12408 /* create a list of network adapters that will be mutable */
12409 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12410 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12411 {
12412 unconst(mNetworkAdapters[slot]).createObject();
12413 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12414 }
12415
12416 /* create another bandwidth control object that will be mutable */
12417 unconst(mBandwidthControl).createObject();
12418 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12419
12420 /* default is to delete saved state on Saved -> PoweredOff transition */
12421 mRemoveSavedState = true;
12422
12423 /* Confirm a successful initialization when it's the case */
12424 autoInitSpan.setSucceeded();
12425
12426 miNATNetworksStarted = 0;
12427
12428 LogFlowThisFuncLeave();
12429 return rc;
12430}
12431
12432/**
12433 * Uninitializes this session object. If the reason is other than
12434 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12435 * or the client watcher code.
12436 *
12437 * @param aReason uninitialization reason
12438 *
12439 * @note Locks mParent + this object for writing.
12440 */
12441void SessionMachine::uninit(Uninit::Reason aReason)
12442{
12443 LogFlowThisFuncEnter();
12444 LogFlowThisFunc(("reason=%d\n", aReason));
12445
12446 /*
12447 * Strongly reference ourselves to prevent this object deletion after
12448 * mData->mSession.mMachine.setNull() below (which can release the last
12449 * reference and call the destructor). Important: this must be done before
12450 * accessing any members (and before AutoUninitSpan that does it as well).
12451 * This self reference will be released as the very last step on return.
12452 */
12453 ComObjPtr<SessionMachine> selfRef;
12454 if (aReason != Uninit::Unexpected)
12455 selfRef = this;
12456
12457 /* Enclose the state transition Ready->InUninit->NotReady */
12458 AutoUninitSpan autoUninitSpan(this);
12459 if (autoUninitSpan.uninitDone())
12460 {
12461 LogFlowThisFunc(("Already uninitialized\n"));
12462 LogFlowThisFuncLeave();
12463 return;
12464 }
12465
12466 if (autoUninitSpan.initFailed())
12467 {
12468 /* We've been called by init() because it's failed. It's not really
12469 * necessary (nor it's safe) to perform the regular uninit sequence
12470 * below, the following is enough.
12471 */
12472 LogFlowThisFunc(("Initialization failed.\n"));
12473 /* destroy the machine client token */
12474 if (mClientToken)
12475 {
12476 delete mClientToken;
12477 mClientToken = NULL;
12478 }
12479 uninitDataAndChildObjects();
12480 mData.free();
12481 unconst(mParent) = NULL;
12482 unconst(mPeer) = NULL;
12483 LogFlowThisFuncLeave();
12484 return;
12485 }
12486
12487 MachineState_T lastState;
12488 {
12489 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12490 lastState = mData->mMachineState;
12491 }
12492 NOREF(lastState);
12493
12494#ifdef VBOX_WITH_USB
12495 // release all captured USB devices, but do this before requesting the locks below
12496 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12497 {
12498 /* Console::captureUSBDevices() is called in the VM process only after
12499 * setting the machine state to Starting or Restoring.
12500 * Console::detachAllUSBDevices() will be called upon successful
12501 * termination. So, we need to release USB devices only if there was
12502 * an abnormal termination of a running VM.
12503 *
12504 * This is identical to SessionMachine::DetachAllUSBDevices except
12505 * for the aAbnormal argument. */
12506 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12507 AssertComRC(rc);
12508 NOREF(rc);
12509
12510 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12511 if (service)
12512 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12513 }
12514#endif /* VBOX_WITH_USB */
12515
12516 // we need to lock this object in uninit() because the lock is shared
12517 // with mPeer (as well as data we modify below). mParent lock is needed
12518 // by several calls to it.
12519 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12520
12521#ifdef VBOX_WITH_RESOURCE_USAGE_API
12522 /*
12523 * It is safe to call Machine::i_unregisterMetrics() here because
12524 * PerformanceCollector::samplerCallback no longer accesses guest methods
12525 * holding the lock.
12526 */
12527 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12528 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12529 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12530 if (mCollectorGuest)
12531 {
12532 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12533 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12534 mCollectorGuest = NULL;
12535 }
12536#endif
12537
12538 if (aReason == Uninit::Abnormal)
12539 {
12540 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12541
12542 /* reset the state to Aborted */
12543 if (mData->mMachineState != MachineState_Aborted)
12544 i_setMachineState(MachineState_Aborted);
12545 }
12546
12547 // any machine settings modified?
12548 if (mData->flModifications)
12549 {
12550 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12551 i_rollback(false /* aNotify */);
12552 }
12553
12554 mData->mSession.mPID = NIL_RTPROCESS;
12555
12556 if (aReason == Uninit::Unexpected)
12557 {
12558 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12559 * client watcher thread to update the set of machines that have open
12560 * sessions. */
12561 mParent->i_updateClientWatcher();
12562 }
12563
12564 /* uninitialize all remote controls */
12565 if (mData->mSession.mRemoteControls.size())
12566 {
12567 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12568 mData->mSession.mRemoteControls.size()));
12569
12570 /* Always restart a the beginning, since the iterator is invalidated
12571 * by using erase(). */
12572 for (Data::Session::RemoteControlList::iterator
12573 it = mData->mSession.mRemoteControls.begin();
12574 it != mData->mSession.mRemoteControls.end();
12575 it = mData->mSession.mRemoteControls.begin())
12576 {
12577 ComPtr<IInternalSessionControl> pControl = *it;
12578 mData->mSession.mRemoteControls.erase(it);
12579 multilock.release();
12580 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12581 HRESULT rc = pControl->Uninitialize();
12582 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12583 if (FAILED(rc))
12584 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12585 multilock.acquire();
12586 }
12587 mData->mSession.mRemoteControls.clear();
12588 }
12589
12590 /* Remove all references to the NAT network service. The service will stop
12591 * if all references (also from other VMs) are removed. */
12592 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12593 {
12594 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12595 {
12596 BOOL enabled;
12597 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12598 if ( FAILED(hrc)
12599 || !enabled)
12600 continue;
12601
12602 NetworkAttachmentType_T type;
12603 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12604 if ( SUCCEEDED(hrc)
12605 && type == NetworkAttachmentType_NATNetwork)
12606 {
12607 Bstr name;
12608 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12609 if (SUCCEEDED(hrc))
12610 {
12611 multilock.release();
12612 Utf8Str strName(name);
12613 LogRel(("VM '%s' stops using NAT network '%s'\n",
12614 mUserData->s.strName.c_str(), strName.c_str()));
12615 mParent->i_natNetworkRefDec(strName);
12616 multilock.acquire();
12617 }
12618 }
12619 }
12620 }
12621
12622 /*
12623 * An expected uninitialization can come only from #i_checkForDeath().
12624 * Otherwise it means that something's gone really wrong (for example,
12625 * the Session implementation has released the VirtualBox reference
12626 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12627 * etc). However, it's also possible, that the client releases the IPC
12628 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12629 * but the VirtualBox release event comes first to the server process.
12630 * This case is practically possible, so we should not assert on an
12631 * unexpected uninit, just log a warning.
12632 */
12633
12634 if (aReason == Uninit::Unexpected)
12635 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12636
12637 if (aReason != Uninit::Normal)
12638 {
12639 mData->mSession.mDirectControl.setNull();
12640 }
12641 else
12642 {
12643 /* this must be null here (see #OnSessionEnd()) */
12644 Assert(mData->mSession.mDirectControl.isNull());
12645 Assert(mData->mSession.mState == SessionState_Unlocking);
12646 Assert(!mData->mSession.mProgress.isNull());
12647 }
12648 if (mData->mSession.mProgress)
12649 {
12650 if (aReason == Uninit::Normal)
12651 mData->mSession.mProgress->i_notifyComplete(S_OK);
12652 else
12653 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12654 COM_IIDOF(ISession),
12655 getComponentName(),
12656 tr("The VM session was aborted"));
12657 mData->mSession.mProgress.setNull();
12658 }
12659
12660 if (mConsoleTaskData.mProgress)
12661 {
12662 Assert(aReason == Uninit::Abnormal);
12663 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12664 COM_IIDOF(ISession),
12665 getComponentName(),
12666 tr("The VM session was aborted"));
12667 mConsoleTaskData.mProgress.setNull();
12668 }
12669
12670 /* remove the association between the peer machine and this session machine */
12671 Assert( (SessionMachine*)mData->mSession.mMachine == this
12672 || aReason == Uninit::Unexpected);
12673
12674 /* reset the rest of session data */
12675 mData->mSession.mLockType = LockType_Null;
12676 mData->mSession.mMachine.setNull();
12677 mData->mSession.mState = SessionState_Unlocked;
12678 mData->mSession.mName.setNull();
12679
12680 /* destroy the machine client token before leaving the exclusive lock */
12681 if (mClientToken)
12682 {
12683 delete mClientToken;
12684 mClientToken = NULL;
12685 }
12686
12687 /* fire an event */
12688 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12689
12690 uninitDataAndChildObjects();
12691
12692 /* free the essential data structure last */
12693 mData.free();
12694
12695 /* release the exclusive lock before setting the below two to NULL */
12696 multilock.release();
12697
12698 unconst(mParent) = NULL;
12699 unconst(mPeer) = NULL;
12700
12701 AuthLibUnload(&mAuthLibCtx);
12702
12703 LogFlowThisFuncLeave();
12704}
12705
12706// util::Lockable interface
12707////////////////////////////////////////////////////////////////////////////////
12708
12709/**
12710 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12711 * with the primary Machine instance (mPeer).
12712 */
12713RWLockHandle *SessionMachine::lockHandle() const
12714{
12715 AssertReturn(mPeer != NULL, NULL);
12716 return mPeer->lockHandle();
12717}
12718
12719// IInternalMachineControl methods
12720////////////////////////////////////////////////////////////////////////////////
12721
12722/**
12723 * Passes collected guest statistics to performance collector object
12724 */
12725HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12726 ULONG aCpuKernel, ULONG aCpuIdle,
12727 ULONG aMemTotal, ULONG aMemFree,
12728 ULONG aMemBalloon, ULONG aMemShared,
12729 ULONG aMemCache, ULONG aPageTotal,
12730 ULONG aAllocVMM, ULONG aFreeVMM,
12731 ULONG aBalloonedVMM, ULONG aSharedVMM,
12732 ULONG aVmNetRx, ULONG aVmNetTx)
12733{
12734#ifdef VBOX_WITH_RESOURCE_USAGE_API
12735 if (mCollectorGuest)
12736 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12737 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12738 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12739 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12740
12741 return S_OK;
12742#else
12743 NOREF(aValidStats);
12744 NOREF(aCpuUser);
12745 NOREF(aCpuKernel);
12746 NOREF(aCpuIdle);
12747 NOREF(aMemTotal);
12748 NOREF(aMemFree);
12749 NOREF(aMemBalloon);
12750 NOREF(aMemShared);
12751 NOREF(aMemCache);
12752 NOREF(aPageTotal);
12753 NOREF(aAllocVMM);
12754 NOREF(aFreeVMM);
12755 NOREF(aBalloonedVMM);
12756 NOREF(aSharedVMM);
12757 NOREF(aVmNetRx);
12758 NOREF(aVmNetTx);
12759 return E_NOTIMPL;
12760#endif
12761}
12762
12763////////////////////////////////////////////////////////////////////////////////
12764//
12765// SessionMachine task records
12766//
12767////////////////////////////////////////////////////////////////////////////////
12768
12769/**
12770 * Task record for saving the machine state.
12771 */
12772class SessionMachine::SaveStateTask
12773 : public Machine::Task
12774{
12775public:
12776 SaveStateTask(SessionMachine *m,
12777 Progress *p,
12778 const Utf8Str &t,
12779 Reason_T enmReason,
12780 const Utf8Str &strStateFilePath)
12781 : Task(m, p, t),
12782 m_enmReason(enmReason),
12783 m_strStateFilePath(strStateFilePath)
12784 {}
12785
12786private:
12787 void handler()
12788 {
12789 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12790 }
12791
12792 Reason_T m_enmReason;
12793 Utf8Str m_strStateFilePath;
12794
12795 friend class SessionMachine;
12796};
12797
12798/**
12799 * Task thread implementation for SessionMachine::SaveState(), called from
12800 * SessionMachine::taskHandler().
12801 *
12802 * @note Locks this object for writing.
12803 *
12804 * @param task
12805 * @return
12806 */
12807void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12808{
12809 LogFlowThisFuncEnter();
12810
12811 AutoCaller autoCaller(this);
12812 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12813 if (FAILED(autoCaller.rc()))
12814 {
12815 /* we might have been uninitialized because the session was accidentally
12816 * closed by the client, so don't assert */
12817 HRESULT rc = setError(E_FAIL,
12818 tr("The session has been accidentally closed"));
12819 task.m_pProgress->i_notifyComplete(rc);
12820 LogFlowThisFuncLeave();
12821 return;
12822 }
12823
12824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12825
12826 HRESULT rc = S_OK;
12827
12828 try
12829 {
12830 ComPtr<IInternalSessionControl> directControl;
12831 if (mData->mSession.mLockType == LockType_VM)
12832 directControl = mData->mSession.mDirectControl;
12833 if (directControl.isNull())
12834 throw setError(VBOX_E_INVALID_VM_STATE,
12835 tr("Trying to save state without a running VM"));
12836 alock.release();
12837 BOOL fSuspendedBySave;
12838 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12839 Assert(!fSuspendedBySave);
12840 alock.acquire();
12841
12842 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12843 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12844 throw E_FAIL);
12845
12846 if (SUCCEEDED(rc))
12847 {
12848 mSSData->strStateFilePath = task.m_strStateFilePath;
12849
12850 /* save all VM settings */
12851 rc = i_saveSettings(NULL);
12852 // no need to check whether VirtualBox.xml needs saving also since
12853 // we can't have a name change pending at this point
12854 }
12855 else
12856 {
12857 // On failure, set the state to the state we had at the beginning.
12858 i_setMachineState(task.m_machineStateBackup);
12859 i_updateMachineStateOnClient();
12860
12861 // Delete the saved state file (might have been already created).
12862 // No need to check whether this is shared with a snapshot here
12863 // because we certainly created a fresh saved state file here.
12864 RTFileDelete(task.m_strStateFilePath.c_str());
12865 }
12866 }
12867 catch (HRESULT aRC) { rc = aRC; }
12868
12869 task.m_pProgress->i_notifyComplete(rc);
12870
12871 LogFlowThisFuncLeave();
12872}
12873
12874/**
12875 * @note Locks this object for writing.
12876 */
12877HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12878{
12879 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12880}
12881
12882HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12883{
12884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12885
12886 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12887 if (FAILED(rc)) return rc;
12888
12889 if ( mData->mMachineState != MachineState_Running
12890 && mData->mMachineState != MachineState_Paused
12891 )
12892 return setError(VBOX_E_INVALID_VM_STATE,
12893 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12894 Global::stringifyMachineState(mData->mMachineState));
12895
12896 ComObjPtr<Progress> pProgress;
12897 pProgress.createObject();
12898 rc = pProgress->init(i_getVirtualBox(),
12899 static_cast<IMachine *>(this) /* aInitiator */,
12900 tr("Saving the execution state of the virtual machine"),
12901 FALSE /* aCancelable */);
12902 if (FAILED(rc))
12903 return rc;
12904
12905 Utf8Str strStateFilePath;
12906 i_composeSavedStateFilename(strStateFilePath);
12907
12908 /* create and start the task on a separate thread (note that it will not
12909 * start working until we release alock) */
12910 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12911 rc = pTask->createThread();
12912 if (FAILED(rc))
12913 return rc;
12914
12915 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12916 i_setMachineState(MachineState_Saving);
12917 i_updateMachineStateOnClient();
12918
12919 pProgress.queryInterfaceTo(aProgress.asOutParam());
12920
12921 return S_OK;
12922}
12923
12924/**
12925 * @note Locks this object for writing.
12926 */
12927HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12928{
12929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12930
12931 HRESULT rc = i_checkStateDependency(MutableStateDep);
12932 if (FAILED(rc)) return rc;
12933
12934 if ( mData->mMachineState != MachineState_PoweredOff
12935 && mData->mMachineState != MachineState_Teleported
12936 && mData->mMachineState != MachineState_Aborted
12937 )
12938 return setError(VBOX_E_INVALID_VM_STATE,
12939 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12940 Global::stringifyMachineState(mData->mMachineState));
12941
12942 com::Utf8Str stateFilePathFull;
12943 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12944 if (RT_FAILURE(vrc))
12945 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12946 tr("Invalid saved state file path '%s' (%Rrc)"),
12947 aSavedStateFile.c_str(),
12948 vrc);
12949
12950 mSSData->strStateFilePath = stateFilePathFull;
12951
12952 /* The below i_setMachineState() will detect the state transition and will
12953 * update the settings file */
12954
12955 return i_setMachineState(MachineState_Saved);
12956}
12957
12958/**
12959 * @note Locks this object for writing.
12960 */
12961HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12962{
12963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12964
12965 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12966 if (FAILED(rc)) return rc;
12967
12968 if (mData->mMachineState != MachineState_Saved)
12969 return setError(VBOX_E_INVALID_VM_STATE,
12970 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12971 Global::stringifyMachineState(mData->mMachineState));
12972
12973 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12974
12975 /*
12976 * Saved -> PoweredOff transition will be detected in the SessionMachine
12977 * and properly handled.
12978 */
12979 rc = i_setMachineState(MachineState_PoweredOff);
12980 return rc;
12981}
12982
12983
12984/**
12985 * @note Locks the same as #i_setMachineState() does.
12986 */
12987HRESULT SessionMachine::updateState(MachineState_T aState)
12988{
12989 return i_setMachineState(aState);
12990}
12991
12992/**
12993 * @note Locks this object for writing.
12994 */
12995HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12996{
12997 IProgress *pProgress(aProgress);
12998
12999 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13000
13001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13002
13003 if (mData->mSession.mState != SessionState_Locked)
13004 return VBOX_E_INVALID_OBJECT_STATE;
13005
13006 if (!mData->mSession.mProgress.isNull())
13007 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13008
13009 /* If we didn't reference the NAT network service yet, add a reference to
13010 * force a start */
13011 if (miNATNetworksStarted < 1)
13012 {
13013 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13014 {
13015 BOOL enabled;
13016 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13017 if ( FAILED(hrc)
13018 || !enabled)
13019 continue;
13020
13021 NetworkAttachmentType_T type;
13022 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13023 if ( SUCCEEDED(hrc)
13024 && type == NetworkAttachmentType_NATNetwork)
13025 {
13026 Bstr name;
13027 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13028 if (SUCCEEDED(hrc))
13029 {
13030 Utf8Str strName(name);
13031 LogRel(("VM '%s' starts using NAT network '%s'\n",
13032 mUserData->s.strName.c_str(), strName.c_str()));
13033 mPeer->lockHandle()->unlockWrite();
13034 mParent->i_natNetworkRefInc(strName);
13035#ifdef RT_LOCK_STRICT
13036 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13037#else
13038 mPeer->lockHandle()->lockWrite();
13039#endif
13040 }
13041 }
13042 }
13043 miNATNetworksStarted++;
13044 }
13045
13046 LogFlowThisFunc(("returns S_OK.\n"));
13047 return S_OK;
13048}
13049
13050/**
13051 * @note Locks this object for writing.
13052 */
13053HRESULT SessionMachine::endPowerUp(LONG aResult)
13054{
13055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13056
13057 if (mData->mSession.mState != SessionState_Locked)
13058 return VBOX_E_INVALID_OBJECT_STATE;
13059
13060 /* Finalize the LaunchVMProcess progress object. */
13061 if (mData->mSession.mProgress)
13062 {
13063 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13064 mData->mSession.mProgress.setNull();
13065 }
13066
13067 if (SUCCEEDED((HRESULT)aResult))
13068 {
13069#ifdef VBOX_WITH_RESOURCE_USAGE_API
13070 /* The VM has been powered up successfully, so it makes sense
13071 * now to offer the performance metrics for a running machine
13072 * object. Doing it earlier wouldn't be safe. */
13073 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13074 mData->mSession.mPID);
13075#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13076 }
13077
13078 return S_OK;
13079}
13080
13081/**
13082 * @note Locks this object for writing.
13083 */
13084HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13085{
13086 LogFlowThisFuncEnter();
13087
13088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13089
13090 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13091 E_FAIL);
13092
13093 /* create a progress object to track operation completion */
13094 ComObjPtr<Progress> pProgress;
13095 pProgress.createObject();
13096 pProgress->init(i_getVirtualBox(),
13097 static_cast<IMachine *>(this) /* aInitiator */,
13098 tr("Stopping the virtual machine"),
13099 FALSE /* aCancelable */);
13100
13101 /* fill in the console task data */
13102 mConsoleTaskData.mLastState = mData->mMachineState;
13103 mConsoleTaskData.mProgress = pProgress;
13104
13105 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13106 i_setMachineState(MachineState_Stopping);
13107
13108 pProgress.queryInterfaceTo(aProgress.asOutParam());
13109
13110 return S_OK;
13111}
13112
13113/**
13114 * @note Locks this object for writing.
13115 */
13116HRESULT SessionMachine::endPoweringDown(LONG aResult,
13117 const com::Utf8Str &aErrMsg)
13118{
13119 HRESULT const hrcResult = (HRESULT)aResult;
13120 LogFlowThisFuncEnter();
13121
13122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13123
13124 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13125 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13126 && mConsoleTaskData.mLastState != MachineState_Null,
13127 E_FAIL);
13128
13129 /*
13130 * On failure, set the state to the state we had when BeginPoweringDown()
13131 * was called (this is expected by Console::PowerDown() and the associated
13132 * task). On success the VM process already changed the state to
13133 * MachineState_PoweredOff, so no need to do anything.
13134 */
13135 if (FAILED(hrcResult))
13136 i_setMachineState(mConsoleTaskData.mLastState);
13137
13138 /* notify the progress object about operation completion */
13139 Assert(mConsoleTaskData.mProgress);
13140 if (SUCCEEDED(hrcResult))
13141 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13142 else
13143 {
13144 if (aErrMsg.length())
13145 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13146 COM_IIDOF(ISession),
13147 getComponentName(),
13148 aErrMsg.c_str());
13149 else
13150 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13151 }
13152
13153 /* clear out the temporary saved state data */
13154 mConsoleTaskData.mLastState = MachineState_Null;
13155 mConsoleTaskData.mProgress.setNull();
13156
13157 LogFlowThisFuncLeave();
13158 return S_OK;
13159}
13160
13161
13162/**
13163 * Goes through the USB filters of the given machine to see if the given
13164 * device matches any filter or not.
13165 *
13166 * @note Locks the same as USBController::hasMatchingFilter() does.
13167 */
13168HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13169 BOOL *aMatched,
13170 ULONG *aMaskedInterfaces)
13171{
13172 LogFlowThisFunc(("\n"));
13173
13174#ifdef VBOX_WITH_USB
13175 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13176#else
13177 NOREF(aDevice);
13178 NOREF(aMaskedInterfaces);
13179 *aMatched = FALSE;
13180#endif
13181
13182 return S_OK;
13183}
13184
13185/**
13186 * @note Locks the same as Host::captureUSBDevice() does.
13187 */
13188HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13189{
13190 LogFlowThisFunc(("\n"));
13191
13192#ifdef VBOX_WITH_USB
13193 /* if captureDeviceForVM() fails, it must have set extended error info */
13194 clearError();
13195 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13196 if (FAILED(rc)) return rc;
13197
13198 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13199 AssertReturn(service, E_FAIL);
13200 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13201#else
13202 RT_NOREF(aId, aCaptureFilename);
13203 return E_NOTIMPL;
13204#endif
13205}
13206
13207/**
13208 * @note Locks the same as Host::detachUSBDevice() does.
13209 */
13210HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13211 BOOL aDone)
13212{
13213 LogFlowThisFunc(("\n"));
13214
13215#ifdef VBOX_WITH_USB
13216 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13217 AssertReturn(service, E_FAIL);
13218 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13219#else
13220 NOREF(aId);
13221 NOREF(aDone);
13222 return E_NOTIMPL;
13223#endif
13224}
13225
13226/**
13227 * Inserts all machine filters to the USB proxy service and then calls
13228 * Host::autoCaptureUSBDevices().
13229 *
13230 * Called by Console from the VM process upon VM startup.
13231 *
13232 * @note Locks what called methods lock.
13233 */
13234HRESULT SessionMachine::autoCaptureUSBDevices()
13235{
13236 LogFlowThisFunc(("\n"));
13237
13238#ifdef VBOX_WITH_USB
13239 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13240 AssertComRC(rc);
13241 NOREF(rc);
13242
13243 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13244 AssertReturn(service, E_FAIL);
13245 return service->autoCaptureDevicesForVM(this);
13246#else
13247 return S_OK;
13248#endif
13249}
13250
13251/**
13252 * Removes all machine filters from the USB proxy service and then calls
13253 * Host::detachAllUSBDevices().
13254 *
13255 * Called by Console from the VM process upon normal VM termination or by
13256 * SessionMachine::uninit() upon abnormal VM termination (from under the
13257 * Machine/SessionMachine lock).
13258 *
13259 * @note Locks what called methods lock.
13260 */
13261HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13262{
13263 LogFlowThisFunc(("\n"));
13264
13265#ifdef VBOX_WITH_USB
13266 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13267 AssertComRC(rc);
13268 NOREF(rc);
13269
13270 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13271 AssertReturn(service, E_FAIL);
13272 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13273#else
13274 NOREF(aDone);
13275 return S_OK;
13276#endif
13277}
13278
13279/**
13280 * @note Locks this object for writing.
13281 */
13282HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13283 ComPtr<IProgress> &aProgress)
13284{
13285 LogFlowThisFuncEnter();
13286
13287 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13288 /*
13289 * We don't assert below because it might happen that a non-direct session
13290 * informs us it is closed right after we've been uninitialized -- it's ok.
13291 */
13292
13293 /* get IInternalSessionControl interface */
13294 ComPtr<IInternalSessionControl> control(aSession);
13295
13296 ComAssertRet(!control.isNull(), E_INVALIDARG);
13297
13298 /* Creating a Progress object requires the VirtualBox lock, and
13299 * thus locking it here is required by the lock order rules. */
13300 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13301
13302 if (control == mData->mSession.mDirectControl)
13303 {
13304 /* The direct session is being normally closed by the client process
13305 * ----------------------------------------------------------------- */
13306
13307 /* go to the closing state (essential for all open*Session() calls and
13308 * for #i_checkForDeath()) */
13309 Assert(mData->mSession.mState == SessionState_Locked);
13310 mData->mSession.mState = SessionState_Unlocking;
13311
13312 /* set direct control to NULL to release the remote instance */
13313 mData->mSession.mDirectControl.setNull();
13314 LogFlowThisFunc(("Direct control is set to NULL\n"));
13315
13316 if (mData->mSession.mProgress)
13317 {
13318 /* finalize the progress, someone might wait if a frontend
13319 * closes the session before powering on the VM. */
13320 mData->mSession.mProgress->notifyComplete(E_FAIL,
13321 COM_IIDOF(ISession),
13322 getComponentName(),
13323 tr("The VM session was closed before any attempt to power it on"));
13324 mData->mSession.mProgress.setNull();
13325 }
13326
13327 /* Create the progress object the client will use to wait until
13328 * #i_checkForDeath() is called to uninitialize this session object after
13329 * it releases the IPC semaphore.
13330 * Note! Because we're "reusing" mProgress here, this must be a proxy
13331 * object just like for LaunchVMProcess. */
13332 Assert(mData->mSession.mProgress.isNull());
13333 ComObjPtr<ProgressProxy> progress;
13334 progress.createObject();
13335 ComPtr<IUnknown> pPeer(mPeer);
13336 progress->init(mParent, pPeer,
13337 Bstr(tr("Closing session")).raw(),
13338 FALSE /* aCancelable */);
13339 progress.queryInterfaceTo(aProgress.asOutParam());
13340 mData->mSession.mProgress = progress;
13341 }
13342 else
13343 {
13344 /* the remote session is being normally closed */
13345 bool found = false;
13346 for (Data::Session::RemoteControlList::iterator
13347 it = mData->mSession.mRemoteControls.begin();
13348 it != mData->mSession.mRemoteControls.end();
13349 ++it)
13350 {
13351 if (control == *it)
13352 {
13353 found = true;
13354 // This MUST be erase(it), not remove(*it) as the latter
13355 // triggers a very nasty use after free due to the place where
13356 // the value "lives".
13357 mData->mSession.mRemoteControls.erase(it);
13358 break;
13359 }
13360 }
13361 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13362 E_INVALIDARG);
13363 }
13364
13365 /* signal the client watcher thread, because the client is going away */
13366 mParent->i_updateClientWatcher();
13367
13368 LogFlowThisFuncLeave();
13369 return S_OK;
13370}
13371
13372HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13373{
13374#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13375 ULONG uID;
13376 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13377 if (RT_SUCCESS(rc))
13378 {
13379 if (aID)
13380 *aID = uID;
13381 return S_OK;
13382 }
13383 return E_FAIL;
13384#else
13385 RT_NOREF(aParms, aID);
13386 ReturnComNotImplemented();
13387#endif
13388}
13389
13390HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13391{
13392#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13393 return mParent->i_onClipboardAreaUnregister(aID);
13394#else
13395 RT_NOREF(aID);
13396 ReturnComNotImplemented();
13397#endif
13398}
13399
13400HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13401{
13402#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13403 return mParent->i_onClipboardAreaAttach(aID);
13404#else
13405 RT_NOREF(aID);
13406 ReturnComNotImplemented();
13407#endif
13408}
13409HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13410{
13411#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13412 return mParent->i_onClipboardAreaDetach(aID);
13413#else
13414 RT_NOREF(aID);
13415 ReturnComNotImplemented();
13416#endif
13417}
13418
13419HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13420{
13421#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13422 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13423 if (aID)
13424 *aID = uID;
13425 return S_OK;
13426#else
13427 RT_NOREF(aID);
13428 ReturnComNotImplemented();
13429#endif
13430}
13431
13432HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13433{
13434#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13435 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13436 if (aRefCount)
13437 *aRefCount = uRefCount;
13438 return S_OK;
13439#else
13440 RT_NOREF(aID, aRefCount);
13441 ReturnComNotImplemented();
13442#endif
13443}
13444
13445HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13446 std::vector<com::Utf8Str> &aValues,
13447 std::vector<LONG64> &aTimestamps,
13448 std::vector<com::Utf8Str> &aFlags)
13449{
13450 LogFlowThisFunc(("\n"));
13451
13452#ifdef VBOX_WITH_GUEST_PROPS
13453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13454
13455 size_t cEntries = mHWData->mGuestProperties.size();
13456 aNames.resize(cEntries);
13457 aValues.resize(cEntries);
13458 aTimestamps.resize(cEntries);
13459 aFlags.resize(cEntries);
13460
13461 size_t i = 0;
13462 for (HWData::GuestPropertyMap::const_iterator
13463 it = mHWData->mGuestProperties.begin();
13464 it != mHWData->mGuestProperties.end();
13465 ++it, ++i)
13466 {
13467 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13468 aNames[i] = it->first;
13469 aValues[i] = it->second.strValue;
13470 aTimestamps[i] = it->second.mTimestamp;
13471
13472 /* If it is NULL, keep it NULL. */
13473 if (it->second.mFlags)
13474 {
13475 GuestPropWriteFlags(it->second.mFlags, szFlags);
13476 aFlags[i] = szFlags;
13477 }
13478 else
13479 aFlags[i] = "";
13480 }
13481 return S_OK;
13482#else
13483 ReturnComNotImplemented();
13484#endif
13485}
13486
13487HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13488 const com::Utf8Str &aValue,
13489 LONG64 aTimestamp,
13490 const com::Utf8Str &aFlags)
13491{
13492 LogFlowThisFunc(("\n"));
13493
13494#ifdef VBOX_WITH_GUEST_PROPS
13495 try
13496 {
13497 /*
13498 * Convert input up front.
13499 */
13500 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13501 if (aFlags.length())
13502 {
13503 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13504 AssertRCReturn(vrc, E_INVALIDARG);
13505 }
13506
13507 /*
13508 * Now grab the object lock, validate the state and do the update.
13509 */
13510
13511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13512
13513 if (!Global::IsOnline(mData->mMachineState))
13514 {
13515 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13516 VBOX_E_INVALID_VM_STATE);
13517 }
13518
13519 i_setModified(IsModified_MachineData);
13520 mHWData.backup();
13521
13522 bool fDelete = !aValue.length();
13523 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13524 if (it != mHWData->mGuestProperties.end())
13525 {
13526 if (!fDelete)
13527 {
13528 it->second.strValue = aValue;
13529 it->second.mTimestamp = aTimestamp;
13530 it->second.mFlags = fFlags;
13531 }
13532 else
13533 mHWData->mGuestProperties.erase(it);
13534
13535 mData->mGuestPropertiesModified = TRUE;
13536 }
13537 else if (!fDelete)
13538 {
13539 HWData::GuestProperty prop;
13540 prop.strValue = aValue;
13541 prop.mTimestamp = aTimestamp;
13542 prop.mFlags = fFlags;
13543
13544 mHWData->mGuestProperties[aName] = prop;
13545 mData->mGuestPropertiesModified = TRUE;
13546 }
13547
13548 alock.release();
13549
13550 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13551 }
13552 catch (...)
13553 {
13554 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13555 }
13556 return S_OK;
13557#else
13558 ReturnComNotImplemented();
13559#endif
13560}
13561
13562
13563HRESULT SessionMachine::lockMedia()
13564{
13565 AutoMultiWriteLock2 alock(this->lockHandle(),
13566 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13567
13568 AssertReturn( mData->mMachineState == MachineState_Starting
13569 || mData->mMachineState == MachineState_Restoring
13570 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13571
13572 clearError();
13573 alock.release();
13574 return i_lockMedia();
13575}
13576
13577HRESULT SessionMachine::unlockMedia()
13578{
13579 HRESULT hrc = i_unlockMedia();
13580 return hrc;
13581}
13582
13583HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13584 ComPtr<IMediumAttachment> &aNewAttachment)
13585{
13586 // request the host lock first, since might be calling Host methods for getting host drives;
13587 // next, protect the media tree all the while we're in here, as well as our member variables
13588 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13589 this->lockHandle(),
13590 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13591
13592 IMediumAttachment *iAttach = aAttachment;
13593 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13594
13595 Utf8Str ctrlName;
13596 LONG lPort;
13597 LONG lDevice;
13598 bool fTempEject;
13599 {
13600 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13601
13602 /* Need to query the details first, as the IMediumAttachment reference
13603 * might be to the original settings, which we are going to change. */
13604 ctrlName = pAttach->i_getControllerName();
13605 lPort = pAttach->i_getPort();
13606 lDevice = pAttach->i_getDevice();
13607 fTempEject = pAttach->i_getTempEject();
13608 }
13609
13610 if (!fTempEject)
13611 {
13612 /* Remember previously mounted medium. The medium before taking the
13613 * backup is not necessarily the same thing. */
13614 ComObjPtr<Medium> oldmedium;
13615 oldmedium = pAttach->i_getMedium();
13616
13617 i_setModified(IsModified_Storage);
13618 mMediumAttachments.backup();
13619
13620 // The backup operation makes the pAttach reference point to the
13621 // old settings. Re-get the correct reference.
13622 pAttach = i_findAttachment(*mMediumAttachments.data(),
13623 ctrlName,
13624 lPort,
13625 lDevice);
13626
13627 {
13628 AutoCaller autoAttachCaller(this);
13629 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13630
13631 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13632 if (!oldmedium.isNull())
13633 oldmedium->i_removeBackReference(mData->mUuid);
13634
13635 pAttach->i_updateMedium(NULL);
13636 pAttach->i_updateEjected();
13637 }
13638
13639 i_setModified(IsModified_Storage);
13640 }
13641 else
13642 {
13643 {
13644 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13645 pAttach->i_updateEjected();
13646 }
13647 }
13648
13649 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13650
13651 return S_OK;
13652}
13653
13654HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13655 com::Utf8Str &aResult)
13656{
13657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13658
13659 HRESULT hr = S_OK;
13660
13661 if (!mAuthLibCtx.hAuthLibrary)
13662 {
13663 /* Load the external authentication library. */
13664 Bstr authLibrary;
13665 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13666
13667 Utf8Str filename = authLibrary;
13668
13669 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13670 if (RT_FAILURE(vrc))
13671 hr = setErrorBoth(E_FAIL, vrc,
13672 tr("Could not load the external authentication library '%s' (%Rrc)"),
13673 filename.c_str(), vrc);
13674 }
13675
13676 /* The auth library might need the machine lock. */
13677 alock.release();
13678
13679 if (FAILED(hr))
13680 return hr;
13681
13682 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13683 {
13684 enum VRDEAuthParams
13685 {
13686 parmUuid = 1,
13687 parmGuestJudgement,
13688 parmUser,
13689 parmPassword,
13690 parmDomain,
13691 parmClientId
13692 };
13693
13694 AuthResult result = AuthResultAccessDenied;
13695
13696 Guid uuid(aAuthParams[parmUuid]);
13697 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13698 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13699
13700 result = AuthLibAuthenticate(&mAuthLibCtx,
13701 uuid.raw(), guestJudgement,
13702 aAuthParams[parmUser].c_str(),
13703 aAuthParams[parmPassword].c_str(),
13704 aAuthParams[parmDomain].c_str(),
13705 u32ClientId);
13706
13707 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13708 size_t cbPassword = aAuthParams[parmPassword].length();
13709 if (cbPassword)
13710 {
13711 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13712 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13713 }
13714
13715 if (result == AuthResultAccessGranted)
13716 aResult = "granted";
13717 else
13718 aResult = "denied";
13719
13720 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13721 aAuthParams[parmUser].c_str(), aResult.c_str()));
13722 }
13723 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13724 {
13725 enum VRDEAuthDisconnectParams
13726 {
13727 parmUuid = 1,
13728 parmClientId
13729 };
13730
13731 Guid uuid(aAuthParams[parmUuid]);
13732 uint32_t u32ClientId = 0;
13733 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13734 }
13735 else
13736 {
13737 hr = E_INVALIDARG;
13738 }
13739
13740 return hr;
13741}
13742
13743// public methods only for internal purposes
13744/////////////////////////////////////////////////////////////////////////////
13745
13746#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13747/**
13748 * Called from the client watcher thread to check for expected or unexpected
13749 * death of the client process that has a direct session to this machine.
13750 *
13751 * On Win32 and on OS/2, this method is called only when we've got the
13752 * mutex (i.e. the client has either died or terminated normally) so it always
13753 * returns @c true (the client is terminated, the session machine is
13754 * uninitialized).
13755 *
13756 * On other platforms, the method returns @c true if the client process has
13757 * terminated normally or abnormally and the session machine was uninitialized,
13758 * and @c false if the client process is still alive.
13759 *
13760 * @note Locks this object for writing.
13761 */
13762bool SessionMachine::i_checkForDeath()
13763{
13764 Uninit::Reason reason;
13765 bool terminated = false;
13766
13767 /* Enclose autoCaller with a block because calling uninit() from under it
13768 * will deadlock. */
13769 {
13770 AutoCaller autoCaller(this);
13771 if (!autoCaller.isOk())
13772 {
13773 /* return true if not ready, to cause the client watcher to exclude
13774 * the corresponding session from watching */
13775 LogFlowThisFunc(("Already uninitialized!\n"));
13776 return true;
13777 }
13778
13779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13780
13781 /* Determine the reason of death: if the session state is Closing here,
13782 * everything is fine. Otherwise it means that the client did not call
13783 * OnSessionEnd() before it released the IPC semaphore. This may happen
13784 * either because the client process has abnormally terminated, or
13785 * because it simply forgot to call ISession::Close() before exiting. We
13786 * threat the latter also as an abnormal termination (see
13787 * Session::uninit() for details). */
13788 reason = mData->mSession.mState == SessionState_Unlocking ?
13789 Uninit::Normal :
13790 Uninit::Abnormal;
13791
13792 if (mClientToken)
13793 terminated = mClientToken->release();
13794 } /* AutoCaller block */
13795
13796 if (terminated)
13797 uninit(reason);
13798
13799 return terminated;
13800}
13801
13802void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13803{
13804 LogFlowThisFunc(("\n"));
13805
13806 strTokenId.setNull();
13807
13808 AutoCaller autoCaller(this);
13809 AssertComRCReturnVoid(autoCaller.rc());
13810
13811 Assert(mClientToken);
13812 if (mClientToken)
13813 mClientToken->getId(strTokenId);
13814}
13815#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13816IToken *SessionMachine::i_getToken()
13817{
13818 LogFlowThisFunc(("\n"));
13819
13820 AutoCaller autoCaller(this);
13821 AssertComRCReturn(autoCaller.rc(), NULL);
13822
13823 Assert(mClientToken);
13824 if (mClientToken)
13825 return mClientToken->getToken();
13826 else
13827 return NULL;
13828}
13829#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13830
13831Machine::ClientToken *SessionMachine::i_getClientToken()
13832{
13833 LogFlowThisFunc(("\n"));
13834
13835 AutoCaller autoCaller(this);
13836 AssertComRCReturn(autoCaller.rc(), NULL);
13837
13838 return mClientToken;
13839}
13840
13841
13842/**
13843 * @note Locks this object for reading.
13844 */
13845HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13846{
13847 LogFlowThisFunc(("\n"));
13848
13849 AutoCaller autoCaller(this);
13850 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13851
13852 ComPtr<IInternalSessionControl> directControl;
13853 {
13854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13855 if (mData->mSession.mLockType == LockType_VM)
13856 directControl = mData->mSession.mDirectControl;
13857 }
13858
13859 /* ignore notifications sent after #OnSessionEnd() is called */
13860 if (!directControl)
13861 return S_OK;
13862
13863 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13864}
13865
13866/**
13867 * @note Locks this object for reading.
13868 */
13869HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13870 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13871 const Utf8Str &aGuestIp, LONG aGuestPort)
13872{
13873 LogFlowThisFunc(("\n"));
13874
13875 AutoCaller autoCaller(this);
13876 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13877
13878 ComPtr<IInternalSessionControl> directControl;
13879 {
13880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13881 if (mData->mSession.mLockType == LockType_VM)
13882 directControl = mData->mSession.mDirectControl;
13883 }
13884
13885 /* ignore notifications sent after #OnSessionEnd() is called */
13886 if (!directControl)
13887 return S_OK;
13888 /*
13889 * instead acting like callback we ask IVirtualBox deliver corresponding event
13890 */
13891
13892 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13893 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13894 return S_OK;
13895}
13896
13897/**
13898 * @note Locks this object for reading.
13899 */
13900HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13901{
13902 LogFlowThisFunc(("\n"));
13903
13904 AutoCaller autoCaller(this);
13905 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13906
13907 ComPtr<IInternalSessionControl> directControl;
13908 {
13909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13910 if (mData->mSession.mLockType == LockType_VM)
13911 directControl = mData->mSession.mDirectControl;
13912 }
13913
13914 /* ignore notifications sent after #OnSessionEnd() is called */
13915 if (!directControl)
13916 return S_OK;
13917
13918 return directControl->OnAudioAdapterChange(audioAdapter);
13919}
13920
13921/**
13922 * @note Locks this object for reading.
13923 */
13924HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13925{
13926 LogFlowThisFunc(("\n"));
13927
13928 AutoCaller autoCaller(this);
13929 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13930
13931 ComPtr<IInternalSessionControl> directControl;
13932 {
13933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13934 if (mData->mSession.mLockType == LockType_VM)
13935 directControl = mData->mSession.mDirectControl;
13936 }
13937
13938 /* ignore notifications sent after #OnSessionEnd() is called */
13939 if (!directControl)
13940 return S_OK;
13941
13942 return directControl->OnSerialPortChange(serialPort);
13943}
13944
13945/**
13946 * @note Locks this object for reading.
13947 */
13948HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952 AutoCaller autoCaller(this);
13953 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13954
13955 ComPtr<IInternalSessionControl> directControl;
13956 {
13957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13958 if (mData->mSession.mLockType == LockType_VM)
13959 directControl = mData->mSession.mDirectControl;
13960 }
13961
13962 /* ignore notifications sent after #OnSessionEnd() is called */
13963 if (!directControl)
13964 return S_OK;
13965
13966 return directControl->OnParallelPortChange(parallelPort);
13967}
13968
13969/**
13970 * @note Locks this object for reading.
13971 */
13972HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13973{
13974 LogFlowThisFunc(("\n"));
13975
13976 AutoCaller autoCaller(this);
13977 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13978
13979 ComPtr<IInternalSessionControl> directControl;
13980 {
13981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13982 if (mData->mSession.mLockType == LockType_VM)
13983 directControl = mData->mSession.mDirectControl;
13984 }
13985
13986 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
13987
13988 /* ignore notifications sent after #OnSessionEnd() is called */
13989 if (!directControl)
13990 return S_OK;
13991
13992 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
13993}
13994
13995/**
13996 * @note Locks this object for reading.
13997 */
13998HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14004
14005 ComPtr<IInternalSessionControl> directControl;
14006 {
14007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14008 if (mData->mSession.mLockType == LockType_VM)
14009 directControl = mData->mSession.mDirectControl;
14010 }
14011
14012 mParent->i_onMediumChanged(aAttachment);
14013
14014 /* ignore notifications sent after #OnSessionEnd() is called */
14015 if (!directControl)
14016 return S_OK;
14017
14018 return directControl->OnMediumChange(aAttachment, aForce);
14019}
14020
14021HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14022{
14023 LogFlowThisFunc(("\n"));
14024
14025 AutoCaller autoCaller(this);
14026 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14027
14028 ComPtr<IInternalSessionControl> directControl;
14029 {
14030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14031 if (mData->mSession.mLockType == LockType_VM)
14032 directControl = mData->mSession.mDirectControl;
14033 }
14034
14035 /* ignore notifications sent after #OnSessionEnd() is called */
14036 if (!directControl)
14037 return S_OK;
14038
14039 return directControl->OnVMProcessPriorityChange(aPriority);
14040}
14041
14042/**
14043 * @note Locks this object for reading.
14044 */
14045HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14046{
14047 LogFlowThisFunc(("\n"));
14048
14049 AutoCaller autoCaller(this);
14050 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14051
14052 ComPtr<IInternalSessionControl> directControl;
14053 {
14054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14055 if (mData->mSession.mLockType == LockType_VM)
14056 directControl = mData->mSession.mDirectControl;
14057 }
14058
14059 /* ignore notifications sent after #OnSessionEnd() is called */
14060 if (!directControl)
14061 return S_OK;
14062
14063 return directControl->OnCPUChange(aCPU, aRemove);
14064}
14065
14066HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14067{
14068 LogFlowThisFunc(("\n"));
14069
14070 AutoCaller autoCaller(this);
14071 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14072
14073 ComPtr<IInternalSessionControl> directControl;
14074 {
14075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14076 if (mData->mSession.mLockType == LockType_VM)
14077 directControl = mData->mSession.mDirectControl;
14078 }
14079
14080 /* ignore notifications sent after #OnSessionEnd() is called */
14081 if (!directControl)
14082 return S_OK;
14083
14084 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14085}
14086
14087/**
14088 * @note Locks this object for reading.
14089 */
14090HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14091{
14092 LogFlowThisFunc(("\n"));
14093
14094 AutoCaller autoCaller(this);
14095 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14096
14097 ComPtr<IInternalSessionControl> directControl;
14098 {
14099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14100 if (mData->mSession.mLockType == LockType_VM)
14101 directControl = mData->mSession.mDirectControl;
14102 }
14103
14104 /* ignore notifications sent after #OnSessionEnd() is called */
14105 if (!directControl)
14106 return S_OK;
14107
14108 return directControl->OnVRDEServerChange(aRestart);
14109}
14110
14111/**
14112 * @note Locks this object for reading.
14113 */
14114HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14120
14121 ComPtr<IInternalSessionControl> directControl;
14122 {
14123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14124 if (mData->mSession.mLockType == LockType_VM)
14125 directControl = mData->mSession.mDirectControl;
14126 }
14127
14128 /* ignore notifications sent after #OnSessionEnd() is called */
14129 if (!directControl)
14130 return S_OK;
14131
14132 return directControl->OnRecordingChange(aEnable);
14133}
14134
14135/**
14136 * @note Locks this object for reading.
14137 */
14138HRESULT SessionMachine::i_onUSBControllerChange()
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14144
14145 ComPtr<IInternalSessionControl> directControl;
14146 {
14147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14148 if (mData->mSession.mLockType == LockType_VM)
14149 directControl = mData->mSession.mDirectControl;
14150 }
14151
14152 /* ignore notifications sent after #OnSessionEnd() is called */
14153 if (!directControl)
14154 return S_OK;
14155
14156 return directControl->OnUSBControllerChange();
14157}
14158
14159/**
14160 * @note Locks this object for reading.
14161 */
14162HRESULT SessionMachine::i_onSharedFolderChange()
14163{
14164 LogFlowThisFunc(("\n"));
14165
14166 AutoCaller autoCaller(this);
14167 AssertComRCReturnRC(autoCaller.rc());
14168
14169 ComPtr<IInternalSessionControl> directControl;
14170 {
14171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14172 if (mData->mSession.mLockType == LockType_VM)
14173 directControl = mData->mSession.mDirectControl;
14174 }
14175
14176 /* ignore notifications sent after #OnSessionEnd() is called */
14177 if (!directControl)
14178 return S_OK;
14179
14180 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14181}
14182
14183/**
14184 * @note Locks this object for reading.
14185 */
14186HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14187{
14188 LogFlowThisFunc(("\n"));
14189
14190 AutoCaller autoCaller(this);
14191 AssertComRCReturnRC(autoCaller.rc());
14192
14193 ComPtr<IInternalSessionControl> directControl;
14194 {
14195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14196 if (mData->mSession.mLockType == LockType_VM)
14197 directControl = mData->mSession.mDirectControl;
14198 }
14199
14200 /* ignore notifications sent after #OnSessionEnd() is called */
14201 if (!directControl)
14202 return S_OK;
14203
14204 return directControl->OnClipboardModeChange(aClipboardMode);
14205}
14206
14207/**
14208 * @note Locks this object for reading.
14209 */
14210HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14211{
14212 LogFlowThisFunc(("\n"));
14213
14214 AutoCaller autoCaller(this);
14215 AssertComRCReturnRC(autoCaller.rc());
14216
14217 ComPtr<IInternalSessionControl> directControl;
14218 {
14219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14220 if (mData->mSession.mLockType == LockType_VM)
14221 directControl = mData->mSession.mDirectControl;
14222 }
14223
14224 /* ignore notifications sent after #OnSessionEnd() is called */
14225 if (!directControl)
14226 return S_OK;
14227
14228 return directControl->OnClipboardFileTransferModeChange(aEnable);
14229}
14230
14231/**
14232 * @note Locks this object for reading.
14233 */
14234HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14235{
14236 LogFlowThisFunc(("\n"));
14237
14238 AutoCaller autoCaller(this);
14239 AssertComRCReturnRC(autoCaller.rc());
14240
14241 ComPtr<IInternalSessionControl> directControl;
14242 {
14243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14244 if (mData->mSession.mLockType == LockType_VM)
14245 directControl = mData->mSession.mDirectControl;
14246 }
14247
14248 /* ignore notifications sent after #OnSessionEnd() is called */
14249 if (!directControl)
14250 return S_OK;
14251
14252 return directControl->OnDnDModeChange(aDnDMode);
14253}
14254
14255/**
14256 * @note Locks this object for reading.
14257 */
14258HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14259{
14260 LogFlowThisFunc(("\n"));
14261
14262 AutoCaller autoCaller(this);
14263 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14264
14265 ComPtr<IInternalSessionControl> directControl;
14266 {
14267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14268 if (mData->mSession.mLockType == LockType_VM)
14269 directControl = mData->mSession.mDirectControl;
14270 }
14271
14272 /* ignore notifications sent after #OnSessionEnd() is called */
14273 if (!directControl)
14274 return S_OK;
14275
14276 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14277}
14278
14279/**
14280 * @note Locks this object for reading.
14281 */
14282HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14283{
14284 LogFlowThisFunc(("\n"));
14285
14286 AutoCaller autoCaller(this);
14287 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14288
14289 ComPtr<IInternalSessionControl> directControl;
14290 {
14291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14292 if (mData->mSession.mLockType == LockType_VM)
14293 directControl = mData->mSession.mDirectControl;
14294 }
14295
14296 /* ignore notifications sent after #OnSessionEnd() is called */
14297 if (!directControl)
14298 return S_OK;
14299
14300 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14301}
14302
14303/**
14304 * Returns @c true if this machine's USB controller reports it has a matching
14305 * filter for the given USB device and @c false otherwise.
14306 *
14307 * @note locks this object for reading.
14308 */
14309bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14310{
14311 AutoCaller autoCaller(this);
14312 /* silently return if not ready -- this method may be called after the
14313 * direct machine session has been called */
14314 if (!autoCaller.isOk())
14315 return false;
14316
14317#ifdef VBOX_WITH_USB
14318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14319
14320 switch (mData->mMachineState)
14321 {
14322 case MachineState_Starting:
14323 case MachineState_Restoring:
14324 case MachineState_TeleportingIn:
14325 case MachineState_Paused:
14326 case MachineState_Running:
14327 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14328 * elsewhere... */
14329 alock.release();
14330 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14331 default: break;
14332 }
14333#else
14334 NOREF(aDevice);
14335 NOREF(aMaskedIfs);
14336#endif
14337 return false;
14338}
14339
14340/**
14341 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14342 */
14343HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14344 IVirtualBoxErrorInfo *aError,
14345 ULONG aMaskedIfs,
14346 const com::Utf8Str &aCaptureFilename)
14347{
14348 LogFlowThisFunc(("\n"));
14349
14350 AutoCaller autoCaller(this);
14351
14352 /* This notification may happen after the machine object has been
14353 * uninitialized (the session was closed), so don't assert. */
14354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14355
14356 ComPtr<IInternalSessionControl> directControl;
14357 {
14358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14359 if (mData->mSession.mLockType == LockType_VM)
14360 directControl = mData->mSession.mDirectControl;
14361 }
14362
14363 /* fail on notifications sent after #OnSessionEnd() is called, it is
14364 * expected by the caller */
14365 if (!directControl)
14366 return E_FAIL;
14367
14368 /* No locks should be held at this point. */
14369 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14370 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14371
14372 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14373}
14374
14375/**
14376 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14377 */
14378HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14379 IVirtualBoxErrorInfo *aError)
14380{
14381 LogFlowThisFunc(("\n"));
14382
14383 AutoCaller autoCaller(this);
14384
14385 /* This notification may happen after the machine object has been
14386 * uninitialized (the session was closed), so don't assert. */
14387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14388
14389 ComPtr<IInternalSessionControl> directControl;
14390 {
14391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14392 if (mData->mSession.mLockType == LockType_VM)
14393 directControl = mData->mSession.mDirectControl;
14394 }
14395
14396 /* fail on notifications sent after #OnSessionEnd() is called, it is
14397 * expected by the caller */
14398 if (!directControl)
14399 return E_FAIL;
14400
14401 /* No locks should be held at this point. */
14402 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14403 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14404
14405 return directControl->OnUSBDeviceDetach(aId, aError);
14406}
14407
14408// protected methods
14409/////////////////////////////////////////////////////////////////////////////
14410
14411/**
14412 * Deletes the given file if it is no longer in use by either the current machine state
14413 * (if the machine is "saved") or any of the machine's snapshots.
14414 *
14415 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14416 * but is different for each SnapshotMachine. When calling this, the order of calling this
14417 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14418 * is therefore critical. I know, it's all rather messy.
14419 *
14420 * @param strStateFile
14421 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14422 * the test for whether the saved state file is in use.
14423 */
14424void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14425 Snapshot *pSnapshotToIgnore)
14426{
14427 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14428 if ( (strStateFile.isNotEmpty())
14429 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14430 )
14431 // ... and it must also not be shared with other snapshots
14432 if ( !mData->mFirstSnapshot
14433 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14434 // this checks the SnapshotMachine's state file paths
14435 )
14436 RTFileDelete(strStateFile.c_str());
14437}
14438
14439/**
14440 * Locks the attached media.
14441 *
14442 * All attached hard disks are locked for writing and DVD/floppy are locked for
14443 * reading. Parents of attached hard disks (if any) are locked for reading.
14444 *
14445 * This method also performs accessibility check of all media it locks: if some
14446 * media is inaccessible, the method will return a failure and a bunch of
14447 * extended error info objects per each inaccessible medium.
14448 *
14449 * Note that this method is atomic: if it returns a success, all media are
14450 * locked as described above; on failure no media is locked at all (all
14451 * succeeded individual locks will be undone).
14452 *
14453 * The caller is responsible for doing the necessary state sanity checks.
14454 *
14455 * The locks made by this method must be undone by calling #unlockMedia() when
14456 * no more needed.
14457 */
14458HRESULT SessionMachine::i_lockMedia()
14459{
14460 AutoCaller autoCaller(this);
14461 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14462
14463 AutoMultiWriteLock2 alock(this->lockHandle(),
14464 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14465
14466 /* bail out if trying to lock things with already set up locking */
14467 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14468
14469 MultiResult mrc(S_OK);
14470
14471 /* Collect locking information for all medium objects attached to the VM. */
14472 for (MediumAttachmentList::const_iterator
14473 it = mMediumAttachments->begin();
14474 it != mMediumAttachments->end();
14475 ++it)
14476 {
14477 MediumAttachment *pAtt = *it;
14478 DeviceType_T devType = pAtt->i_getType();
14479 Medium *pMedium = pAtt->i_getMedium();
14480
14481 MediumLockList *pMediumLockList(new MediumLockList());
14482 // There can be attachments without a medium (floppy/dvd), and thus
14483 // it's impossible to create a medium lock list. It still makes sense
14484 // to have the empty medium lock list in the map in case a medium is
14485 // attached later.
14486 if (pMedium != NULL)
14487 {
14488 MediumType_T mediumType = pMedium->i_getType();
14489 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14490 || mediumType == MediumType_Shareable;
14491 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14492
14493 alock.release();
14494 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14495 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14496 false /* fMediumLockWriteAll */,
14497 NULL,
14498 *pMediumLockList);
14499 alock.acquire();
14500 if (FAILED(mrc))
14501 {
14502 delete pMediumLockList;
14503 mData->mSession.mLockedMedia.Clear();
14504 break;
14505 }
14506 }
14507
14508 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14509 if (FAILED(rc))
14510 {
14511 mData->mSession.mLockedMedia.Clear();
14512 mrc = setError(rc,
14513 tr("Collecting locking information for all attached media failed"));
14514 break;
14515 }
14516 }
14517
14518 if (SUCCEEDED(mrc))
14519 {
14520 /* Now lock all media. If this fails, nothing is locked. */
14521 alock.release();
14522 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14523 alock.acquire();
14524 if (FAILED(rc))
14525 {
14526 mrc = setError(rc,
14527 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14528 }
14529 }
14530
14531 return mrc;
14532}
14533
14534/**
14535 * Undoes the locks made by by #lockMedia().
14536 */
14537HRESULT SessionMachine::i_unlockMedia()
14538{
14539 AutoCaller autoCaller(this);
14540 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14541
14542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14543
14544 /* we may be holding important error info on the current thread;
14545 * preserve it */
14546 ErrorInfoKeeper eik;
14547
14548 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14549 AssertComRC(rc);
14550 return rc;
14551}
14552
14553/**
14554 * Helper to change the machine state (reimplementation).
14555 *
14556 * @note Locks this object for writing.
14557 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14558 * it can cause crashes in random places due to unexpectedly committing
14559 * the current settings. The caller is responsible for that. The call
14560 * to saveStateSettings is fine, because this method does not commit.
14561 */
14562HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14563{
14564 LogFlowThisFuncEnter();
14565 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14566
14567 AutoCaller autoCaller(this);
14568 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14569
14570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14571
14572 MachineState_T oldMachineState = mData->mMachineState;
14573
14574 AssertMsgReturn(oldMachineState != aMachineState,
14575 ("oldMachineState=%s, aMachineState=%s\n",
14576 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14577 E_FAIL);
14578
14579 HRESULT rc = S_OK;
14580
14581 int stsFlags = 0;
14582 bool deleteSavedState = false;
14583
14584 /* detect some state transitions */
14585
14586 if ( ( oldMachineState == MachineState_Saved
14587 && aMachineState == MachineState_Restoring)
14588 || ( ( oldMachineState == MachineState_PoweredOff
14589 || oldMachineState == MachineState_Teleported
14590 || oldMachineState == MachineState_Aborted
14591 )
14592 && ( aMachineState == MachineState_TeleportingIn
14593 || aMachineState == MachineState_Starting
14594 )
14595 )
14596 )
14597 {
14598 /* The EMT thread is about to start */
14599
14600 /* Nothing to do here for now... */
14601
14602 /// @todo NEWMEDIA don't let mDVDDrive and other children
14603 /// change anything when in the Starting/Restoring state
14604 }
14605 else if ( ( oldMachineState == MachineState_Running
14606 || oldMachineState == MachineState_Paused
14607 || oldMachineState == MachineState_Teleporting
14608 || oldMachineState == MachineState_OnlineSnapshotting
14609 || oldMachineState == MachineState_LiveSnapshotting
14610 || oldMachineState == MachineState_Stuck
14611 || oldMachineState == MachineState_Starting
14612 || oldMachineState == MachineState_Stopping
14613 || oldMachineState == MachineState_Saving
14614 || oldMachineState == MachineState_Restoring
14615 || oldMachineState == MachineState_TeleportingPausedVM
14616 || oldMachineState == MachineState_TeleportingIn
14617 )
14618 && ( aMachineState == MachineState_PoweredOff
14619 || aMachineState == MachineState_Saved
14620 || aMachineState == MachineState_Teleported
14621 || aMachineState == MachineState_Aborted
14622 )
14623 )
14624 {
14625 /* The EMT thread has just stopped, unlock attached media. Note that as
14626 * opposed to locking that is done from Console, we do unlocking here
14627 * because the VM process may have aborted before having a chance to
14628 * properly unlock all media it locked. */
14629
14630 unlockMedia();
14631 }
14632
14633 if (oldMachineState == MachineState_Restoring)
14634 {
14635 if (aMachineState != MachineState_Saved)
14636 {
14637 /*
14638 * delete the saved state file once the machine has finished
14639 * restoring from it (note that Console sets the state from
14640 * Restoring to Saved if the VM couldn't restore successfully,
14641 * to give the user an ability to fix an error and retry --
14642 * we keep the saved state file in this case)
14643 */
14644 deleteSavedState = true;
14645 }
14646 }
14647 else if ( oldMachineState == MachineState_Saved
14648 && ( aMachineState == MachineState_PoweredOff
14649 || aMachineState == MachineState_Aborted
14650 || aMachineState == MachineState_Teleported
14651 )
14652 )
14653 {
14654 /*
14655 * delete the saved state after SessionMachine::ForgetSavedState() is called
14656 * or if the VM process (owning a direct VM session) crashed while the
14657 * VM was Saved
14658 */
14659
14660 /// @todo (dmik)
14661 // Not sure that deleting the saved state file just because of the
14662 // client death before it attempted to restore the VM is a good
14663 // thing. But when it crashes we need to go to the Aborted state
14664 // which cannot have the saved state file associated... The only
14665 // way to fix this is to make the Aborted condition not a VM state
14666 // but a bool flag: i.e., when a crash occurs, set it to true and
14667 // change the state to PoweredOff or Saved depending on the
14668 // saved state presence.
14669
14670 deleteSavedState = true;
14671 mData->mCurrentStateModified = TRUE;
14672 stsFlags |= SaveSTS_CurStateModified;
14673 }
14674
14675 if ( aMachineState == MachineState_Starting
14676 || aMachineState == MachineState_Restoring
14677 || aMachineState == MachineState_TeleportingIn
14678 )
14679 {
14680 /* set the current state modified flag to indicate that the current
14681 * state is no more identical to the state in the
14682 * current snapshot */
14683 if (!mData->mCurrentSnapshot.isNull())
14684 {
14685 mData->mCurrentStateModified = TRUE;
14686 stsFlags |= SaveSTS_CurStateModified;
14687 }
14688 }
14689
14690 if (deleteSavedState)
14691 {
14692 if (mRemoveSavedState)
14693 {
14694 Assert(!mSSData->strStateFilePath.isEmpty());
14695
14696 // it is safe to delete the saved state file if ...
14697 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14698 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14699 // ... none of the snapshots share the saved state file
14700 )
14701 RTFileDelete(mSSData->strStateFilePath.c_str());
14702 }
14703
14704 mSSData->strStateFilePath.setNull();
14705 stsFlags |= SaveSTS_StateFilePath;
14706 }
14707
14708 /* redirect to the underlying peer machine */
14709 mPeer->i_setMachineState(aMachineState);
14710
14711 if ( oldMachineState != MachineState_RestoringSnapshot
14712 && ( aMachineState == MachineState_PoweredOff
14713 || aMachineState == MachineState_Teleported
14714 || aMachineState == MachineState_Aborted
14715 || aMachineState == MachineState_Saved))
14716 {
14717 /* the machine has stopped execution
14718 * (or the saved state file was adopted) */
14719 stsFlags |= SaveSTS_StateTimeStamp;
14720 }
14721
14722 if ( ( oldMachineState == MachineState_PoweredOff
14723 || oldMachineState == MachineState_Aborted
14724 || oldMachineState == MachineState_Teleported
14725 )
14726 && aMachineState == MachineState_Saved)
14727 {
14728 /* the saved state file was adopted */
14729 Assert(!mSSData->strStateFilePath.isEmpty());
14730 stsFlags |= SaveSTS_StateFilePath;
14731 }
14732
14733#ifdef VBOX_WITH_GUEST_PROPS
14734 if ( aMachineState == MachineState_PoweredOff
14735 || aMachineState == MachineState_Aborted
14736 || aMachineState == MachineState_Teleported)
14737 {
14738 /* Make sure any transient guest properties get removed from the
14739 * property store on shutdown. */
14740 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14741
14742 /* remove it from the settings representation */
14743 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14744 for (settings::GuestPropertiesList::iterator
14745 it = llGuestProperties.begin();
14746 it != llGuestProperties.end();
14747 /*nothing*/)
14748 {
14749 const settings::GuestProperty &prop = *it;
14750 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14751 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14752 {
14753 it = llGuestProperties.erase(it);
14754 fNeedsSaving = true;
14755 }
14756 else
14757 {
14758 ++it;
14759 }
14760 }
14761
14762 /* Additionally remove it from the HWData representation. Required to
14763 * keep everything in sync, as this is what the API keeps using. */
14764 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14765 for (HWData::GuestPropertyMap::iterator
14766 it = llHWGuestProperties.begin();
14767 it != llHWGuestProperties.end();
14768 /*nothing*/)
14769 {
14770 uint32_t fFlags = it->second.mFlags;
14771 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14772 {
14773 /* iterator where we need to continue after the erase call
14774 * (C++03 is a fact still, and it doesn't return the iterator
14775 * which would allow continuing) */
14776 HWData::GuestPropertyMap::iterator it2 = it;
14777 ++it2;
14778 llHWGuestProperties.erase(it);
14779 it = it2;
14780 fNeedsSaving = true;
14781 }
14782 else
14783 {
14784 ++it;
14785 }
14786 }
14787
14788 if (fNeedsSaving)
14789 {
14790 mData->mCurrentStateModified = TRUE;
14791 stsFlags |= SaveSTS_CurStateModified;
14792 }
14793 }
14794#endif /* VBOX_WITH_GUEST_PROPS */
14795
14796 rc = i_saveStateSettings(stsFlags);
14797
14798 if ( ( oldMachineState != MachineState_PoweredOff
14799 && oldMachineState != MachineState_Aborted
14800 && oldMachineState != MachineState_Teleported
14801 )
14802 && ( aMachineState == MachineState_PoweredOff
14803 || aMachineState == MachineState_Aborted
14804 || aMachineState == MachineState_Teleported
14805 )
14806 )
14807 {
14808 /* we've been shut down for any reason */
14809 /* no special action so far */
14810 }
14811
14812 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14813 LogFlowThisFuncLeave();
14814 return rc;
14815}
14816
14817/**
14818 * Sends the current machine state value to the VM process.
14819 *
14820 * @note Locks this object for reading, then calls a client process.
14821 */
14822HRESULT SessionMachine::i_updateMachineStateOnClient()
14823{
14824 AutoCaller autoCaller(this);
14825 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14826
14827 ComPtr<IInternalSessionControl> directControl;
14828 {
14829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14830 AssertReturn(!!mData, E_FAIL);
14831 if (mData->mSession.mLockType == LockType_VM)
14832 directControl = mData->mSession.mDirectControl;
14833
14834 /* directControl may be already set to NULL here in #OnSessionEnd()
14835 * called too early by the direct session process while there is still
14836 * some operation (like deleting the snapshot) in progress. The client
14837 * process in this case is waiting inside Session::close() for the
14838 * "end session" process object to complete, while #uninit() called by
14839 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14840 * operation to complete. For now, we accept this inconsistent behavior
14841 * and simply do nothing here. */
14842
14843 if (mData->mSession.mState == SessionState_Unlocking)
14844 return S_OK;
14845 }
14846
14847 /* ignore notifications sent after #OnSessionEnd() is called */
14848 if (!directControl)
14849 return S_OK;
14850
14851 return directControl->UpdateMachineState(mData->mMachineState);
14852}
14853
14854
14855/*static*/
14856HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14857{
14858 va_list args;
14859 va_start(args, pcszMsg);
14860 HRESULT rc = setErrorInternal(aResultCode,
14861 getStaticClassIID(),
14862 getStaticComponentName(),
14863 Utf8Str(pcszMsg, args),
14864 false /* aWarning */,
14865 true /* aLogIt */);
14866 va_end(args);
14867 return rc;
14868}
14869
14870
14871HRESULT Machine::updateState(MachineState_T aState)
14872{
14873 NOREF(aState);
14874 ReturnComNotImplemented();
14875}
14876
14877HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14878{
14879 NOREF(aProgress);
14880 ReturnComNotImplemented();
14881}
14882
14883HRESULT Machine::endPowerUp(LONG aResult)
14884{
14885 NOREF(aResult);
14886 ReturnComNotImplemented();
14887}
14888
14889HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14890{
14891 NOREF(aProgress);
14892 ReturnComNotImplemented();
14893}
14894
14895HRESULT Machine::endPoweringDown(LONG aResult,
14896 const com::Utf8Str &aErrMsg)
14897{
14898 NOREF(aResult);
14899 NOREF(aErrMsg);
14900 ReturnComNotImplemented();
14901}
14902
14903HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14904 BOOL *aMatched,
14905 ULONG *aMaskedInterfaces)
14906{
14907 NOREF(aDevice);
14908 NOREF(aMatched);
14909 NOREF(aMaskedInterfaces);
14910 ReturnComNotImplemented();
14911
14912}
14913
14914HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14915{
14916 NOREF(aId); NOREF(aCaptureFilename);
14917 ReturnComNotImplemented();
14918}
14919
14920HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14921 BOOL aDone)
14922{
14923 NOREF(aId);
14924 NOREF(aDone);
14925 ReturnComNotImplemented();
14926}
14927
14928HRESULT Machine::autoCaptureUSBDevices()
14929{
14930 ReturnComNotImplemented();
14931}
14932
14933HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14934{
14935 NOREF(aDone);
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14940 ComPtr<IProgress> &aProgress)
14941{
14942 NOREF(aSession);
14943 NOREF(aProgress);
14944 ReturnComNotImplemented();
14945}
14946
14947HRESULT Machine::finishOnlineMergeMedium()
14948{
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14953{
14954 RT_NOREF(aParms, aID);
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14959{
14960 RT_NOREF(aID);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::clipboardAreaAttach(ULONG aID)
14965{
14966 RT_NOREF(aID);
14967 ReturnComNotImplemented();
14968}
14969HRESULT Machine::clipboardAreaDetach(ULONG aID)
14970{
14971 RT_NOREF(aID);
14972 ReturnComNotImplemented();
14973}
14974
14975HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
14976{
14977 RT_NOREF(aID);
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
14982{
14983 RT_NOREF(aID, aRefCount);
14984 ReturnComNotImplemented();
14985}
14986
14987HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14988 std::vector<com::Utf8Str> &aValues,
14989 std::vector<LONG64> &aTimestamps,
14990 std::vector<com::Utf8Str> &aFlags)
14991{
14992 NOREF(aNames);
14993 NOREF(aValues);
14994 NOREF(aTimestamps);
14995 NOREF(aFlags);
14996 ReturnComNotImplemented();
14997}
14998
14999HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15000 const com::Utf8Str &aValue,
15001 LONG64 aTimestamp,
15002 const com::Utf8Str &aFlags)
15003{
15004 NOREF(aName);
15005 NOREF(aValue);
15006 NOREF(aTimestamp);
15007 NOREF(aFlags);
15008 ReturnComNotImplemented();
15009}
15010
15011HRESULT Machine::lockMedia()
15012{
15013 ReturnComNotImplemented();
15014}
15015
15016HRESULT Machine::unlockMedia()
15017{
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15022 ComPtr<IMediumAttachment> &aNewAttachment)
15023{
15024 NOREF(aAttachment);
15025 NOREF(aNewAttachment);
15026 ReturnComNotImplemented();
15027}
15028
15029HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15030 ULONG aCpuUser,
15031 ULONG aCpuKernel,
15032 ULONG aCpuIdle,
15033 ULONG aMemTotal,
15034 ULONG aMemFree,
15035 ULONG aMemBalloon,
15036 ULONG aMemShared,
15037 ULONG aMemCache,
15038 ULONG aPagedTotal,
15039 ULONG aMemAllocTotal,
15040 ULONG aMemFreeTotal,
15041 ULONG aMemBalloonTotal,
15042 ULONG aMemSharedTotal,
15043 ULONG aVmNetRx,
15044 ULONG aVmNetTx)
15045{
15046 NOREF(aValidStats);
15047 NOREF(aCpuUser);
15048 NOREF(aCpuKernel);
15049 NOREF(aCpuIdle);
15050 NOREF(aMemTotal);
15051 NOREF(aMemFree);
15052 NOREF(aMemBalloon);
15053 NOREF(aMemShared);
15054 NOREF(aMemCache);
15055 NOREF(aPagedTotal);
15056 NOREF(aMemAllocTotal);
15057 NOREF(aMemFreeTotal);
15058 NOREF(aMemBalloonTotal);
15059 NOREF(aMemSharedTotal);
15060 NOREF(aVmNetRx);
15061 NOREF(aVmNetTx);
15062 ReturnComNotImplemented();
15063}
15064
15065HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15066 com::Utf8Str &aResult)
15067{
15068 NOREF(aAuthParams);
15069 NOREF(aResult);
15070 ReturnComNotImplemented();
15071}
15072
15073com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15074{
15075 com::Utf8Str strControllerName = "Unknown";
15076 switch (aBusType)
15077 {
15078 case StorageBus_IDE:
15079 {
15080 strControllerName = "IDE";
15081 break;
15082 }
15083 case StorageBus_SATA:
15084 {
15085 strControllerName = "SATA";
15086 break;
15087 }
15088 case StorageBus_SCSI:
15089 {
15090 strControllerName = "SCSI";
15091 break;
15092 }
15093 case StorageBus_Floppy:
15094 {
15095 strControllerName = "Floppy";
15096 break;
15097 }
15098 case StorageBus_SAS:
15099 {
15100 strControllerName = "SAS";
15101 break;
15102 }
15103 case StorageBus_USB:
15104 {
15105 strControllerName = "USB";
15106 break;
15107 }
15108 default:
15109 break;
15110 }
15111 return strControllerName;
15112}
15113
15114HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15115{
15116 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15117
15118 AutoCaller autoCaller(this);
15119 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15120
15121 HRESULT rc = S_OK;
15122
15123 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15124 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15125 rc = getUSBDeviceFilters(usbDeviceFilters);
15126 if (FAILED(rc)) return rc;
15127
15128 NOREF(aFlags);
15129 com::Utf8Str osTypeId;
15130 ComObjPtr<GuestOSType> osType = NULL;
15131
15132 /* Get the guest os type as a string from the VB. */
15133 rc = getOSTypeId(osTypeId);
15134 if (FAILED(rc)) return rc;
15135
15136 /* Get the os type obj that coresponds, can be used to get
15137 * the defaults for this guest OS. */
15138 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15139 if (FAILED(rc)) return rc;
15140
15141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15142
15143 /* Let the OS type select 64-bit ness. */
15144 mHWData->mLongMode = osType->i_is64Bit()
15145 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15146
15147 /* Let the OS type enable the X2APIC */
15148 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15149
15150 /* This one covers IOAPICEnabled. */
15151 mBIOSSettings->i_applyDefaults(osType);
15152
15153 /* Initialize default record settings. */
15154 mRecordingSettings->i_applyDefaults();
15155
15156 /* Initialize default BIOS settings here */
15157 /* Hardware virtualization must be ON by default */
15158 mHWData->mAPIC = true;
15159 mHWData->mHWVirtExEnabled = true;
15160
15161 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15162 if (FAILED(rc)) return rc;
15163
15164 /* Graphics stuff. */
15165 GraphicsControllerType_T graphicsController;
15166 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15167 if (FAILED(rc)) return rc;
15168
15169 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15170 if (FAILED(rc)) return rc;
15171
15172 ULONG vramSize;
15173 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15174 if (FAILED(rc)) return rc;
15175
15176 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15177 if (FAILED(rc)) return rc;
15178
15179 BOOL fAccelerate2DVideoEnabled;
15180 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15181 if (FAILED(rc)) return rc;
15182
15183 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15184 if (FAILED(rc)) return rc;
15185
15186 BOOL fAccelerate3DEnabled;
15187 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15188 if (FAILED(rc)) return rc;
15189
15190 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15191 if (FAILED(rc)) return rc;
15192
15193 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15194 if (FAILED(rc)) return rc;
15195
15196 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15197 if (FAILED(rc)) return rc;
15198
15199 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15200 if (FAILED(rc)) return rc;
15201
15202 BOOL mRTCUseUTC;
15203 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15204 if (FAILED(rc)) return rc;
15205
15206 setRTCUseUTC(mRTCUseUTC);
15207 if (FAILED(rc)) return rc;
15208
15209 /* the setter does more than just the assignment, so use it */
15210 ChipsetType_T enmChipsetType;
15211 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15212 if (FAILED(rc)) return rc;
15213
15214 rc = COMSETTER(ChipsetType)(enmChipsetType);
15215 if (FAILED(rc)) return rc;
15216
15217 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15218 if (FAILED(rc)) return rc;
15219
15220 /* Apply network adapters defaults */
15221 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15222 mNetworkAdapters[slot]->i_applyDefaults(osType);
15223
15224 /* Apply serial port defaults */
15225 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15226 mSerialPorts[slot]->i_applyDefaults(osType);
15227
15228 /* Apply parallel port defaults - not OS dependent*/
15229 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15230 mParallelPorts[slot]->i_applyDefaults();
15231
15232 /* Audio stuff. */
15233 AudioControllerType_T audioController;
15234 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15235 if (FAILED(rc)) return rc;
15236
15237 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15238 if (FAILED(rc)) return rc;
15239
15240 AudioCodecType_T audioCodec;
15241 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15242 if (FAILED(rc)) return rc;
15243
15244 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15245 if (FAILED(rc)) return rc;
15246
15247 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15248 if (FAILED(rc)) return rc;
15249
15250 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15251 if (FAILED(rc)) return rc;
15252
15253 /* Storage Controllers */
15254 StorageControllerType_T hdStorageControllerType;
15255 StorageBus_T hdStorageBusType;
15256 StorageControllerType_T dvdStorageControllerType;
15257 StorageBus_T dvdStorageBusType;
15258 BOOL recommendedFloppy;
15259 ComPtr<IStorageController> floppyController;
15260 ComPtr<IStorageController> hdController;
15261 ComPtr<IStorageController> dvdController;
15262 Utf8Str strFloppyName, strDVDName, strHDName;
15263
15264 /* GUI auto generates controller names using bus type. Do the same*/
15265 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15266
15267 /* Floppy recommended? add one. */
15268 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15269 if (FAILED(rc)) return rc;
15270 if (recommendedFloppy)
15271 {
15272 rc = addStorageController(strFloppyName,
15273 StorageBus_Floppy,
15274 floppyController);
15275 if (FAILED(rc)) return rc;
15276 }
15277
15278 /* Setup one DVD storage controller. */
15279 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15280 if (FAILED(rc)) return rc;
15281
15282 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15283 if (FAILED(rc)) return rc;
15284
15285 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15286
15287 rc = addStorageController(strDVDName,
15288 dvdStorageBusType,
15289 dvdController);
15290 if (FAILED(rc)) return rc;
15291
15292 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15293 if (FAILED(rc)) return rc;
15294
15295 /* Setup one HDD storage controller. */
15296 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15297 if (FAILED(rc)) return rc;
15298
15299 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15300 if (FAILED(rc)) return rc;
15301
15302 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15303
15304 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15305 {
15306 rc = addStorageController(strHDName,
15307 hdStorageBusType,
15308 hdController);
15309 if (FAILED(rc)) return rc;
15310
15311 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15312 if (FAILED(rc)) return rc;
15313 }
15314 else
15315 {
15316 /* The HD controller is the same as DVD: */
15317 hdController = dvdController;
15318 }
15319
15320 /* Limit the AHCI port count if it's used because windows has trouble with
15321 * too many ports and other guest (OS X in particular) may take extra long
15322 * boot: */
15323
15324 // pParent = static_cast<Medium*>(aP)
15325 IStorageController *temp = hdController;
15326 ComObjPtr<StorageController> storageController;
15327 storageController = static_cast<StorageController *>(temp);
15328
15329 // tempHDController = aHDController;
15330 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15331 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15332 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15333 storageController->COMSETTER(PortCount)(1);
15334
15335 /* USB stuff */
15336
15337 bool ohciEnabled = false;
15338
15339 ComPtr<IUSBController> usbController;
15340 BOOL recommendedUSB3;
15341 BOOL recommendedUSB;
15342 BOOL usbProxyAvailable;
15343
15344 getUSBProxyAvailable(&usbProxyAvailable);
15345 if (FAILED(rc)) return rc;
15346
15347 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15348 if (FAILED(rc)) return rc;
15349 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15350 if (FAILED(rc)) return rc;
15351
15352 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15353 {
15354#ifdef VBOX_WITH_EXTPACK
15355 /* USB 3.0 is only available if the proper ExtPack is installed. */
15356 ExtPackManager *aManager = mParent->i_getExtPackManager();
15357 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15358 {
15359 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15360 if (FAILED(rc)) return rc;
15361
15362 /* xHci includes OHCI */
15363 ohciEnabled = true;
15364 }
15365#endif
15366 }
15367 if ( !ohciEnabled
15368 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15369 {
15370 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15371 if (FAILED(rc)) return rc;
15372 ohciEnabled = true;
15373
15374#ifdef VBOX_WITH_EXTPACK
15375 /* USB 2.0 is only available if the proper ExtPack is installed.
15376 * Note. Configuring EHCI here and providing messages about
15377 * the missing extpack isn't exactly clean, but it is a
15378 * necessary evil to patch over legacy compatability issues
15379 * introduced by the new distribution model. */
15380 ExtPackManager *manager = mParent->i_getExtPackManager();
15381 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15382 {
15383 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15384 if (FAILED(rc)) return rc;
15385 }
15386#endif
15387 }
15388
15389 /* Set recommended human interface device types: */
15390 BOOL recommendedUSBHID;
15391 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15392 if (FAILED(rc)) return rc;
15393
15394 if (recommendedUSBHID)
15395 {
15396 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15397 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15398 if (!ohciEnabled && !usbDeviceFilters.isNull())
15399 {
15400 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15401 if (FAILED(rc)) return rc;
15402 }
15403 }
15404
15405 BOOL recommendedUSBTablet;
15406 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15407 if (FAILED(rc)) return rc;
15408
15409 if (recommendedUSBTablet)
15410 {
15411 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15412 if (!ohciEnabled && !usbDeviceFilters.isNull())
15413 {
15414 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15415 if (FAILED(rc)) return rc;
15416 }
15417 }
15418 return S_OK;
15419}
15420
15421/* This isn't handled entirely by the wrapper generator yet. */
15422#ifdef VBOX_WITH_XPCOM
15423NS_DECL_CLASSINFO(SessionMachine)
15424NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15425
15426NS_DECL_CLASSINFO(SnapshotMachine)
15427NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15428#endif
Note: See TracBrowser for help on using the repository browser.

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