VirtualBox

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

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

Main: bugref:9618 Added Main/API support for AMD-V Virtualized VMSAVE/VMLOAD hardware virtualization feature.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 537.0 KB
Line 
1/* $Id: MachineImpl.cpp 85769 2020-08-14 12:59:51Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#ifdef VBOX_WITH_SHARED_CLIPBOARD
91# include <VBox/HostServices/VBoxClipboardSvc.h>
92#endif
93
94#include "VBox/com/MultiResult.h"
95
96#include <algorithm>
97
98#ifdef VBOX_WITH_DTRACE_R3_MAIN
99# include "dtrace/VBoxAPI.h"
100#endif
101
102#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
103# define HOSTSUFF_EXE ".exe"
104#else /* !RT_OS_WINDOWS */
105# define HOSTSUFF_EXE ""
106#endif /* !RT_OS_WINDOWS */
107
108// defines / prototypes
109/////////////////////////////////////////////////////////////////////////////
110
111/////////////////////////////////////////////////////////////////////////////
112// Machine::Data structure
113/////////////////////////////////////////////////////////////////////////////
114
115Machine::Data::Data()
116{
117 mRegistered = FALSE;
118 pMachineConfigFile = NULL;
119 /* Contains hints on what has changed when the user is using the VM (config
120 * changes, running the VM, ...). This is used to decide if a config needs
121 * to be written to disk. */
122 flModifications = 0;
123 /* VM modification usually also trigger setting the current state to
124 * "Modified". Although this is not always the case. An e.g. is the VM
125 * initialization phase or when snapshot related data is changed. The
126 * actually behavior is controlled by the following flag. */
127 m_fAllowStateModification = false;
128 mAccessible = FALSE;
129 /* mUuid is initialized in Machine::init() */
130
131 mMachineState = MachineState_PoweredOff;
132 RTTimeNow(&mLastStateChange);
133
134 mMachineStateDeps = 0;
135 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
136 mMachineStateChangePending = 0;
137
138 mCurrentStateModified = TRUE;
139 mGuestPropertiesModified = FALSE;
140
141 mSession.mPID = NIL_RTPROCESS;
142 mSession.mLockType = LockType_Null;
143 mSession.mState = SessionState_Unlocked;
144}
145
146Machine::Data::~Data()
147{
148 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
149 {
150 RTSemEventMultiDestroy(mMachineStateDepsSem);
151 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
152 }
153 if (pMachineConfigFile)
154 {
155 delete pMachineConfigFile;
156 pMachineConfigFile = NULL;
157 }
158}
159
160/////////////////////////////////////////////////////////////////////////////
161// Machine::HWData structure
162/////////////////////////////////////////////////////////////////////////////
163
164Machine::HWData::HWData()
165{
166 /* default values for a newly created machine */
167 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
168 mMemorySize = 128;
169 mCPUCount = 1;
170 mCPUHotPlugEnabled = false;
171 mMemoryBalloonSize = 0;
172 mPageFusionEnabled = false;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185 mHWVirtExVirtVmsaveVmload = true;
186#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
187 mPAEEnabled = true;
188#else
189 mPAEEnabled = false;
190#endif
191 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
192 mTripleFaultReset = false;
193 mAPIC = true;
194 mX2APIC = false;
195 mIBPBOnVMExit = false;
196 mIBPBOnVMEntry = false;
197 mSpecCtrl = false;
198 mSpecCtrlByHost = false;
199 mL1DFlushOnSched = true;
200 mL1DFlushOnVMEntry = false;
201 mMDSClearOnSched = true;
202 mMDSClearOnVMEntry = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mClipboardFileTransfersEnabled = FALSE;
218
219 mDnDMode = DnDMode_Disabled;
220
221 mFirmwareType = FirmwareType_BIOS;
222 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
223 mPointingHIDType = PointingHIDType_PS2Mouse;
224 mChipsetType = ChipsetType_PIIX3;
225 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()
3519 )
3520 return setError(VBOX_E_OBJECT_IN_USE,
3521 tr("Medium '%s' is already attached to this virtual machine"),
3522 medium->i_getLocationFull().c_str());
3523
3524 if (!medium.isNull())
3525 {
3526 MediumType_T mtype = medium->i_getType();
3527 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3528 // For DVDs it's not written to the config file, so needs no global config
3529 // version bump. For floppies it's a new attribute "type", which is ignored
3530 // by older VirtualBox version, so needs no global config version bump either.
3531 // For hard disks this type is not accepted.
3532 if (mtype == MediumType_MultiAttach)
3533 {
3534 // This type is new with VirtualBox 4.0 and therefore requires settings
3535 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3536 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3537 // two reasons: The medium type is a property of the media registry tree, which
3538 // can reside in the global config file (for pre-4.0 media); we would therefore
3539 // possibly need to bump the global config version. We don't want to do that though
3540 // because that might make downgrading to pre-4.0 impossible.
3541 // As a result, we can only use these two new types if the medium is NOT in the
3542 // global registry:
3543 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3544 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3545 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3546 )
3547 return setError(VBOX_E_INVALID_OBJECT_STATE,
3548 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3549 "to machines that were created with VirtualBox 4.0 or later"),
3550 medium->i_getLocationFull().c_str());
3551 }
3552 }
3553
3554 bool fIndirect = false;
3555 if (!medium.isNull())
3556 fIndirect = medium->i_isReadOnly();
3557 bool associate = true;
3558
3559 do
3560 {
3561 if ( aType == DeviceType_HardDisk
3562 && mMediumAttachments.isBackedUp())
3563 {
3564 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3565
3566 /* check if the medium was attached to the VM before we started
3567 * changing attachments in which case the attachment just needs to
3568 * be restored */
3569 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3570 {
3571 AssertReturn(!fIndirect, E_FAIL);
3572
3573 /* see if it's the same bus/channel/device */
3574 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3575 {
3576 /* the simplest case: restore the whole attachment
3577 * and return, nothing else to do */
3578 mMediumAttachments->push_back(pAttachTemp);
3579
3580 /* Reattach the medium to the VM. */
3581 if (fHotplug || fSilent)
3582 {
3583 mediumLock.release();
3584 treeLock.release();
3585 alock.release();
3586
3587 MediumLockList *pMediumLockList(new MediumLockList());
3588
3589 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3590 medium /* pToLockWrite */,
3591 false /* fMediumLockWriteAll */,
3592 NULL,
3593 *pMediumLockList);
3594 alock.acquire();
3595 if (FAILED(rc))
3596 delete pMediumLockList;
3597 else
3598 {
3599 mData->mSession.mLockedMedia.Unlock();
3600 alock.release();
3601 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3602 mData->mSession.mLockedMedia.Lock();
3603 alock.acquire();
3604 }
3605 alock.release();
3606
3607 if (SUCCEEDED(rc))
3608 {
3609 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3610 /* Remove lock list in case of error. */
3611 if (FAILED(rc))
3612 {
3613 mData->mSession.mLockedMedia.Unlock();
3614 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3615 mData->mSession.mLockedMedia.Lock();
3616 }
3617 }
3618 }
3619
3620 return S_OK;
3621 }
3622
3623 /* bus/channel/device differ; we need a new attachment object,
3624 * but don't try to associate it again */
3625 associate = false;
3626 break;
3627 }
3628 }
3629
3630 /* go further only if the attachment is to be indirect */
3631 if (!fIndirect)
3632 break;
3633
3634 /* perform the so called smart attachment logic for indirect
3635 * attachments. Note that smart attachment is only applicable to base
3636 * hard disks. */
3637
3638 if (medium->i_getParent().isNull())
3639 {
3640 /* first, investigate the backup copy of the current hard disk
3641 * attachments to make it possible to re-attach existing diffs to
3642 * another device slot w/o losing their contents */
3643 if (mMediumAttachments.isBackedUp())
3644 {
3645 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3646
3647 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3648 uint32_t foundLevel = 0;
3649
3650 for (MediumAttachmentList::const_iterator
3651 it = oldAtts.begin();
3652 it != oldAtts.end();
3653 ++it)
3654 {
3655 uint32_t level = 0;
3656 MediumAttachment *pAttach = *it;
3657 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3658 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3659 if (pMedium.isNull())
3660 continue;
3661
3662 if (pMedium->i_getBase(&level) == medium)
3663 {
3664 /* skip the hard disk if its currently attached (we
3665 * cannot attach the same hard disk twice) */
3666 if (i_findAttachment(*mMediumAttachments.data(),
3667 pMedium))
3668 continue;
3669
3670 /* matched device, channel and bus (i.e. attached to the
3671 * same place) will win and immediately stop the search;
3672 * otherwise the attachment that has the youngest
3673 * descendant of medium will be used
3674 */
3675 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3676 {
3677 /* the simplest case: restore the whole attachment
3678 * and return, nothing else to do */
3679 mMediumAttachments->push_back(*it);
3680
3681 /* Reattach the medium to the VM. */
3682 if (fHotplug || fSilent)
3683 {
3684 mediumLock.release();
3685 treeLock.release();
3686 alock.release();
3687
3688 MediumLockList *pMediumLockList(new MediumLockList());
3689
3690 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3691 medium /* pToLockWrite */,
3692 false /* fMediumLockWriteAll */,
3693 NULL,
3694 *pMediumLockList);
3695 alock.acquire();
3696 if (FAILED(rc))
3697 delete pMediumLockList;
3698 else
3699 {
3700 mData->mSession.mLockedMedia.Unlock();
3701 alock.release();
3702 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3703 mData->mSession.mLockedMedia.Lock();
3704 alock.acquire();
3705 }
3706 alock.release();
3707
3708 if (SUCCEEDED(rc))
3709 {
3710 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3711 /* Remove lock list in case of error. */
3712 if (FAILED(rc))
3713 {
3714 mData->mSession.mLockedMedia.Unlock();
3715 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3716 mData->mSession.mLockedMedia.Lock();
3717 }
3718 }
3719 }
3720
3721 return S_OK;
3722 }
3723 else if ( foundIt == oldAtts.end()
3724 || level > foundLevel /* prefer younger */
3725 )
3726 {
3727 foundIt = it;
3728 foundLevel = level;
3729 }
3730 }
3731 }
3732
3733 if (foundIt != oldAtts.end())
3734 {
3735 /* use the previously attached hard disk */
3736 medium = (*foundIt)->i_getMedium();
3737 mediumCaller.attach(medium);
3738 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3739 mediumLock.attach(medium);
3740 /* not implicit, doesn't require association with this VM */
3741 fIndirect = false;
3742 associate = false;
3743 /* go right to the MediumAttachment creation */
3744 break;
3745 }
3746 }
3747
3748 /* must give up the medium lock and medium tree lock as below we
3749 * go over snapshots, which needs a lock with higher lock order. */
3750 mediumLock.release();
3751 treeLock.release();
3752
3753 /* then, search through snapshots for the best diff in the given
3754 * hard disk's chain to base the new diff on */
3755
3756 ComObjPtr<Medium> base;
3757 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3758 while (snap)
3759 {
3760 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3761
3762 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3763
3764 MediumAttachment *pAttachFound = NULL;
3765 uint32_t foundLevel = 0;
3766
3767 for (MediumAttachmentList::const_iterator
3768 it = snapAtts.begin();
3769 it != snapAtts.end();
3770 ++it)
3771 {
3772 MediumAttachment *pAttach = *it;
3773 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3774 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3775 if (pMedium.isNull())
3776 continue;
3777
3778 uint32_t level = 0;
3779 if (pMedium->i_getBase(&level) == medium)
3780 {
3781 /* matched device, channel and bus (i.e. attached to the
3782 * same place) will win and immediately stop the search;
3783 * otherwise the attachment that has the youngest
3784 * descendant of medium will be used
3785 */
3786 if ( pAttach->i_getDevice() == aDevice
3787 && pAttach->i_getPort() == aControllerPort
3788 && pAttach->i_getControllerName() == aName
3789 )
3790 {
3791 pAttachFound = pAttach;
3792 break;
3793 }
3794 else if ( !pAttachFound
3795 || level > foundLevel /* prefer younger */
3796 )
3797 {
3798 pAttachFound = pAttach;
3799 foundLevel = level;
3800 }
3801 }
3802 }
3803
3804 if (pAttachFound)
3805 {
3806 base = pAttachFound->i_getMedium();
3807 break;
3808 }
3809
3810 snap = snap->i_getParent();
3811 }
3812
3813 /* re-lock medium tree and the medium, as we need it below */
3814 treeLock.acquire();
3815 mediumLock.acquire();
3816
3817 /* found a suitable diff, use it as a base */
3818 if (!base.isNull())
3819 {
3820 medium = base;
3821 mediumCaller.attach(medium);
3822 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3823 mediumLock.attach(medium);
3824 }
3825 }
3826
3827 Utf8Str strFullSnapshotFolder;
3828 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3829
3830 ComObjPtr<Medium> diff;
3831 diff.createObject();
3832 // store this diff in the same registry as the parent
3833 Guid uuidRegistryParent;
3834 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3835 {
3836 // parent image has no registry: this can happen if we're attaching a new immutable
3837 // image that has not yet been attached (medium then points to the base and we're
3838 // creating the diff image for the immutable, and the parent is not yet registered);
3839 // put the parent in the machine registry then
3840 mediumLock.release();
3841 treeLock.release();
3842 alock.release();
3843 i_addMediumToRegistry(medium);
3844 alock.acquire();
3845 treeLock.acquire();
3846 mediumLock.acquire();
3847 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3848 }
3849 rc = diff->init(mParent,
3850 medium->i_getPreferredDiffFormat(),
3851 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3852 uuidRegistryParent,
3853 DeviceType_HardDisk);
3854 if (FAILED(rc)) return rc;
3855
3856 /* Apply the normal locking logic to the entire chain. */
3857 MediumLockList *pMediumLockList(new MediumLockList());
3858 mediumLock.release();
3859 treeLock.release();
3860 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3861 diff /* pToLockWrite */,
3862 false /* fMediumLockWriteAll */,
3863 medium,
3864 *pMediumLockList);
3865 treeLock.acquire();
3866 mediumLock.acquire();
3867 if (SUCCEEDED(rc))
3868 {
3869 mediumLock.release();
3870 treeLock.release();
3871 rc = pMediumLockList->Lock();
3872 treeLock.acquire();
3873 mediumLock.acquire();
3874 if (FAILED(rc))
3875 setError(rc,
3876 tr("Could not lock medium when creating diff '%s'"),
3877 diff->i_getLocationFull().c_str());
3878 else
3879 {
3880 /* will release the lock before the potentially lengthy
3881 * operation, so protect with the special state */
3882 MachineState_T oldState = mData->mMachineState;
3883 i_setMachineState(MachineState_SettingUp);
3884
3885 mediumLock.release();
3886 treeLock.release();
3887 alock.release();
3888
3889 rc = medium->i_createDiffStorage(diff,
3890 medium->i_getPreferredDiffVariant(),
3891 pMediumLockList,
3892 NULL /* aProgress */,
3893 true /* aWait */,
3894 false /* aNotify */);
3895
3896 alock.acquire();
3897 treeLock.acquire();
3898 mediumLock.acquire();
3899
3900 i_setMachineState(oldState);
3901 }
3902 }
3903
3904 /* Unlock the media and free the associated memory. */
3905 delete pMediumLockList;
3906
3907 if (FAILED(rc)) return rc;
3908
3909 /* use the created diff for the actual attachment */
3910 medium = diff;
3911 mediumCaller.attach(medium);
3912 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3913 mediumLock.attach(medium);
3914 }
3915 while (0);
3916
3917 ComObjPtr<MediumAttachment> attachment;
3918 attachment.createObject();
3919 rc = attachment->init(this,
3920 medium,
3921 aName,
3922 aControllerPort,
3923 aDevice,
3924 aType,
3925 fIndirect,
3926 false /* fPassthrough */,
3927 false /* fTempEject */,
3928 false /* fNonRotational */,
3929 false /* fDiscard */,
3930 fHotplug /* fHotPluggable */,
3931 Utf8Str::Empty);
3932 if (FAILED(rc)) return rc;
3933
3934 if (associate && !medium.isNull())
3935 {
3936 // as the last step, associate the medium to the VM
3937 rc = medium->i_addBackReference(mData->mUuid);
3938 // here we can fail because of Deleting, or being in process of creating a Diff
3939 if (FAILED(rc)) return rc;
3940
3941 mediumLock.release();
3942 treeLock.release();
3943 alock.release();
3944 i_addMediumToRegistry(medium);
3945 alock.acquire();
3946 treeLock.acquire();
3947 mediumLock.acquire();
3948 }
3949
3950 /* success: finally remember the attachment */
3951 i_setModified(IsModified_Storage);
3952 mMediumAttachments.backup();
3953 mMediumAttachments->push_back(attachment);
3954
3955 mediumLock.release();
3956 treeLock.release();
3957 alock.release();
3958
3959 if (fHotplug || fSilent)
3960 {
3961 if (!medium.isNull())
3962 {
3963 MediumLockList *pMediumLockList(new MediumLockList());
3964
3965 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3966 medium /* pToLockWrite */,
3967 false /* fMediumLockWriteAll */,
3968 NULL,
3969 *pMediumLockList);
3970 alock.acquire();
3971 if (FAILED(rc))
3972 delete pMediumLockList;
3973 else
3974 {
3975 mData->mSession.mLockedMedia.Unlock();
3976 alock.release();
3977 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3978 mData->mSession.mLockedMedia.Lock();
3979 alock.acquire();
3980 }
3981 alock.release();
3982 }
3983
3984 if (SUCCEEDED(rc))
3985 {
3986 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3987 /* Remove lock list in case of error. */
3988 if (FAILED(rc))
3989 {
3990 mData->mSession.mLockedMedia.Unlock();
3991 mData->mSession.mLockedMedia.Remove(attachment);
3992 mData->mSession.mLockedMedia.Lock();
3993 }
3994 }
3995 }
3996
3997 /* Save modified registries, but skip this machine as it's the caller's
3998 * job to save its settings like all other settings changes. */
3999 mParent->i_unmarkRegistryModified(i_getId());
4000 mParent->i_saveModifiedRegistries();
4001
4002 if (SUCCEEDED(rc))
4003 {
4004 if (fIndirect && medium != aM)
4005 mParent->i_onMediumConfigChanged(medium);
4006 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4007 }
4008
4009 return rc;
4010}
4011
4012HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4013 LONG aDevice)
4014{
4015 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4016 aName.c_str(), aControllerPort, aDevice));
4017
4018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4019
4020 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4021 if (FAILED(rc)) return rc;
4022
4023 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4024
4025 /* Check for an existing controller. */
4026 ComObjPtr<StorageController> ctl;
4027 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4028 if (FAILED(rc)) return rc;
4029
4030 StorageControllerType_T ctrlType;
4031 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4032 if (FAILED(rc))
4033 return setError(E_FAIL,
4034 tr("Could not get type of controller '%s'"),
4035 aName.c_str());
4036
4037 bool fSilent = false;
4038 Utf8Str strReconfig;
4039
4040 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4041 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4042 if ( mData->mMachineState == MachineState_Paused
4043 && strReconfig == "1")
4044 fSilent = true;
4045
4046 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4047 bool fHotplug = false;
4048 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4049 fHotplug = true;
4050
4051 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4052 return setError(VBOX_E_INVALID_VM_STATE,
4053 tr("Controller '%s' does not support hotplugging"),
4054 aName.c_str());
4055
4056 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4057 aName,
4058 aControllerPort,
4059 aDevice);
4060 if (!pAttach)
4061 return setError(VBOX_E_OBJECT_NOT_FOUND,
4062 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4063 aDevice, aControllerPort, aName.c_str());
4064
4065 if (fHotplug && !pAttach->i_getHotPluggable())
4066 return setError(VBOX_E_NOT_SUPPORTED,
4067 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4068 aDevice, aControllerPort, aName.c_str());
4069
4070 /*
4071 * The VM has to detach the device before we delete any implicit diffs.
4072 * If this fails we can roll back without loosing data.
4073 */
4074 if (fHotplug || fSilent)
4075 {
4076 alock.release();
4077 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4078 alock.acquire();
4079 }
4080 if (FAILED(rc)) return rc;
4081
4082 /* If we are here everything went well and we can delete the implicit now. */
4083 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4084
4085 alock.release();
4086
4087 /* Save modified registries, but skip this machine as it's the caller's
4088 * job to save its settings like all other settings changes. */
4089 mParent->i_unmarkRegistryModified(i_getId());
4090 mParent->i_saveModifiedRegistries();
4091
4092 if (SUCCEEDED(rc))
4093 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4094
4095 return rc;
4096}
4097
4098HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4099 LONG aDevice, BOOL aPassthrough)
4100{
4101 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4102 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4103
4104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4105
4106 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4107 if (FAILED(rc)) return rc;
4108
4109 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4110
4111 /* Check for an existing controller. */
4112 ComObjPtr<StorageController> ctl;
4113 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4114 if (FAILED(rc)) return rc;
4115
4116 StorageControllerType_T ctrlType;
4117 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4118 if (FAILED(rc))
4119 return setError(E_FAIL,
4120 tr("Could not get type of controller '%s'"),
4121 aName.c_str());
4122
4123 bool fSilent = false;
4124 Utf8Str strReconfig;
4125
4126 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4127 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4128 if ( mData->mMachineState == MachineState_Paused
4129 && strReconfig == "1")
4130 fSilent = true;
4131
4132 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4133 bool fHotplug = false;
4134 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4135 fHotplug = true;
4136
4137 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4138 return setError(VBOX_E_INVALID_VM_STATE,
4139 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4140 aName.c_str());
4141
4142 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4143 aName,
4144 aControllerPort,
4145 aDevice);
4146 if (!pAttach)
4147 return setError(VBOX_E_OBJECT_NOT_FOUND,
4148 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4149 aDevice, aControllerPort, aName.c_str());
4150
4151
4152 i_setModified(IsModified_Storage);
4153 mMediumAttachments.backup();
4154
4155 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4156
4157 if (pAttach->i_getType() != DeviceType_DVD)
4158 return setError(E_INVALIDARG,
4159 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4160 aDevice, aControllerPort, aName.c_str());
4161
4162 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4163
4164 pAttach->i_updatePassthrough(!!aPassthrough);
4165
4166 attLock.release();
4167 alock.release();
4168 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4169 if (SUCCEEDED(rc) && fValueChanged)
4170 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4171
4172 return rc;
4173}
4174
4175HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4176 LONG aDevice, BOOL aTemporaryEject)
4177{
4178
4179 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4180 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4181
4182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4183
4184 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4185 if (FAILED(rc)) return rc;
4186
4187 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4188 aName,
4189 aControllerPort,
4190 aDevice);
4191 if (!pAttach)
4192 return setError(VBOX_E_OBJECT_NOT_FOUND,
4193 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4194 aDevice, aControllerPort, aName.c_str());
4195
4196
4197 i_setModified(IsModified_Storage);
4198 mMediumAttachments.backup();
4199
4200 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4201
4202 if (pAttach->i_getType() != DeviceType_DVD)
4203 return setError(E_INVALIDARG,
4204 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4205 aDevice, aControllerPort, aName.c_str());
4206 pAttach->i_updateTempEject(!!aTemporaryEject);
4207
4208 return S_OK;
4209}
4210
4211HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4212 LONG aDevice, BOOL aNonRotational)
4213{
4214
4215 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4216 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4217
4218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4219
4220 HRESULT rc = i_checkStateDependency(MutableStateDep);
4221 if (FAILED(rc)) return rc;
4222
4223 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4224
4225 if (Global::IsOnlineOrTransient(mData->mMachineState))
4226 return setError(VBOX_E_INVALID_VM_STATE,
4227 tr("Invalid machine state: %s"),
4228 Global::stringifyMachineState(mData->mMachineState));
4229
4230 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4231 aName,
4232 aControllerPort,
4233 aDevice);
4234 if (!pAttach)
4235 return setError(VBOX_E_OBJECT_NOT_FOUND,
4236 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4237 aDevice, aControllerPort, aName.c_str());
4238
4239
4240 i_setModified(IsModified_Storage);
4241 mMediumAttachments.backup();
4242
4243 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4244
4245 if (pAttach->i_getType() != DeviceType_HardDisk)
4246 return setError(E_INVALIDARG,
4247 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"),
4248 aDevice, aControllerPort, aName.c_str());
4249 pAttach->i_updateNonRotational(!!aNonRotational);
4250
4251 return S_OK;
4252}
4253
4254HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4255 LONG aDevice, BOOL aDiscard)
4256{
4257
4258 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4259 aName.c_str(), aControllerPort, aDevice, aDiscard));
4260
4261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4262
4263 HRESULT rc = i_checkStateDependency(MutableStateDep);
4264 if (FAILED(rc)) return rc;
4265
4266 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4267
4268 if (Global::IsOnlineOrTransient(mData->mMachineState))
4269 return setError(VBOX_E_INVALID_VM_STATE,
4270 tr("Invalid machine state: %s"),
4271 Global::stringifyMachineState(mData->mMachineState));
4272
4273 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4274 aName,
4275 aControllerPort,
4276 aDevice);
4277 if (!pAttach)
4278 return setError(VBOX_E_OBJECT_NOT_FOUND,
4279 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4280 aDevice, aControllerPort, aName.c_str());
4281
4282
4283 i_setModified(IsModified_Storage);
4284 mMediumAttachments.backup();
4285
4286 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4287
4288 if (pAttach->i_getType() != DeviceType_HardDisk)
4289 return setError(E_INVALIDARG,
4290 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"),
4291 aDevice, aControllerPort, aName.c_str());
4292 pAttach->i_updateDiscard(!!aDiscard);
4293
4294 return S_OK;
4295}
4296
4297HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4298 LONG aDevice, BOOL aHotPluggable)
4299{
4300 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4301 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4302
4303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4304
4305 HRESULT rc = i_checkStateDependency(MutableStateDep);
4306 if (FAILED(rc)) return rc;
4307
4308 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4309
4310 if (Global::IsOnlineOrTransient(mData->mMachineState))
4311 return setError(VBOX_E_INVALID_VM_STATE,
4312 tr("Invalid machine state: %s"),
4313 Global::stringifyMachineState(mData->mMachineState));
4314
4315 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4316 aName,
4317 aControllerPort,
4318 aDevice);
4319 if (!pAttach)
4320 return setError(VBOX_E_OBJECT_NOT_FOUND,
4321 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4322 aDevice, aControllerPort, aName.c_str());
4323
4324 /* Check for an existing controller. */
4325 ComObjPtr<StorageController> ctl;
4326 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4327 if (FAILED(rc)) return rc;
4328
4329 StorageControllerType_T ctrlType;
4330 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4331 if (FAILED(rc))
4332 return setError(E_FAIL,
4333 tr("Could not get type of controller '%s'"),
4334 aName.c_str());
4335
4336 if (!i_isControllerHotplugCapable(ctrlType))
4337 return setError(VBOX_E_NOT_SUPPORTED,
4338 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4339 aName.c_str());
4340
4341 i_setModified(IsModified_Storage);
4342 mMediumAttachments.backup();
4343
4344 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4345
4346 if (pAttach->i_getType() == DeviceType_Floppy)
4347 return setError(E_INVALIDARG,
4348 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"),
4349 aDevice, aControllerPort, aName.c_str());
4350 pAttach->i_updateHotPluggable(!!aHotPluggable);
4351
4352 return S_OK;
4353}
4354
4355HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4356 LONG aDevice)
4357{
4358 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4359 aName.c_str(), aControllerPort, aDevice));
4360
4361 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4362}
4363
4364HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4365 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4366{
4367 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4368 aName.c_str(), aControllerPort, aDevice));
4369
4370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4371
4372 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4373 if (FAILED(rc)) return rc;
4374
4375 if (Global::IsOnlineOrTransient(mData->mMachineState))
4376 return setError(VBOX_E_INVALID_VM_STATE,
4377 tr("Invalid machine state: %s"),
4378 Global::stringifyMachineState(mData->mMachineState));
4379
4380 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4381 aName,
4382 aControllerPort,
4383 aDevice);
4384 if (!pAttach)
4385 return setError(VBOX_E_OBJECT_NOT_FOUND,
4386 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4387 aDevice, aControllerPort, aName.c_str());
4388
4389
4390 i_setModified(IsModified_Storage);
4391 mMediumAttachments.backup();
4392
4393 IBandwidthGroup *iB = aBandwidthGroup;
4394 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4395 if (aBandwidthGroup && group.isNull())
4396 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4397
4398 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4399
4400 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4401 if (strBandwidthGroupOld.isNotEmpty())
4402 {
4403 /* Get the bandwidth group object and release it - this must not fail. */
4404 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4405 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4406 Assert(SUCCEEDED(rc));
4407
4408 pBandwidthGroupOld->i_release();
4409 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4410 }
4411
4412 if (!group.isNull())
4413 {
4414 group->i_reference();
4415 pAttach->i_updateBandwidthGroup(group->i_getName());
4416 }
4417
4418 return S_OK;
4419}
4420
4421HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4422 LONG aControllerPort,
4423 LONG aDevice,
4424 DeviceType_T aType)
4425{
4426 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4427 aName.c_str(), aControllerPort, aDevice, aType));
4428
4429 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4430}
4431
4432
4433HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4434 LONG aControllerPort,
4435 LONG aDevice,
4436 BOOL aForce)
4437{
4438 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4439 aName.c_str(), aControllerPort, aForce));
4440
4441 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4442}
4443
4444HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4445 LONG aControllerPort,
4446 LONG aDevice,
4447 const ComPtr<IMedium> &aMedium,
4448 BOOL aForce)
4449{
4450 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4451 aName.c_str(), aControllerPort, aDevice, aForce));
4452
4453 // request the host lock first, since might be calling Host methods for getting host drives;
4454 // next, protect the media tree all the while we're in here, as well as our member variables
4455 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4456 this->lockHandle(),
4457 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4458
4459 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4460 aName,
4461 aControllerPort,
4462 aDevice);
4463 if (pAttach.isNull())
4464 return setError(VBOX_E_OBJECT_NOT_FOUND,
4465 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4466 aDevice, aControllerPort, aName.c_str());
4467
4468 /* Remember previously mounted medium. The medium before taking the
4469 * backup is not necessarily the same thing. */
4470 ComObjPtr<Medium> oldmedium;
4471 oldmedium = pAttach->i_getMedium();
4472
4473 IMedium *iM = aMedium;
4474 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4475 if (aMedium && pMedium.isNull())
4476 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4477
4478 AutoCaller mediumCaller(pMedium);
4479 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4480
4481 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4482 if (pMedium)
4483 {
4484 DeviceType_T mediumType = pAttach->i_getType();
4485 switch (mediumType)
4486 {
4487 case DeviceType_DVD:
4488 case DeviceType_Floppy:
4489 break;
4490
4491 default:
4492 return setError(VBOX_E_INVALID_OBJECT_STATE,
4493 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4494 aControllerPort,
4495 aDevice,
4496 aName.c_str());
4497 }
4498 }
4499
4500 i_setModified(IsModified_Storage);
4501 mMediumAttachments.backup();
4502
4503 {
4504 // The backup operation makes the pAttach reference point to the
4505 // old settings. Re-get the correct reference.
4506 pAttach = i_findAttachment(*mMediumAttachments.data(),
4507 aName,
4508 aControllerPort,
4509 aDevice);
4510 if (!oldmedium.isNull())
4511 oldmedium->i_removeBackReference(mData->mUuid);
4512 if (!pMedium.isNull())
4513 {
4514 pMedium->i_addBackReference(mData->mUuid);
4515
4516 mediumLock.release();
4517 multiLock.release();
4518 i_addMediumToRegistry(pMedium);
4519 multiLock.acquire();
4520 mediumLock.acquire();
4521 }
4522
4523 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4524 pAttach->i_updateMedium(pMedium);
4525 }
4526
4527 i_setModified(IsModified_Storage);
4528
4529 mediumLock.release();
4530 multiLock.release();
4531 HRESULT rc = i_onMediumChange(pAttach, aForce);
4532 multiLock.acquire();
4533 mediumLock.acquire();
4534
4535 /* On error roll back this change only. */
4536 if (FAILED(rc))
4537 {
4538 if (!pMedium.isNull())
4539 pMedium->i_removeBackReference(mData->mUuid);
4540 pAttach = i_findAttachment(*mMediumAttachments.data(),
4541 aName,
4542 aControllerPort,
4543 aDevice);
4544 /* If the attachment is gone in the meantime, bail out. */
4545 if (pAttach.isNull())
4546 return rc;
4547 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4548 if (!oldmedium.isNull())
4549 oldmedium->i_addBackReference(mData->mUuid);
4550 pAttach->i_updateMedium(oldmedium);
4551 }
4552
4553 mediumLock.release();
4554 multiLock.release();
4555
4556 /* Save modified registries, but skip this machine as it's the caller's
4557 * job to save its settings like all other settings changes. */
4558 mParent->i_unmarkRegistryModified(i_getId());
4559 mParent->i_saveModifiedRegistries();
4560
4561 return rc;
4562}
4563HRESULT Machine::getMedium(const com::Utf8Str &aName,
4564 LONG aControllerPort,
4565 LONG aDevice,
4566 ComPtr<IMedium> &aMedium)
4567{
4568 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4569 aName.c_str(), aControllerPort, aDevice));
4570
4571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4572
4573 aMedium = NULL;
4574
4575 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4576 aName,
4577 aControllerPort,
4578 aDevice);
4579 if (pAttach.isNull())
4580 return setError(VBOX_E_OBJECT_NOT_FOUND,
4581 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4582 aDevice, aControllerPort, aName.c_str());
4583
4584 aMedium = pAttach->i_getMedium();
4585
4586 return S_OK;
4587}
4588
4589HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4590{
4591
4592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4593
4594 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4595
4596 return S_OK;
4597}
4598
4599HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4600{
4601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4602
4603 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4604
4605 return S_OK;
4606}
4607
4608HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4609{
4610 /* Do not assert if slot is out of range, just return the advertised
4611 status. testdriver/vbox.py triggers this in logVmInfo. */
4612 if (aSlot >= mNetworkAdapters.size())
4613 return setError(E_INVALIDARG,
4614 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4615 aSlot, mNetworkAdapters.size());
4616
4617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4618
4619 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4620
4621 return S_OK;
4622}
4623
4624HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4625{
4626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4627
4628 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4629 size_t i = 0;
4630 for (settings::StringsMap::const_iterator
4631 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4632 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4633 ++it, ++i)
4634 aKeys[i] = it->first;
4635
4636 return S_OK;
4637}
4638
4639 /**
4640 * @note Locks this object for reading.
4641 */
4642HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4643 com::Utf8Str &aValue)
4644{
4645 /* start with nothing found */
4646 aValue = "";
4647
4648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4649
4650 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4651 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4652 // found:
4653 aValue = it->second; // source is a Utf8Str
4654
4655 /* return the result to caller (may be empty) */
4656 return S_OK;
4657}
4658
4659 /**
4660 * @note Locks mParent for writing + this object for writing.
4661 */
4662HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4663{
4664 /* Because control characters in aKey have caused problems in the settings
4665 * they are rejected unless the key should be deleted. */
4666 if (!aValue.isEmpty())
4667 {
4668 for (size_t i = 0; i < aKey.length(); ++i)
4669 {
4670 char ch = aKey[i];
4671 if (RTLocCIsCntrl(ch))
4672 return E_INVALIDARG;
4673 }
4674 }
4675
4676 Utf8Str strOldValue; // empty
4677
4678 // locking note: we only hold the read lock briefly to look up the old value,
4679 // then release it and call the onExtraCanChange callbacks. There is a small
4680 // chance of a race insofar as the callback might be called twice if two callers
4681 // change the same key at the same time, but that's a much better solution
4682 // than the deadlock we had here before. The actual changing of the extradata
4683 // is then performed under the write lock and race-free.
4684
4685 // look up the old value first; if nothing has changed then we need not do anything
4686 {
4687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4688
4689 // For snapshots don't even think about allowing changes, extradata
4690 // is global for a machine, so there is nothing snapshot specific.
4691 if (i_isSnapshotMachine())
4692 return setError(VBOX_E_INVALID_VM_STATE,
4693 tr("Cannot set extradata for a snapshot"));
4694
4695 // check if the right IMachine instance is used
4696 if (mData->mRegistered && !i_isSessionMachine())
4697 return setError(VBOX_E_INVALID_VM_STATE,
4698 tr("Cannot set extradata for an immutable machine"));
4699
4700 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4701 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4702 strOldValue = it->second;
4703 }
4704
4705 bool fChanged;
4706 if ((fChanged = (strOldValue != aValue)))
4707 {
4708 // ask for permission from all listeners outside the locks;
4709 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4710 // lock to copy the list of callbacks to invoke
4711 Bstr bstrError;
4712 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4713 {
4714 const char *sep = bstrError.isEmpty() ? "" : ": ";
4715 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4716 return setError(E_ACCESSDENIED,
4717 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4718 aKey.c_str(),
4719 aValue.c_str(),
4720 sep,
4721 bstrError.raw());
4722 }
4723
4724 // data is changing and change not vetoed: then write it out under the lock
4725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4726
4727 if (aValue.isEmpty())
4728 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4729 else
4730 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4731 // creates a new key if needed
4732
4733 bool fNeedsGlobalSaveSettings = false;
4734 // This saving of settings is tricky: there is no "old state" for the
4735 // extradata items at all (unlike all other settings), so the old/new
4736 // settings comparison would give a wrong result!
4737 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4738
4739 if (fNeedsGlobalSaveSettings)
4740 {
4741 // save the global settings; for that we should hold only the VirtualBox lock
4742 alock.release();
4743 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4744 mParent->i_saveSettings();
4745 }
4746 }
4747
4748 // fire notification outside the lock
4749 if (fChanged)
4750 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4751
4752 return S_OK;
4753}
4754
4755HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4756{
4757 aProgress = NULL;
4758 NOREF(aSettingsFilePath);
4759 ReturnComNotImplemented();
4760}
4761
4762HRESULT Machine::saveSettings()
4763{
4764 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4765
4766 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4767 if (FAILED(rc)) return rc;
4768
4769 /* the settings file path may never be null */
4770 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4771
4772 /* save all VM data excluding snapshots */
4773 bool fNeedsGlobalSaveSettings = false;
4774 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4775 mlock.release();
4776
4777 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4778 {
4779 // save the global settings; for that we should hold only the VirtualBox lock
4780 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4781 rc = mParent->i_saveSettings();
4782 }
4783
4784 return rc;
4785}
4786
4787
4788HRESULT Machine::discardSettings()
4789{
4790 /*
4791 * We need to take the machine list lock here as well as the machine one
4792 * or we'll get into trouble should any media stuff require rolling back.
4793 *
4794 * Details:
4795 *
4796 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4797 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4798 * 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]
4799 * 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
4800 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4801 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4802 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4803 * 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
4804 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4805 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4806 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4807 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4808 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4809 * 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]
4810 * 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] (*)
4811 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4812 * 0:005> k
4813 * # Child-SP RetAddr Call Site
4814 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4815 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4816 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4817 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4818 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4819 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4820 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4821 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4822 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4823 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4824 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4825 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4826 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4827 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4828 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4829 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4830 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4831 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4832 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4833 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4834 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4835 *
4836 */
4837 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4839
4840 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4841 if (FAILED(rc)) return rc;
4842
4843 /*
4844 * during this rollback, the session will be notified if data has
4845 * been actually changed
4846 */
4847 i_rollback(true /* aNotify */);
4848
4849 return S_OK;
4850}
4851
4852/** @note Locks objects! */
4853HRESULT Machine::unregister(AutoCaller &autoCaller,
4854 CleanupMode_T aCleanupMode,
4855 std::vector<ComPtr<IMedium> > &aMedia)
4856{
4857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4858
4859 Guid id(i_getId());
4860
4861 if (mData->mSession.mState != SessionState_Unlocked)
4862 return setError(VBOX_E_INVALID_OBJECT_STATE,
4863 tr("Cannot unregister the machine '%s' while it is locked"),
4864 mUserData->s.strName.c_str());
4865
4866 // wait for state dependents to drop to zero
4867 i_ensureNoStateDependencies();
4868
4869 if (!mData->mAccessible)
4870 {
4871 // inaccessible machines can only be unregistered; uninitialize ourselves
4872 // here because currently there may be no unregistered that are inaccessible
4873 // (this state combination is not supported). Note releasing the caller and
4874 // leaving the lock before calling uninit()
4875 alock.release();
4876 autoCaller.release();
4877
4878 uninit();
4879
4880 mParent->i_unregisterMachine(this, id);
4881 // calls VirtualBox::i_saveSettings()
4882
4883 return S_OK;
4884 }
4885
4886 HRESULT rc = S_OK;
4887 mData->llFilesToDelete.clear();
4888
4889 if (!mSSData->strStateFilePath.isEmpty())
4890 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4891
4892 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4893 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4894 mData->llFilesToDelete.push_back(strNVRAMFile);
4895
4896 // This list collects the medium objects from all medium attachments
4897 // which we will detach from the machine and its snapshots, in a specific
4898 // order which allows for closing all media without getting "media in use"
4899 // errors, simply by going through the list from the front to the back:
4900 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4901 // and must be closed before the parent media from the snapshots, or closing the parents
4902 // will fail because they still have children);
4903 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4904 // the root ("first") snapshot of the machine.
4905 MediaList llMedia;
4906
4907 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4908 && mMediumAttachments->size()
4909 )
4910 {
4911 // we have media attachments: detach them all and add the Medium objects to our list
4912 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4913 }
4914
4915 if (mData->mFirstSnapshot)
4916 {
4917 // add the media from the medium attachments of the snapshots to llMedia
4918 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4919 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4920 // into the children first
4921
4922 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4923 MachineState_T oldState = mData->mMachineState;
4924 mData->mMachineState = MachineState_DeletingSnapshot;
4925
4926 // make a copy of the first snapshot reference so the refcount does not
4927 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4928 // (would hang due to the AutoCaller voodoo)
4929 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4930
4931 // GO!
4932 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4933
4934 mData->mMachineState = oldState;
4935 }
4936
4937 if (FAILED(rc))
4938 {
4939 i_rollbackMedia();
4940 return rc;
4941 }
4942
4943 // commit all the media changes made above
4944 i_commitMedia();
4945
4946 mData->mRegistered = false;
4947
4948 // machine lock no longer needed
4949 alock.release();
4950
4951 /* Make sure that the settings of the current VM are not saved, because
4952 * they are rather crippled at this point to meet the cleanup expectations
4953 * and there's no point destroying the VM config on disk just because. */
4954 mParent->i_unmarkRegistryModified(id);
4955
4956 // return media to caller
4957 aMedia.resize(llMedia.size());
4958 size_t i = 0;
4959 for (MediaList::const_iterator
4960 it = llMedia.begin();
4961 it != llMedia.end();
4962 ++it, ++i)
4963 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4964
4965 mParent->i_unregisterMachine(this, id);
4966 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4967
4968 return S_OK;
4969}
4970
4971/**
4972 * Task record for deleting a machine config.
4973 */
4974class Machine::DeleteConfigTask
4975 : public Machine::Task
4976{
4977public:
4978 DeleteConfigTask(Machine *m,
4979 Progress *p,
4980 const Utf8Str &t,
4981 const RTCList<ComPtr<IMedium> > &llMediums,
4982 const StringsList &llFilesToDelete)
4983 : Task(m, p, t),
4984 m_llMediums(llMediums),
4985 m_llFilesToDelete(llFilesToDelete)
4986 {}
4987
4988private:
4989 void handler()
4990 {
4991 try
4992 {
4993 m_pMachine->i_deleteConfigHandler(*this);
4994 }
4995 catch (...)
4996 {
4997 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4998 }
4999 }
5000
5001 RTCList<ComPtr<IMedium> > m_llMediums;
5002 StringsList m_llFilesToDelete;
5003
5004 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5005};
5006
5007/**
5008 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5009 * SessionMachine::taskHandler().
5010 *
5011 * @note Locks this object for writing.
5012 *
5013 * @param task
5014 * @return
5015 */
5016void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5017{
5018 LogFlowThisFuncEnter();
5019
5020 AutoCaller autoCaller(this);
5021 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5022 if (FAILED(autoCaller.rc()))
5023 {
5024 /* we might have been uninitialized because the session was accidentally
5025 * closed by the client, so don't assert */
5026 HRESULT rc = setError(E_FAIL,
5027 tr("The session has been accidentally closed"));
5028 task.m_pProgress->i_notifyComplete(rc);
5029 LogFlowThisFuncLeave();
5030 return;
5031 }
5032
5033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5034
5035 HRESULT rc = S_OK;
5036
5037 try
5038 {
5039 ULONG uLogHistoryCount = 3;
5040 ComPtr<ISystemProperties> systemProperties;
5041 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5042 if (FAILED(rc)) throw rc;
5043
5044 if (!systemProperties.isNull())
5045 {
5046 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5047 if (FAILED(rc)) throw rc;
5048 }
5049
5050 MachineState_T oldState = mData->mMachineState;
5051 i_setMachineState(MachineState_SettingUp);
5052 alock.release();
5053 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5054 {
5055 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5056 {
5057 AutoCaller mac(pMedium);
5058 if (FAILED(mac.rc())) throw mac.rc();
5059 Utf8Str strLocation = pMedium->i_getLocationFull();
5060 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5061 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5062 if (FAILED(rc)) throw rc;
5063 }
5064 if (pMedium->i_isMediumFormatFile())
5065 {
5066 ComPtr<IProgress> pProgress2;
5067 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5068 if (FAILED(rc)) throw rc;
5069 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5070 if (FAILED(rc)) throw rc;
5071 }
5072
5073 /* Close the medium, deliberately without checking the return
5074 * code, and without leaving any trace in the error info, as
5075 * a failure here is a very minor issue, which shouldn't happen
5076 * as above we even managed to delete the medium. */
5077 {
5078 ErrorInfoKeeper eik;
5079 pMedium->Close();
5080 }
5081 }
5082 i_setMachineState(oldState);
5083 alock.acquire();
5084
5085 // delete the files pushed on the task list by Machine::Delete()
5086 // (this includes saved states of the machine and snapshots and
5087 // medium storage files from the IMedium list passed in, and the
5088 // machine XML file)
5089 for (StringsList::const_iterator
5090 it = task.m_llFilesToDelete.begin();
5091 it != task.m_llFilesToDelete.end();
5092 ++it)
5093 {
5094 const Utf8Str &strFile = *it;
5095 LogFunc(("Deleting file %s\n", strFile.c_str()));
5096 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5097 if (FAILED(rc)) throw rc;
5098
5099 int vrc = RTFileDelete(strFile.c_str());
5100 if (RT_FAILURE(vrc))
5101 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5102 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5103 }
5104
5105 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5106 if (FAILED(rc)) throw rc;
5107
5108 /* delete the settings only when the file actually exists */
5109 if (mData->pMachineConfigFile->fileExists())
5110 {
5111 /* Delete any backup or uncommitted XML files. Ignore failures.
5112 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5113 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5114 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5115 RTFileDelete(otherXml.c_str());
5116 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5117 RTFileDelete(otherXml.c_str());
5118
5119 /* delete the Logs folder, nothing important should be left
5120 * there (we don't check for errors because the user might have
5121 * some private files there that we don't want to delete) */
5122 Utf8Str logFolder;
5123 getLogFolder(logFolder);
5124 Assert(logFolder.length());
5125 if (RTDirExists(logFolder.c_str()))
5126 {
5127 /* Delete all VBox.log[.N] files from the Logs folder
5128 * (this must be in sync with the rotation logic in
5129 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5130 * files that may have been created by the GUI. */
5131 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5132 RTFileDelete(log.c_str());
5133 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5134 RTFileDelete(log.c_str());
5135 for (ULONG i = uLogHistoryCount; i > 0; i--)
5136 {
5137 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5138 RTFileDelete(log.c_str());
5139 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5140 RTFileDelete(log.c_str());
5141 }
5142#if defined(RT_OS_WINDOWS)
5143 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5144 RTFileDelete(log.c_str());
5145 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5146 RTFileDelete(log.c_str());
5147#endif
5148
5149 RTDirRemove(logFolder.c_str());
5150 }
5151
5152 /* delete the Snapshots folder, nothing important should be left
5153 * there (we don't check for errors because the user might have
5154 * some private files there that we don't want to delete) */
5155 Utf8Str strFullSnapshotFolder;
5156 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5157 Assert(!strFullSnapshotFolder.isEmpty());
5158 if (RTDirExists(strFullSnapshotFolder.c_str()))
5159 RTDirRemove(strFullSnapshotFolder.c_str());
5160
5161 // delete the directory that contains the settings file, but only
5162 // if it matches the VM name
5163 Utf8Str settingsDir;
5164 if (i_isInOwnDir(&settingsDir))
5165 RTDirRemove(settingsDir.c_str());
5166 }
5167
5168 alock.release();
5169
5170 mParent->i_saveModifiedRegistries();
5171 }
5172 catch (HRESULT aRC) { rc = aRC; }
5173
5174 task.m_pProgress->i_notifyComplete(rc);
5175
5176 LogFlowThisFuncLeave();
5177}
5178
5179HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5180{
5181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5182
5183 HRESULT rc = i_checkStateDependency(MutableStateDep);
5184 if (FAILED(rc)) return rc;
5185
5186 if (mData->mRegistered)
5187 return setError(VBOX_E_INVALID_VM_STATE,
5188 tr("Cannot delete settings of a registered machine"));
5189
5190 // collect files to delete
5191 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5192 // machine config file
5193 if (mData->pMachineConfigFile->fileExists())
5194 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5195 // backup of machine config file
5196 Utf8Str strTmp(mData->m_strConfigFileFull);
5197 strTmp.append("-prev");
5198 if (RTFileExists(strTmp.c_str()))
5199 llFilesToDelete.push_back(strTmp);
5200
5201 RTCList<ComPtr<IMedium> > llMediums;
5202 for (size_t i = 0; i < aMedia.size(); ++i)
5203 {
5204 IMedium *pIMedium(aMedia[i]);
5205 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5206 if (pMedium.isNull())
5207 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5208 SafeArray<BSTR> ids;
5209 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5210 if (FAILED(rc)) return rc;
5211 /* At this point the medium should not have any back references
5212 * anymore. If it has it is attached to another VM and *must* not
5213 * deleted. */
5214 if (ids.size() < 1)
5215 llMediums.append(pMedium);
5216 }
5217
5218 ComObjPtr<Progress> pProgress;
5219 pProgress.createObject();
5220 rc = pProgress->init(i_getVirtualBox(),
5221 static_cast<IMachine*>(this) /* aInitiator */,
5222 tr("Deleting files"),
5223 true /* fCancellable */,
5224 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5225 tr("Collecting file inventory"));
5226 if (FAILED(rc))
5227 return rc;
5228
5229 /* create and start the task on a separate thread (note that it will not
5230 * start working until we release alock) */
5231 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5232 rc = pTask->createThread();
5233 pTask = NULL;
5234 if (FAILED(rc))
5235 return rc;
5236
5237 pProgress.queryInterfaceTo(aProgress.asOutParam());
5238
5239 LogFlowFuncLeave();
5240
5241 return S_OK;
5242}
5243
5244HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5245{
5246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5247
5248 ComObjPtr<Snapshot> pSnapshot;
5249 HRESULT rc;
5250
5251 if (aNameOrId.isEmpty())
5252 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5253 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5254 else
5255 {
5256 Guid uuid(aNameOrId);
5257 if (uuid.isValid())
5258 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5259 else
5260 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5261 }
5262 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5263
5264 return rc;
5265}
5266
5267HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5268 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5269{
5270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5271
5272 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5273 if (FAILED(rc)) return rc;
5274
5275 ComObjPtr<SharedFolder> sharedFolder;
5276 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5277 if (SUCCEEDED(rc))
5278 return setError(VBOX_E_OBJECT_IN_USE,
5279 tr("Shared folder named '%s' already exists"),
5280 aName.c_str());
5281
5282 sharedFolder.createObject();
5283 rc = sharedFolder->init(i_getMachine(),
5284 aName,
5285 aHostPath,
5286 !!aWritable,
5287 !!aAutomount,
5288 aAutoMountPoint,
5289 true /* fFailOnError */);
5290 if (FAILED(rc)) return rc;
5291
5292 i_setModified(IsModified_SharedFolders);
5293 mHWData.backup();
5294 mHWData->mSharedFolders.push_back(sharedFolder);
5295
5296 /* inform the direct session if any */
5297 alock.release();
5298 i_onSharedFolderChange();
5299
5300 return S_OK;
5301}
5302
5303HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5304{
5305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5306
5307 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5308 if (FAILED(rc)) return rc;
5309
5310 ComObjPtr<SharedFolder> sharedFolder;
5311 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5312 if (FAILED(rc)) return rc;
5313
5314 i_setModified(IsModified_SharedFolders);
5315 mHWData.backup();
5316 mHWData->mSharedFolders.remove(sharedFolder);
5317
5318 /* inform the direct session if any */
5319 alock.release();
5320 i_onSharedFolderChange();
5321
5322 return S_OK;
5323}
5324
5325HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5326{
5327 /* start with No */
5328 *aCanShow = FALSE;
5329
5330 ComPtr<IInternalSessionControl> directControl;
5331 {
5332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5333
5334 if (mData->mSession.mState != SessionState_Locked)
5335 return setError(VBOX_E_INVALID_VM_STATE,
5336 tr("Machine is not locked for session (session state: %s)"),
5337 Global::stringifySessionState(mData->mSession.mState));
5338
5339 if (mData->mSession.mLockType == LockType_VM)
5340 directControl = mData->mSession.mDirectControl;
5341 }
5342
5343 /* ignore calls made after #OnSessionEnd() is called */
5344 if (!directControl)
5345 return S_OK;
5346
5347 LONG64 dummy;
5348 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5349}
5350
5351HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5352{
5353 ComPtr<IInternalSessionControl> directControl;
5354 {
5355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5356
5357 if (mData->mSession.mState != SessionState_Locked)
5358 return setError(E_FAIL,
5359 tr("Machine is not locked for session (session state: %s)"),
5360 Global::stringifySessionState(mData->mSession.mState));
5361
5362 if (mData->mSession.mLockType == LockType_VM)
5363 directControl = mData->mSession.mDirectControl;
5364 }
5365
5366 /* ignore calls made after #OnSessionEnd() is called */
5367 if (!directControl)
5368 return S_OK;
5369
5370 BOOL dummy;
5371 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5372}
5373
5374#ifdef VBOX_WITH_GUEST_PROPS
5375/**
5376 * Look up a guest property in VBoxSVC's internal structures.
5377 */
5378HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5379 com::Utf8Str &aValue,
5380 LONG64 *aTimestamp,
5381 com::Utf8Str &aFlags) const
5382{
5383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5384
5385 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5386 if (it != mHWData->mGuestProperties.end())
5387 {
5388 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5389 aValue = it->second.strValue;
5390 *aTimestamp = it->second.mTimestamp;
5391 GuestPropWriteFlags(it->second.mFlags, szFlags);
5392 aFlags = Utf8Str(szFlags);
5393 }
5394
5395 return S_OK;
5396}
5397
5398/**
5399 * Query the VM that a guest property belongs to for the property.
5400 * @returns E_ACCESSDENIED if the VM process is not available or not
5401 * currently handling queries and the lookup should then be done in
5402 * VBoxSVC.
5403 */
5404HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5405 com::Utf8Str &aValue,
5406 LONG64 *aTimestamp,
5407 com::Utf8Str &aFlags) const
5408{
5409 HRESULT rc = S_OK;
5410 Bstr bstrValue;
5411 Bstr bstrFlags;
5412
5413 ComPtr<IInternalSessionControl> directControl;
5414 {
5415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5416 if (mData->mSession.mLockType == LockType_VM)
5417 directControl = mData->mSession.mDirectControl;
5418 }
5419
5420 /* ignore calls made after #OnSessionEnd() is called */
5421 if (!directControl)
5422 rc = E_ACCESSDENIED;
5423 else
5424 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5425 0 /* accessMode */,
5426 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5427
5428 aValue = bstrValue;
5429 aFlags = bstrFlags;
5430
5431 return rc;
5432}
5433#endif // VBOX_WITH_GUEST_PROPS
5434
5435HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5436 com::Utf8Str &aValue,
5437 LONG64 *aTimestamp,
5438 com::Utf8Str &aFlags)
5439{
5440#ifndef VBOX_WITH_GUEST_PROPS
5441 ReturnComNotImplemented();
5442#else // VBOX_WITH_GUEST_PROPS
5443
5444 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5445
5446 if (rc == E_ACCESSDENIED)
5447 /* The VM is not running or the service is not (yet) accessible */
5448 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5449 return rc;
5450#endif // VBOX_WITH_GUEST_PROPS
5451}
5452
5453HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5454{
5455 LONG64 dummyTimestamp;
5456 com::Utf8Str dummyFlags;
5457 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5458 return rc;
5459
5460}
5461HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5462{
5463 com::Utf8Str dummyFlags;
5464 com::Utf8Str dummyValue;
5465 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5466 return rc;
5467}
5468
5469#ifdef VBOX_WITH_GUEST_PROPS
5470/**
5471 * Set a guest property in VBoxSVC's internal structures.
5472 */
5473HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5474 const com::Utf8Str &aFlags, bool fDelete)
5475{
5476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5477 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5478 if (FAILED(rc)) return rc;
5479
5480 try
5481 {
5482 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5483 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5484 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5485
5486 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5487 if (it == mHWData->mGuestProperties.end())
5488 {
5489 if (!fDelete)
5490 {
5491 i_setModified(IsModified_MachineData);
5492 mHWData.backupEx();
5493
5494 RTTIMESPEC time;
5495 HWData::GuestProperty prop;
5496 prop.strValue = Bstr(aValue).raw();
5497 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5498 prop.mFlags = fFlags;
5499 mHWData->mGuestProperties[aName] = prop;
5500 }
5501 }
5502 else
5503 {
5504 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5505 {
5506 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5507 }
5508 else
5509 {
5510 i_setModified(IsModified_MachineData);
5511 mHWData.backupEx();
5512
5513 /* The backupEx() operation invalidates our iterator,
5514 * so get a new one. */
5515 it = mHWData->mGuestProperties.find(aName);
5516 Assert(it != mHWData->mGuestProperties.end());
5517
5518 if (!fDelete)
5519 {
5520 RTTIMESPEC time;
5521 it->second.strValue = aValue;
5522 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5523 it->second.mFlags = fFlags;
5524 }
5525 else
5526 mHWData->mGuestProperties.erase(it);
5527 }
5528 }
5529
5530 if (SUCCEEDED(rc))
5531 {
5532 alock.release();
5533
5534 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5535 }
5536 }
5537 catch (std::bad_alloc &)
5538 {
5539 rc = E_OUTOFMEMORY;
5540 }
5541
5542 return rc;
5543}
5544
5545/**
5546 * Set a property on the VM that that property belongs to.
5547 * @returns E_ACCESSDENIED if the VM process is not available or not
5548 * currently handling queries and the setting should then be done in
5549 * VBoxSVC.
5550 */
5551HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5552 const com::Utf8Str &aFlags, bool fDelete)
5553{
5554 HRESULT rc;
5555
5556 try
5557 {
5558 ComPtr<IInternalSessionControl> directControl;
5559 {
5560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5561 if (mData->mSession.mLockType == LockType_VM)
5562 directControl = mData->mSession.mDirectControl;
5563 }
5564
5565 Bstr dummy1; /* will not be changed (setter) */
5566 Bstr dummy2; /* will not be changed (setter) */
5567 LONG64 dummy64;
5568 if (!directControl)
5569 rc = E_ACCESSDENIED;
5570 else
5571 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5572 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5573 fDelete ? 2 : 1 /* accessMode */,
5574 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5575 }
5576 catch (std::bad_alloc &)
5577 {
5578 rc = E_OUTOFMEMORY;
5579 }
5580
5581 return rc;
5582}
5583#endif // VBOX_WITH_GUEST_PROPS
5584
5585HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5586 const com::Utf8Str &aFlags)
5587{
5588#ifndef VBOX_WITH_GUEST_PROPS
5589 ReturnComNotImplemented();
5590#else // VBOX_WITH_GUEST_PROPS
5591 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5592 if (rc == E_ACCESSDENIED)
5593 /* The VM is not running or the service is not (yet) accessible */
5594 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5595 return rc;
5596#endif // VBOX_WITH_GUEST_PROPS
5597}
5598
5599HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5600{
5601 return setGuestProperty(aProperty, aValue, "");
5602}
5603
5604HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5605{
5606#ifndef VBOX_WITH_GUEST_PROPS
5607 ReturnComNotImplemented();
5608#else // VBOX_WITH_GUEST_PROPS
5609 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5610 if (rc == E_ACCESSDENIED)
5611 /* The VM is not running or the service is not (yet) accessible */
5612 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5613 return rc;
5614#endif // VBOX_WITH_GUEST_PROPS
5615}
5616
5617#ifdef VBOX_WITH_GUEST_PROPS
5618/**
5619 * Enumerate the guest properties in VBoxSVC's internal structures.
5620 */
5621HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5622 std::vector<com::Utf8Str> &aNames,
5623 std::vector<com::Utf8Str> &aValues,
5624 std::vector<LONG64> &aTimestamps,
5625 std::vector<com::Utf8Str> &aFlags)
5626{
5627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5628 Utf8Str strPatterns(aPatterns);
5629
5630 /*
5631 * Look for matching patterns and build up a list.
5632 */
5633 HWData::GuestPropertyMap propMap;
5634 for (HWData::GuestPropertyMap::const_iterator
5635 it = mHWData->mGuestProperties.begin();
5636 it != mHWData->mGuestProperties.end();
5637 ++it)
5638 {
5639 if ( strPatterns.isEmpty()
5640 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5641 RTSTR_MAX,
5642 it->first.c_str(),
5643 RTSTR_MAX,
5644 NULL)
5645 )
5646 propMap.insert(*it);
5647 }
5648
5649 alock.release();
5650
5651 /*
5652 * And build up the arrays for returning the property information.
5653 */
5654 size_t cEntries = propMap.size();
5655
5656 aNames.resize(cEntries);
5657 aValues.resize(cEntries);
5658 aTimestamps.resize(cEntries);
5659 aFlags.resize(cEntries);
5660
5661 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5662 size_t i = 0;
5663 for (HWData::GuestPropertyMap::const_iterator
5664 it = propMap.begin();
5665 it != propMap.end();
5666 ++it, ++i)
5667 {
5668 aNames[i] = it->first;
5669 aValues[i] = it->second.strValue;
5670 aTimestamps[i] = it->second.mTimestamp;
5671 GuestPropWriteFlags(it->second.mFlags, szFlags);
5672 aFlags[i] = Utf8Str(szFlags);
5673 }
5674
5675 return S_OK;
5676}
5677
5678/**
5679 * Enumerate the properties managed by a VM.
5680 * @returns E_ACCESSDENIED if the VM process is not available or not
5681 * currently handling queries and the setting should then be done in
5682 * VBoxSVC.
5683 */
5684HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5685 std::vector<com::Utf8Str> &aNames,
5686 std::vector<com::Utf8Str> &aValues,
5687 std::vector<LONG64> &aTimestamps,
5688 std::vector<com::Utf8Str> &aFlags)
5689{
5690 HRESULT rc;
5691 ComPtr<IInternalSessionControl> directControl;
5692 {
5693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5694 if (mData->mSession.mLockType == LockType_VM)
5695 directControl = mData->mSession.mDirectControl;
5696 }
5697
5698 com::SafeArray<BSTR> bNames;
5699 com::SafeArray<BSTR> bValues;
5700 com::SafeArray<LONG64> bTimestamps;
5701 com::SafeArray<BSTR> bFlags;
5702
5703 if (!directControl)
5704 rc = E_ACCESSDENIED;
5705 else
5706 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5707 ComSafeArrayAsOutParam(bNames),
5708 ComSafeArrayAsOutParam(bValues),
5709 ComSafeArrayAsOutParam(bTimestamps),
5710 ComSafeArrayAsOutParam(bFlags));
5711 size_t i;
5712 aNames.resize(bNames.size());
5713 for (i = 0; i < bNames.size(); ++i)
5714 aNames[i] = Utf8Str(bNames[i]);
5715 aValues.resize(bValues.size());
5716 for (i = 0; i < bValues.size(); ++i)
5717 aValues[i] = Utf8Str(bValues[i]);
5718 aTimestamps.resize(bTimestamps.size());
5719 for (i = 0; i < bTimestamps.size(); ++i)
5720 aTimestamps[i] = bTimestamps[i];
5721 aFlags.resize(bFlags.size());
5722 for (i = 0; i < bFlags.size(); ++i)
5723 aFlags[i] = Utf8Str(bFlags[i]);
5724
5725 return rc;
5726}
5727#endif // VBOX_WITH_GUEST_PROPS
5728HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5729 std::vector<com::Utf8Str> &aNames,
5730 std::vector<com::Utf8Str> &aValues,
5731 std::vector<LONG64> &aTimestamps,
5732 std::vector<com::Utf8Str> &aFlags)
5733{
5734#ifndef VBOX_WITH_GUEST_PROPS
5735 ReturnComNotImplemented();
5736#else // VBOX_WITH_GUEST_PROPS
5737
5738 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5739
5740 if (rc == E_ACCESSDENIED)
5741 /* The VM is not running or the service is not (yet) accessible */
5742 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5743 return rc;
5744#endif // VBOX_WITH_GUEST_PROPS
5745}
5746
5747HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5748 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5749{
5750 MediumAttachmentList atts;
5751
5752 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5753 if (FAILED(rc)) return rc;
5754
5755 aMediumAttachments.resize(atts.size());
5756 size_t i = 0;
5757 for (MediumAttachmentList::const_iterator
5758 it = atts.begin();
5759 it != atts.end();
5760 ++it, ++i)
5761 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5762
5763 return S_OK;
5764}
5765
5766HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5767 LONG aControllerPort,
5768 LONG aDevice,
5769 ComPtr<IMediumAttachment> &aAttachment)
5770{
5771 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5772 aName.c_str(), aControllerPort, aDevice));
5773
5774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5775
5776 aAttachment = NULL;
5777
5778 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5779 aName,
5780 aControllerPort,
5781 aDevice);
5782 if (pAttach.isNull())
5783 return setError(VBOX_E_OBJECT_NOT_FOUND,
5784 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5785 aDevice, aControllerPort, aName.c_str());
5786
5787 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5788
5789 return S_OK;
5790}
5791
5792
5793HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5794 StorageBus_T aConnectionType,
5795 ComPtr<IStorageController> &aController)
5796{
5797 if ( (aConnectionType <= StorageBus_Null)
5798 || (aConnectionType > StorageBus_VirtioSCSI))
5799 return setError(E_INVALIDARG,
5800 tr("Invalid connection type: %d"),
5801 aConnectionType);
5802
5803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5804
5805 HRESULT rc = i_checkStateDependency(MutableStateDep);
5806 if (FAILED(rc)) return rc;
5807
5808 /* try to find one with the name first. */
5809 ComObjPtr<StorageController> ctrl;
5810
5811 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5812 if (SUCCEEDED(rc))
5813 return setError(VBOX_E_OBJECT_IN_USE,
5814 tr("Storage controller named '%s' already exists"),
5815 aName.c_str());
5816
5817 ctrl.createObject();
5818
5819 /* get a new instance number for the storage controller */
5820 ULONG ulInstance = 0;
5821 bool fBootable = true;
5822 for (StorageControllerList::const_iterator
5823 it = mStorageControllers->begin();
5824 it != mStorageControllers->end();
5825 ++it)
5826 {
5827 if ((*it)->i_getStorageBus() == aConnectionType)
5828 {
5829 ULONG ulCurInst = (*it)->i_getInstance();
5830
5831 if (ulCurInst >= ulInstance)
5832 ulInstance = ulCurInst + 1;
5833
5834 /* Only one controller of each type can be marked as bootable. */
5835 if ((*it)->i_getBootable())
5836 fBootable = false;
5837 }
5838 }
5839
5840 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5841 if (FAILED(rc)) return rc;
5842
5843 i_setModified(IsModified_Storage);
5844 mStorageControllers.backup();
5845 mStorageControllers->push_back(ctrl);
5846
5847 ctrl.queryInterfaceTo(aController.asOutParam());
5848
5849 /* inform the direct session if any */
5850 alock.release();
5851 i_onStorageControllerChange(i_getId(), aName);
5852
5853 return S_OK;
5854}
5855
5856HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5857 ComPtr<IStorageController> &aStorageController)
5858{
5859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5860
5861 ComObjPtr<StorageController> ctrl;
5862
5863 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5864 if (SUCCEEDED(rc))
5865 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5866
5867 return rc;
5868}
5869
5870HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5871 ULONG aInstance,
5872 ComPtr<IStorageController> &aStorageController)
5873{
5874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5875
5876 for (StorageControllerList::const_iterator
5877 it = mStorageControllers->begin();
5878 it != mStorageControllers->end();
5879 ++it)
5880 {
5881 if ( (*it)->i_getStorageBus() == aConnectionType
5882 && (*it)->i_getInstance() == aInstance)
5883 {
5884 (*it).queryInterfaceTo(aStorageController.asOutParam());
5885 return S_OK;
5886 }
5887 }
5888
5889 return setError(VBOX_E_OBJECT_NOT_FOUND,
5890 tr("Could not find a storage controller with instance number '%lu'"),
5891 aInstance);
5892}
5893
5894HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5895{
5896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5897
5898 HRESULT rc = i_checkStateDependency(MutableStateDep);
5899 if (FAILED(rc)) return rc;
5900
5901 ComObjPtr<StorageController> ctrl;
5902
5903 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5904 if (SUCCEEDED(rc))
5905 {
5906 /* Ensure that only one controller of each type is marked as bootable. */
5907 if (aBootable == TRUE)
5908 {
5909 for (StorageControllerList::const_iterator
5910 it = mStorageControllers->begin();
5911 it != mStorageControllers->end();
5912 ++it)
5913 {
5914 ComObjPtr<StorageController> aCtrl = (*it);
5915
5916 if ( (aCtrl->i_getName() != aName)
5917 && aCtrl->i_getBootable() == TRUE
5918 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5919 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5920 {
5921 aCtrl->i_setBootable(FALSE);
5922 break;
5923 }
5924 }
5925 }
5926
5927 if (SUCCEEDED(rc))
5928 {
5929 ctrl->i_setBootable(aBootable);
5930 i_setModified(IsModified_Storage);
5931 }
5932 }
5933
5934 if (SUCCEEDED(rc))
5935 {
5936 /* inform the direct session if any */
5937 alock.release();
5938 i_onStorageControllerChange(i_getId(), aName);
5939 }
5940
5941 return rc;
5942}
5943
5944HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5945{
5946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5947
5948 HRESULT rc = i_checkStateDependency(MutableStateDep);
5949 if (FAILED(rc)) return rc;
5950
5951 ComObjPtr<StorageController> ctrl;
5952 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5953 if (FAILED(rc)) return rc;
5954
5955 MediumAttachmentList llDetachedAttachments;
5956 {
5957 /* find all attached devices to the appropriate storage controller and detach them all */
5958 // make a temporary list because detachDevice invalidates iterators into
5959 // mMediumAttachments
5960 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5961
5962 for (MediumAttachmentList::const_iterator
5963 it = llAttachments2.begin();
5964 it != llAttachments2.end();
5965 ++it)
5966 {
5967 MediumAttachment *pAttachTemp = *it;
5968
5969 AutoCaller localAutoCaller(pAttachTemp);
5970 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5971
5972 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5973
5974 if (pAttachTemp->i_getControllerName() == aName)
5975 {
5976 llDetachedAttachments.push_back(pAttachTemp);
5977 rc = i_detachDevice(pAttachTemp, alock, NULL);
5978 if (FAILED(rc)) return rc;
5979 }
5980 }
5981 }
5982
5983 /* send event about detached devices before removing parent controller */
5984 for (MediumAttachmentList::const_iterator
5985 it = llDetachedAttachments.begin();
5986 it != llDetachedAttachments.end();
5987 ++it)
5988 {
5989 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5990 }
5991
5992 /* We can remove it now. */
5993 i_setModified(IsModified_Storage);
5994 mStorageControllers.backup();
5995
5996 ctrl->i_unshare();
5997
5998 mStorageControllers->remove(ctrl);
5999
6000 /* inform the direct session if any */
6001 alock.release();
6002 i_onStorageControllerChange(i_getId(), aName);
6003
6004 return S_OK;
6005}
6006
6007HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6008 ComPtr<IUSBController> &aController)
6009{
6010 if ( (aType <= USBControllerType_Null)
6011 || (aType >= USBControllerType_Last))
6012 return setError(E_INVALIDARG,
6013 tr("Invalid USB controller type: %d"),
6014 aType);
6015
6016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6017
6018 HRESULT rc = i_checkStateDependency(MutableStateDep);
6019 if (FAILED(rc)) return rc;
6020
6021 /* try to find one with the same type first. */
6022 ComObjPtr<USBController> ctrl;
6023
6024 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6025 if (SUCCEEDED(rc))
6026 return setError(VBOX_E_OBJECT_IN_USE,
6027 tr("USB controller named '%s' already exists"),
6028 aName.c_str());
6029
6030 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6031 ULONG maxInstances;
6032 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6033 if (FAILED(rc))
6034 return rc;
6035
6036 ULONG cInstances = i_getUSBControllerCountByType(aType);
6037 if (cInstances >= maxInstances)
6038 return setError(E_INVALIDARG,
6039 tr("Too many USB controllers of this type"));
6040
6041 ctrl.createObject();
6042
6043 rc = ctrl->init(this, aName, aType);
6044 if (FAILED(rc)) return rc;
6045
6046 i_setModified(IsModified_USB);
6047 mUSBControllers.backup();
6048 mUSBControllers->push_back(ctrl);
6049
6050 ctrl.queryInterfaceTo(aController.asOutParam());
6051
6052 /* inform the direct session if any */
6053 alock.release();
6054 i_onUSBControllerChange();
6055
6056 return S_OK;
6057}
6058
6059HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6060{
6061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6062
6063 ComObjPtr<USBController> ctrl;
6064
6065 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6066 if (SUCCEEDED(rc))
6067 ctrl.queryInterfaceTo(aController.asOutParam());
6068
6069 return rc;
6070}
6071
6072HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6073 ULONG *aControllers)
6074{
6075 if ( (aType <= USBControllerType_Null)
6076 || (aType >= USBControllerType_Last))
6077 return setError(E_INVALIDARG,
6078 tr("Invalid USB controller type: %d"),
6079 aType);
6080
6081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6082
6083 ComObjPtr<USBController> ctrl;
6084
6085 *aControllers = i_getUSBControllerCountByType(aType);
6086
6087 return S_OK;
6088}
6089
6090HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6091{
6092
6093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6094
6095 HRESULT rc = i_checkStateDependency(MutableStateDep);
6096 if (FAILED(rc)) return rc;
6097
6098 ComObjPtr<USBController> ctrl;
6099 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6100 if (FAILED(rc)) return rc;
6101
6102 i_setModified(IsModified_USB);
6103 mUSBControllers.backup();
6104
6105 ctrl->i_unshare();
6106
6107 mUSBControllers->remove(ctrl);
6108
6109 /* inform the direct session if any */
6110 alock.release();
6111 i_onUSBControllerChange();
6112
6113 return S_OK;
6114}
6115
6116HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6117 ULONG *aOriginX,
6118 ULONG *aOriginY,
6119 ULONG *aWidth,
6120 ULONG *aHeight,
6121 BOOL *aEnabled)
6122{
6123 uint32_t u32OriginX= 0;
6124 uint32_t u32OriginY= 0;
6125 uint32_t u32Width = 0;
6126 uint32_t u32Height = 0;
6127 uint16_t u16Flags = 0;
6128
6129 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6130 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6131 if (RT_FAILURE(vrc))
6132 {
6133#ifdef RT_OS_WINDOWS
6134 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6135 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6136 * So just assign fEnable to TRUE again.
6137 * The right fix would be to change GUI API wrappers to make sure that parameters
6138 * are changed only if API succeeds.
6139 */
6140 *aEnabled = TRUE;
6141#endif
6142 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6143 tr("Saved guest size is not available (%Rrc)"),
6144 vrc);
6145 }
6146
6147 *aOriginX = u32OriginX;
6148 *aOriginY = u32OriginY;
6149 *aWidth = u32Width;
6150 *aHeight = u32Height;
6151 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6152
6153 return S_OK;
6154}
6155
6156HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6157 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6158{
6159 if (aScreenId != 0)
6160 return E_NOTIMPL;
6161
6162 if ( aBitmapFormat != BitmapFormat_BGR0
6163 && aBitmapFormat != BitmapFormat_BGRA
6164 && aBitmapFormat != BitmapFormat_RGBA
6165 && aBitmapFormat != BitmapFormat_PNG)
6166 return setError(E_NOTIMPL,
6167 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6168
6169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6170
6171 uint8_t *pu8Data = NULL;
6172 uint32_t cbData = 0;
6173 uint32_t u32Width = 0;
6174 uint32_t u32Height = 0;
6175
6176 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6177
6178 if (RT_FAILURE(vrc))
6179 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6180 tr("Saved thumbnail data is not available (%Rrc)"),
6181 vrc);
6182
6183 HRESULT hr = S_OK;
6184
6185 *aWidth = u32Width;
6186 *aHeight = u32Height;
6187
6188 if (cbData > 0)
6189 {
6190 /* Convert pixels to the format expected by the API caller. */
6191 if (aBitmapFormat == BitmapFormat_BGR0)
6192 {
6193 /* [0] B, [1] G, [2] R, [3] 0. */
6194 aData.resize(cbData);
6195 memcpy(&aData.front(), pu8Data, cbData);
6196 }
6197 else if (aBitmapFormat == BitmapFormat_BGRA)
6198 {
6199 /* [0] B, [1] G, [2] R, [3] A. */
6200 aData.resize(cbData);
6201 for (uint32_t i = 0; i < cbData; i += 4)
6202 {
6203 aData[i] = pu8Data[i];
6204 aData[i + 1] = pu8Data[i + 1];
6205 aData[i + 2] = pu8Data[i + 2];
6206 aData[i + 3] = 0xff;
6207 }
6208 }
6209 else if (aBitmapFormat == BitmapFormat_RGBA)
6210 {
6211 /* [0] R, [1] G, [2] B, [3] A. */
6212 aData.resize(cbData);
6213 for (uint32_t i = 0; i < cbData; i += 4)
6214 {
6215 aData[i] = pu8Data[i + 2];
6216 aData[i + 1] = pu8Data[i + 1];
6217 aData[i + 2] = pu8Data[i];
6218 aData[i + 3] = 0xff;
6219 }
6220 }
6221 else if (aBitmapFormat == BitmapFormat_PNG)
6222 {
6223 uint8_t *pu8PNG = NULL;
6224 uint32_t cbPNG = 0;
6225 uint32_t cxPNG = 0;
6226 uint32_t cyPNG = 0;
6227
6228 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6229
6230 if (RT_SUCCESS(vrc))
6231 {
6232 aData.resize(cbPNG);
6233 if (cbPNG)
6234 memcpy(&aData.front(), pu8PNG, cbPNG);
6235 }
6236 else
6237 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6238 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6239 vrc);
6240
6241 RTMemFree(pu8PNG);
6242 }
6243 }
6244
6245 freeSavedDisplayScreenshot(pu8Data);
6246
6247 return hr;
6248}
6249
6250HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6251 ULONG *aWidth,
6252 ULONG *aHeight,
6253 std::vector<BitmapFormat_T> &aBitmapFormats)
6254{
6255 if (aScreenId != 0)
6256 return E_NOTIMPL;
6257
6258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6259
6260 uint8_t *pu8Data = NULL;
6261 uint32_t cbData = 0;
6262 uint32_t u32Width = 0;
6263 uint32_t u32Height = 0;
6264
6265 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6266
6267 if (RT_FAILURE(vrc))
6268 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6269 tr("Saved screenshot data is not available (%Rrc)"),
6270 vrc);
6271
6272 *aWidth = u32Width;
6273 *aHeight = u32Height;
6274 aBitmapFormats.resize(1);
6275 aBitmapFormats[0] = BitmapFormat_PNG;
6276
6277 freeSavedDisplayScreenshot(pu8Data);
6278
6279 return S_OK;
6280}
6281
6282HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6283 BitmapFormat_T aBitmapFormat,
6284 ULONG *aWidth,
6285 ULONG *aHeight,
6286 std::vector<BYTE> &aData)
6287{
6288 if (aScreenId != 0)
6289 return E_NOTIMPL;
6290
6291 if (aBitmapFormat != BitmapFormat_PNG)
6292 return E_NOTIMPL;
6293
6294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6295
6296 uint8_t *pu8Data = NULL;
6297 uint32_t cbData = 0;
6298 uint32_t u32Width = 0;
6299 uint32_t u32Height = 0;
6300
6301 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6302
6303 if (RT_FAILURE(vrc))
6304 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6305 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6306 vrc);
6307
6308 *aWidth = u32Width;
6309 *aHeight = u32Height;
6310
6311 aData.resize(cbData);
6312 if (cbData)
6313 memcpy(&aData.front(), pu8Data, cbData);
6314
6315 freeSavedDisplayScreenshot(pu8Data);
6316
6317 return S_OK;
6318}
6319
6320HRESULT Machine::hotPlugCPU(ULONG aCpu)
6321{
6322 HRESULT rc = S_OK;
6323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 if (!mHWData->mCPUHotPlugEnabled)
6326 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6327
6328 if (aCpu >= mHWData->mCPUCount)
6329 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6330
6331 if (mHWData->mCPUAttached[aCpu])
6332 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6333
6334 alock.release();
6335 rc = i_onCPUChange(aCpu, false);
6336 alock.acquire();
6337 if (FAILED(rc)) return rc;
6338
6339 i_setModified(IsModified_MachineData);
6340 mHWData.backup();
6341 mHWData->mCPUAttached[aCpu] = true;
6342
6343 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6344 if (Global::IsOnline(mData->mMachineState))
6345 i_saveSettings(NULL);
6346
6347 return S_OK;
6348}
6349
6350HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6351{
6352 HRESULT rc = S_OK;
6353
6354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6355
6356 if (!mHWData->mCPUHotPlugEnabled)
6357 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6358
6359 if (aCpu >= SchemaDefs::MaxCPUCount)
6360 return setError(E_INVALIDARG,
6361 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6362 SchemaDefs::MaxCPUCount);
6363
6364 if (!mHWData->mCPUAttached[aCpu])
6365 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6366
6367 /* CPU 0 can't be detached */
6368 if (aCpu == 0)
6369 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6370
6371 alock.release();
6372 rc = i_onCPUChange(aCpu, true);
6373 alock.acquire();
6374 if (FAILED(rc)) return rc;
6375
6376 i_setModified(IsModified_MachineData);
6377 mHWData.backup();
6378 mHWData->mCPUAttached[aCpu] = false;
6379
6380 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6381 if (Global::IsOnline(mData->mMachineState))
6382 i_saveSettings(NULL);
6383
6384 return S_OK;
6385}
6386
6387HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6388{
6389 *aAttached = false;
6390
6391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6392
6393 /* If hotplug is enabled the CPU is always enabled. */
6394 if (!mHWData->mCPUHotPlugEnabled)
6395 {
6396 if (aCpu < mHWData->mCPUCount)
6397 *aAttached = true;
6398 }
6399 else
6400 {
6401 if (aCpu < SchemaDefs::MaxCPUCount)
6402 *aAttached = mHWData->mCPUAttached[aCpu];
6403 }
6404
6405 return S_OK;
6406}
6407
6408HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6409{
6410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6411
6412 Utf8Str log = i_getLogFilename(aIdx);
6413 if (!RTFileExists(log.c_str()))
6414 log.setNull();
6415 aFilename = log;
6416
6417 return S_OK;
6418}
6419
6420HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6421{
6422 if (aSize < 0)
6423 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6424
6425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6426
6427 HRESULT rc = S_OK;
6428 Utf8Str log = i_getLogFilename(aIdx);
6429
6430 /* do not unnecessarily hold the lock while doing something which does
6431 * not need the lock and potentially takes a long time. */
6432 alock.release();
6433
6434 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6435 * keeps the SOAP reply size under 1M for the webservice (we're using
6436 * base64 encoded strings for binary data for years now, avoiding the
6437 * expansion of each byte array element to approx. 25 bytes of XML. */
6438 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6439 aData.resize(cbData);
6440
6441 RTFILE LogFile;
6442 int vrc = RTFileOpen(&LogFile, log.c_str(),
6443 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6444 if (RT_SUCCESS(vrc))
6445 {
6446 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6447 if (RT_SUCCESS(vrc))
6448 aData.resize(cbData);
6449 else
6450 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6451 tr("Could not read log file '%s' (%Rrc)"),
6452 log.c_str(), vrc);
6453 RTFileClose(LogFile);
6454 }
6455 else
6456 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6457 tr("Could not open log file '%s' (%Rrc)"),
6458 log.c_str(), vrc);
6459
6460 if (FAILED(rc))
6461 aData.resize(0);
6462
6463 return rc;
6464}
6465
6466
6467/**
6468 * Currently this method doesn't attach device to the running VM,
6469 * just makes sure it's plugged on next VM start.
6470 */
6471HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6472{
6473 // lock scope
6474 {
6475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6476
6477 HRESULT rc = i_checkStateDependency(MutableStateDep);
6478 if (FAILED(rc)) return rc;
6479
6480 ChipsetType_T aChipset = ChipsetType_PIIX3;
6481 COMGETTER(ChipsetType)(&aChipset);
6482
6483 if (aChipset != ChipsetType_ICH9)
6484 {
6485 return setError(E_INVALIDARG,
6486 tr("Host PCI attachment only supported with ICH9 chipset"));
6487 }
6488
6489 // check if device with this host PCI address already attached
6490 for (HWData::PCIDeviceAssignmentList::const_iterator
6491 it = mHWData->mPCIDeviceAssignments.begin();
6492 it != mHWData->mPCIDeviceAssignments.end();
6493 ++it)
6494 {
6495 LONG iHostAddress = -1;
6496 ComPtr<PCIDeviceAttachment> pAttach;
6497 pAttach = *it;
6498 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6499 if (iHostAddress == aHostAddress)
6500 return setError(E_INVALIDARG,
6501 tr("Device with host PCI address already attached to this VM"));
6502 }
6503
6504 ComObjPtr<PCIDeviceAttachment> pda;
6505 char name[32];
6506
6507 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6508 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6509 pda.createObject();
6510 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6511 i_setModified(IsModified_MachineData);
6512 mHWData.backup();
6513 mHWData->mPCIDeviceAssignments.push_back(pda);
6514 }
6515
6516 return S_OK;
6517}
6518
6519/**
6520 * Currently this method doesn't detach device from the running VM,
6521 * just makes sure it's not plugged on next VM start.
6522 */
6523HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6524{
6525 ComObjPtr<PCIDeviceAttachment> pAttach;
6526 bool fRemoved = false;
6527 HRESULT rc;
6528
6529 // lock scope
6530 {
6531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6532
6533 rc = i_checkStateDependency(MutableStateDep);
6534 if (FAILED(rc)) return rc;
6535
6536 for (HWData::PCIDeviceAssignmentList::const_iterator
6537 it = mHWData->mPCIDeviceAssignments.begin();
6538 it != mHWData->mPCIDeviceAssignments.end();
6539 ++it)
6540 {
6541 LONG iHostAddress = -1;
6542 pAttach = *it;
6543 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6544 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6545 {
6546 i_setModified(IsModified_MachineData);
6547 mHWData.backup();
6548 mHWData->mPCIDeviceAssignments.remove(pAttach);
6549 fRemoved = true;
6550 break;
6551 }
6552 }
6553 }
6554
6555
6556 /* Fire event outside of the lock */
6557 if (fRemoved)
6558 {
6559 Assert(!pAttach.isNull());
6560 ComPtr<IEventSource> es;
6561 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6562 Assert(SUCCEEDED(rc));
6563 Bstr mid;
6564 rc = this->COMGETTER(Id)(mid.asOutParam());
6565 Assert(SUCCEEDED(rc));
6566 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6567 }
6568
6569 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6570 tr("No host PCI device %08x attached"),
6571 aHostAddress
6572 );
6573}
6574
6575HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6576{
6577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6578
6579 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6580 size_t i = 0;
6581 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6582 it = mHWData->mPCIDeviceAssignments.begin();
6583 it != mHWData->mPCIDeviceAssignments.end();
6584 ++it, ++i)
6585 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6586
6587 return S_OK;
6588}
6589
6590HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6591{
6592 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6593
6594 return S_OK;
6595}
6596
6597HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6598{
6599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6600
6601 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6602
6603 return S_OK;
6604}
6605
6606HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6607{
6608 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6609 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6610 if (SUCCEEDED(hrc))
6611 {
6612 hrc = mHWData.backupEx();
6613 if (SUCCEEDED(hrc))
6614 {
6615 i_setModified(IsModified_MachineData);
6616 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6617 }
6618 }
6619 return hrc;
6620}
6621
6622HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6623{
6624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6625 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6626 return S_OK;
6627}
6628
6629HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6630{
6631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6632 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6633 if (SUCCEEDED(hrc))
6634 {
6635 hrc = mHWData.backupEx();
6636 if (SUCCEEDED(hrc))
6637 {
6638 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6639 if (SUCCEEDED(hrc))
6640 i_setModified(IsModified_MachineData);
6641 }
6642 }
6643 return hrc;
6644}
6645
6646HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6647{
6648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6649
6650 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6651
6652 return S_OK;
6653}
6654
6655HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6656{
6657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6658 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6659 if (SUCCEEDED(hrc))
6660 {
6661 hrc = mHWData.backupEx();
6662 if (SUCCEEDED(hrc))
6663 {
6664 i_setModified(IsModified_MachineData);
6665 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6666 }
6667 }
6668 return hrc;
6669}
6670
6671HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6672{
6673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6674
6675 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6676
6677 return S_OK;
6678}
6679
6680HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6681{
6682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6685 if ( SUCCEEDED(hrc)
6686 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6687 {
6688 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6689 int vrc;
6690
6691 if (aAutostartEnabled)
6692 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6693 else
6694 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6695
6696 if (RT_SUCCESS(vrc))
6697 {
6698 hrc = mHWData.backupEx();
6699 if (SUCCEEDED(hrc))
6700 {
6701 i_setModified(IsModified_MachineData);
6702 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6703 }
6704 }
6705 else if (vrc == VERR_NOT_SUPPORTED)
6706 hrc = setError(VBOX_E_NOT_SUPPORTED,
6707 tr("The VM autostart feature is not supported on this platform"));
6708 else if (vrc == VERR_PATH_NOT_FOUND)
6709 hrc = setError(E_FAIL,
6710 tr("The path to the autostart database is not set"));
6711 else
6712 hrc = setError(E_UNEXPECTED,
6713 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6714 aAutostartEnabled ? "Adding" : "Removing",
6715 mUserData->s.strName.c_str(), vrc);
6716 }
6717 return hrc;
6718}
6719
6720HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6721{
6722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6723
6724 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6725
6726 return S_OK;
6727}
6728
6729HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6730{
6731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6732 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6733 if (SUCCEEDED(hrc))
6734 {
6735 hrc = mHWData.backupEx();
6736 if (SUCCEEDED(hrc))
6737 {
6738 i_setModified(IsModified_MachineData);
6739 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6740 }
6741 }
6742 return hrc;
6743}
6744
6745HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6746{
6747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6748
6749 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6750
6751 return S_OK;
6752}
6753
6754HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6755{
6756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6757 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6758 if ( SUCCEEDED(hrc)
6759 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6760 {
6761 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6762 int vrc;
6763
6764 if (aAutostopType != AutostopType_Disabled)
6765 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6766 else
6767 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6768
6769 if (RT_SUCCESS(vrc))
6770 {
6771 hrc = mHWData.backupEx();
6772 if (SUCCEEDED(hrc))
6773 {
6774 i_setModified(IsModified_MachineData);
6775 mHWData->mAutostart.enmAutostopType = aAutostopType;
6776 }
6777 }
6778 else if (vrc == VERR_NOT_SUPPORTED)
6779 hrc = setError(VBOX_E_NOT_SUPPORTED,
6780 tr("The VM autostop feature is not supported on this platform"));
6781 else if (vrc == VERR_PATH_NOT_FOUND)
6782 hrc = setError(E_FAIL,
6783 tr("The path to the autostart database is not set"));
6784 else
6785 hrc = setError(E_UNEXPECTED,
6786 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6787 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6788 mUserData->s.strName.c_str(), vrc);
6789 }
6790 return hrc;
6791}
6792
6793HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6794{
6795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6796
6797 aDefaultFrontend = mHWData->mDefaultFrontend;
6798
6799 return S_OK;
6800}
6801
6802HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6803{
6804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6805 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6806 if (SUCCEEDED(hrc))
6807 {
6808 hrc = mHWData.backupEx();
6809 if (SUCCEEDED(hrc))
6810 {
6811 i_setModified(IsModified_MachineData);
6812 mHWData->mDefaultFrontend = aDefaultFrontend;
6813 }
6814 }
6815 return hrc;
6816}
6817
6818HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6819{
6820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6821 size_t cbIcon = mUserData->s.ovIcon.size();
6822 aIcon.resize(cbIcon);
6823 if (cbIcon)
6824 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6825 return S_OK;
6826}
6827
6828HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6829{
6830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6831 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6832 if (SUCCEEDED(hrc))
6833 {
6834 i_setModified(IsModified_MachineData);
6835 mUserData.backup();
6836 size_t cbIcon = aIcon.size();
6837 mUserData->s.ovIcon.resize(cbIcon);
6838 if (cbIcon)
6839 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6840 }
6841 return hrc;
6842}
6843
6844HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6845{
6846#ifdef VBOX_WITH_USB
6847 *aUSBProxyAvailable = true;
6848#else
6849 *aUSBProxyAvailable = false;
6850#endif
6851 return S_OK;
6852}
6853
6854HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6855{
6856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6857
6858 *aVMProcessPriority = mUserData->s.enmVMPriority;
6859
6860 return S_OK;
6861}
6862
6863HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6864{
6865 RT_NOREF(aVMProcessPriority);
6866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6867 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6868 if (SUCCEEDED(hrc))
6869 {
6870 hrc = mUserData.backupEx();
6871 if (SUCCEEDED(hrc))
6872 {
6873 i_setModified(IsModified_MachineData);
6874 mUserData->s.enmVMPriority = aVMProcessPriority;
6875 }
6876 }
6877 alock.release();
6878 if (SUCCEEDED(hrc))
6879 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6880 return hrc;
6881}
6882
6883HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6884 ComPtr<IProgress> &aProgress)
6885{
6886 ComObjPtr<Progress> pP;
6887 Progress *ppP = pP;
6888 IProgress *iP = static_cast<IProgress *>(ppP);
6889 IProgress **pProgress = &iP;
6890
6891 IMachine *pTarget = aTarget;
6892
6893 /* Convert the options. */
6894 RTCList<CloneOptions_T> optList;
6895 if (aOptions.size())
6896 for (size_t i = 0; i < aOptions.size(); ++i)
6897 optList.append(aOptions[i]);
6898
6899 if (optList.contains(CloneOptions_Link))
6900 {
6901 if (!i_isSnapshotMachine())
6902 return setError(E_INVALIDARG,
6903 tr("Linked clone can only be created from a snapshot"));
6904 if (aMode != CloneMode_MachineState)
6905 return setError(E_INVALIDARG,
6906 tr("Linked clone can only be created for a single machine state"));
6907 }
6908 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6909
6910 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6911
6912 HRESULT rc = pWorker->start(pProgress);
6913
6914 pP = static_cast<Progress *>(*pProgress);
6915 pP.queryInterfaceTo(aProgress.asOutParam());
6916
6917 return rc;
6918
6919}
6920
6921HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6922 const com::Utf8Str &aType,
6923 ComPtr<IProgress> &aProgress)
6924{
6925 LogFlowThisFuncEnter();
6926
6927 ComObjPtr<Progress> ptrProgress;
6928 HRESULT hrc = ptrProgress.createObject();
6929 if (SUCCEEDED(hrc))
6930 {
6931 /* Initialize our worker task */
6932 MachineMoveVM *pTask = NULL;
6933 try
6934 {
6935 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6936 }
6937 catch (std::bad_alloc &)
6938 {
6939 return E_OUTOFMEMORY;
6940 }
6941
6942 hrc = pTask->init();//no exceptions are thrown
6943
6944 if (SUCCEEDED(hrc))
6945 {
6946 hrc = pTask->createThread();
6947 pTask = NULL; /* Consumed by createThread(). */
6948 if (SUCCEEDED(hrc))
6949 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6950 else
6951 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6952 }
6953 else
6954 delete pTask;
6955 }
6956
6957 LogFlowThisFuncLeave();
6958 return hrc;
6959
6960}
6961
6962HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6963{
6964 NOREF(aProgress);
6965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6966
6967 // This check should always fail.
6968 HRESULT rc = i_checkStateDependency(MutableStateDep);
6969 if (FAILED(rc)) return rc;
6970
6971 AssertFailedReturn(E_NOTIMPL);
6972}
6973
6974HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6975{
6976 NOREF(aSavedStateFile);
6977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6978
6979 // This check should always fail.
6980 HRESULT rc = i_checkStateDependency(MutableStateDep);
6981 if (FAILED(rc)) return rc;
6982
6983 AssertFailedReturn(E_NOTIMPL);
6984}
6985
6986HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6987{
6988 NOREF(aFRemoveFile);
6989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6990
6991 // This check should always fail.
6992 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
6993 if (FAILED(rc)) return rc;
6994
6995 AssertFailedReturn(E_NOTIMPL);
6996}
6997
6998// public methods for internal purposes
6999/////////////////////////////////////////////////////////////////////////////
7000
7001/**
7002 * Adds the given IsModified_* flag to the dirty flags of the machine.
7003 * This must be called either during i_loadSettings or under the machine write lock.
7004 * @param fl Flag
7005 * @param fAllowStateModification If state modifications are allowed.
7006 */
7007void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7008{
7009 mData->flModifications |= fl;
7010 if (fAllowStateModification && i_isStateModificationAllowed())
7011 mData->mCurrentStateModified = true;
7012}
7013
7014/**
7015 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7016 * care of the write locking.
7017 *
7018 * @param fModification The flag to add.
7019 * @param fAllowStateModification If state modifications are allowed.
7020 */
7021void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7022{
7023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7024 i_setModified(fModification, fAllowStateModification);
7025}
7026
7027/**
7028 * Saves the registry entry of this machine to the given configuration node.
7029 *
7030 * @param data Machine registry data.
7031 *
7032 * @note locks this object for reading.
7033 */
7034HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7035{
7036 AutoLimitedCaller autoCaller(this);
7037 AssertComRCReturnRC(autoCaller.rc());
7038
7039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7040
7041 data.uuid = mData->mUuid;
7042 data.strSettingsFile = mData->m_strConfigFile;
7043
7044 return S_OK;
7045}
7046
7047/**
7048 * Calculates the absolute path of the given path taking the directory of the
7049 * machine settings file as the current directory.
7050 *
7051 * @param strPath Path to calculate the absolute path for.
7052 * @param aResult Where to put the result (used only on success, can be the
7053 * same Utf8Str instance as passed in @a aPath).
7054 * @return IPRT result.
7055 *
7056 * @note Locks this object for reading.
7057 */
7058int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7059{
7060 AutoCaller autoCaller(this);
7061 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7062
7063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7064
7065 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7066
7067 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7068
7069 strSettingsDir.stripFilename();
7070 char szFolder[RTPATH_MAX];
7071 size_t cbFolder = sizeof(szFolder);
7072 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7073 if (RT_SUCCESS(vrc))
7074 aResult = szFolder;
7075
7076 return vrc;
7077}
7078
7079/**
7080 * Copies strSource to strTarget, making it relative to the machine folder
7081 * if it is a subdirectory thereof, or simply copying it otherwise.
7082 *
7083 * @param strSource Path to evaluate and copy.
7084 * @param strTarget Buffer to receive target path.
7085 *
7086 * @note Locks this object for reading.
7087 */
7088void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7089 Utf8Str &strTarget)
7090{
7091 AutoCaller autoCaller(this);
7092 AssertComRCReturn(autoCaller.rc(), (void)0);
7093
7094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7095
7096 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7097 // use strTarget as a temporary buffer to hold the machine settings dir
7098 strTarget = mData->m_strConfigFileFull;
7099 strTarget.stripFilename();
7100 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7101 {
7102 // is relative: then append what's left
7103 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7104 // for empty paths (only possible for subdirs) use "." to avoid
7105 // triggering default settings for not present config attributes.
7106 if (strTarget.isEmpty())
7107 strTarget = ".";
7108 }
7109 else
7110 // is not relative: then overwrite
7111 strTarget = strSource;
7112}
7113
7114/**
7115 * Returns the full path to the machine's log folder in the
7116 * \a aLogFolder argument.
7117 */
7118void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7119{
7120 AutoCaller autoCaller(this);
7121 AssertComRCReturnVoid(autoCaller.rc());
7122
7123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7124
7125 char szTmp[RTPATH_MAX];
7126 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7127 if (RT_SUCCESS(vrc))
7128 {
7129 if (szTmp[0] && !mUserData.isNull())
7130 {
7131 char szTmp2[RTPATH_MAX];
7132 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7133 if (RT_SUCCESS(vrc))
7134 aLogFolder.printf("%s%c%s",
7135 szTmp2,
7136 RTPATH_DELIMITER,
7137 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7138 }
7139 else
7140 vrc = VERR_PATH_IS_RELATIVE;
7141 }
7142
7143 if (RT_FAILURE(vrc))
7144 {
7145 // fallback if VBOX_USER_LOGHOME is not set or invalid
7146 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7147 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7148 aLogFolder.append(RTPATH_DELIMITER);
7149 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7150 }
7151}
7152
7153/**
7154 * Returns the full path to the machine's log file for an given index.
7155 */
7156Utf8Str Machine::i_getLogFilename(ULONG idx)
7157{
7158 Utf8Str logFolder;
7159 getLogFolder(logFolder);
7160 Assert(logFolder.length());
7161
7162 Utf8Str log;
7163 if (idx == 0)
7164 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7165#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7166 else if (idx == 1)
7167 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7168 else
7169 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7170#else
7171 else
7172 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7173#endif
7174 return log;
7175}
7176
7177/**
7178 * Returns the full path to the machine's hardened log file.
7179 */
7180Utf8Str Machine::i_getHardeningLogFilename(void)
7181{
7182 Utf8Str strFilename;
7183 getLogFolder(strFilename);
7184 Assert(strFilename.length());
7185 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7186 return strFilename;
7187}
7188
7189/**
7190 * Returns the default NVRAM filename based on the location of the VM config.
7191 * Note that this is a relative path.
7192 */
7193Utf8Str Machine::i_getDefaultNVRAMFilename()
7194{
7195 AutoCaller autoCaller(this);
7196 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7197
7198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7199
7200 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7201 || i_isSnapshotMachine())
7202 return Utf8Str::Empty;
7203
7204 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7205 strNVRAMFilePath.stripPath();
7206 strNVRAMFilePath.stripSuffix();
7207 strNVRAMFilePath += ".nvram";
7208
7209 return strNVRAMFilePath;
7210}
7211
7212/**
7213 * Returns the NVRAM filename for a new snapshot. This intentionally works
7214 * similarly to the saved state file naming. Note that this is usually
7215 * a relative path, unless the snapshot folder is absolute.
7216 */
7217Utf8Str Machine::i_getSnapshotNVRAMFilename()
7218{
7219 AutoCaller autoCaller(this);
7220 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7221
7222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7223
7224 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7225 return Utf8Str::Empty;
7226
7227 RTTIMESPEC ts;
7228 RTTimeNow(&ts);
7229 RTTIME time;
7230 RTTimeExplode(&time, &ts);
7231
7232 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7233 strNVRAMFilePath += RTPATH_DELIMITER;
7234 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7235 time.i32Year, time.u8Month, time.u8MonthDay,
7236 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7237
7238 return strNVRAMFilePath;
7239}
7240
7241/**
7242 * Composes a unique saved state filename based on the current system time. The filename is
7243 * granular to the second so this will work so long as no more than one snapshot is taken on
7244 * a machine per second.
7245 *
7246 * Before version 4.1, we used this formula for saved state files:
7247 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7248 * which no longer works because saved state files can now be shared between the saved state of the
7249 * "saved" machine and an online snapshot, and the following would cause problems:
7250 * 1) save machine
7251 * 2) create online snapshot from that machine state --> reusing saved state file
7252 * 3) save machine again --> filename would be reused, breaking the online snapshot
7253 *
7254 * So instead we now use a timestamp.
7255 *
7256 * @param strStateFilePath
7257 */
7258
7259void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7260{
7261 AutoCaller autoCaller(this);
7262 AssertComRCReturnVoid(autoCaller.rc());
7263
7264 {
7265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7266 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7267 }
7268
7269 RTTIMESPEC ts;
7270 RTTimeNow(&ts);
7271 RTTIME time;
7272 RTTimeExplode(&time, &ts);
7273
7274 strStateFilePath += RTPATH_DELIMITER;
7275 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7276 time.i32Year, time.u8Month, time.u8MonthDay,
7277 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7278}
7279
7280/**
7281 * Returns whether at least one USB controller is present for the VM.
7282 */
7283bool Machine::i_isUSBControllerPresent()
7284{
7285 AutoCaller autoCaller(this);
7286 AssertComRCReturn(autoCaller.rc(), false);
7287
7288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7289
7290 return (mUSBControllers->size() > 0);
7291}
7292
7293
7294/**
7295 * @note Locks this object for writing, calls the client process
7296 * (inside the lock).
7297 */
7298HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7299 const Utf8Str &strFrontend,
7300 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7301 ProgressProxy *aProgress)
7302{
7303 LogFlowThisFuncEnter();
7304
7305 AssertReturn(aControl, E_FAIL);
7306 AssertReturn(aProgress, E_FAIL);
7307 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7308
7309 AutoCaller autoCaller(this);
7310 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7311
7312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7313
7314 if (!mData->mRegistered)
7315 return setError(E_UNEXPECTED,
7316 tr("The machine '%s' is not registered"),
7317 mUserData->s.strName.c_str());
7318
7319 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7320
7321 /* The process started when launching a VM with separate UI/VM processes is always
7322 * the UI process, i.e. needs special handling as it won't claim the session. */
7323 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7324
7325 if (fSeparate)
7326 {
7327 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7328 return setError(VBOX_E_INVALID_OBJECT_STATE,
7329 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7330 mUserData->s.strName.c_str());
7331 }
7332 else
7333 {
7334 if ( mData->mSession.mState == SessionState_Locked
7335 || mData->mSession.mState == SessionState_Spawning
7336 || mData->mSession.mState == SessionState_Unlocking)
7337 return setError(VBOX_E_INVALID_OBJECT_STATE,
7338 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7339 mUserData->s.strName.c_str());
7340
7341 /* may not be busy */
7342 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7343 }
7344
7345 /* Hardening logging */
7346#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7347 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7348 {
7349 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7350 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7351 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7352 {
7353 Utf8Str strStartupLogDir = strHardeningLogFile;
7354 strStartupLogDir.stripFilename();
7355 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7356 file without stripping the file. */
7357 }
7358 strSupHardeningLogArg.append(strHardeningLogFile);
7359
7360 /* Remove legacy log filename to avoid confusion. */
7361 Utf8Str strOldStartupLogFile;
7362 getLogFolder(strOldStartupLogFile);
7363 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7364 RTFileDelete(strOldStartupLogFile.c_str());
7365 }
7366#else
7367 Utf8Str strSupHardeningLogArg;
7368#endif
7369
7370 Utf8Str strAppOverride;
7371#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7372 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7373#endif
7374
7375 bool fUseVBoxSDS = false;
7376 Utf8Str strCanonicalName;
7377 if (false)
7378 { }
7379#ifdef VBOX_WITH_QTGUI
7380 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7381 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7382 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7383 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7384 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7385 {
7386 strCanonicalName = "GUI/Qt";
7387 fUseVBoxSDS = true;
7388 }
7389#endif
7390#ifdef VBOX_WITH_VBOXSDL
7391 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7392 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7393 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7394 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7395 {
7396 strCanonicalName = "GUI/SDL";
7397 fUseVBoxSDS = true;
7398 }
7399#endif
7400#ifdef VBOX_WITH_HEADLESS
7401 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7402 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7403 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7404 {
7405 strCanonicalName = "headless";
7406 }
7407#endif
7408 else
7409 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7410
7411 Utf8Str idStr = mData->mUuid.toString();
7412 Utf8Str const &strMachineName = mUserData->s.strName;
7413 RTPROCESS pid = NIL_RTPROCESS;
7414
7415#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7416 RT_NOREF(fUseVBoxSDS);
7417#else
7418 DWORD idCallerSession = ~(DWORD)0;
7419 if (fUseVBoxSDS)
7420 {
7421 /*
7422 * The VBoxSDS should be used for process launching the VM with
7423 * GUI only if the caller and the VBoxSDS are in different Windows
7424 * sessions and the caller in the interactive one.
7425 */
7426 fUseVBoxSDS = false;
7427
7428 /* Get windows session of the current process. The process token used
7429 due to several reasons:
7430 1. The token is absent for the current thread except someone set it
7431 for us.
7432 2. Needs to get the id of the session where the process is started.
7433 We only need to do this once, though. */
7434 static DWORD s_idCurrentSession = ~(DWORD)0;
7435 DWORD idCurrentSession = s_idCurrentSession;
7436 if (idCurrentSession == ~(DWORD)0)
7437 {
7438 HANDLE hCurrentProcessToken = NULL;
7439 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7440 {
7441 DWORD cbIgn = 0;
7442 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7443 s_idCurrentSession = idCurrentSession;
7444 else
7445 {
7446 idCurrentSession = ~(DWORD)0;
7447 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7448 }
7449 CloseHandle(hCurrentProcessToken);
7450 }
7451 else
7452 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7453 }
7454
7455 /* get the caller's session */
7456 HRESULT hrc = CoImpersonateClient();
7457 if (SUCCEEDED(hrc))
7458 {
7459 HANDLE hCallerThreadToken;
7460 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7461 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7462 &hCallerThreadToken))
7463 {
7464 SetLastError(NO_ERROR);
7465 DWORD cbIgn = 0;
7466 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7467 {
7468 /* Only need to use SDS if the session ID differs: */
7469 if (idCurrentSession != idCallerSession)
7470 {
7471 fUseVBoxSDS = false;
7472
7473 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7474 DWORD cbTokenGroups = 0;
7475 PTOKEN_GROUPS pTokenGroups = NULL;
7476 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7477 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7478 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7479 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7480 {
7481 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7482 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7483 PSID pInteractiveSid = NULL;
7484 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7485 {
7486 /* Iterate over the groups looking for the interactive SID: */
7487 fUseVBoxSDS = false;
7488 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7489 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7490 {
7491 fUseVBoxSDS = true;
7492 break;
7493 }
7494 FreeSid(pInteractiveSid);
7495 }
7496 }
7497 else
7498 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7499 RTMemTmpFree(pTokenGroups);
7500 }
7501 }
7502 else
7503 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7504 CloseHandle(hCallerThreadToken);
7505 }
7506 else
7507 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7508 CoRevertToSelf();
7509 }
7510 else
7511 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7512 }
7513 if (fUseVBoxSDS)
7514 {
7515 /* connect to VBoxSDS */
7516 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7517 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7518 if (FAILED(rc))
7519 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7520 strMachineName.c_str());
7521
7522 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7523 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7524 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7525 service to access the files. */
7526 rc = CoSetProxyBlanket(pVBoxSDS,
7527 RPC_C_AUTHN_DEFAULT,
7528 RPC_C_AUTHZ_DEFAULT,
7529 COLE_DEFAULT_PRINCIPAL,
7530 RPC_C_AUTHN_LEVEL_DEFAULT,
7531 RPC_C_IMP_LEVEL_IMPERSONATE,
7532 NULL,
7533 EOAC_DEFAULT);
7534 if (FAILED(rc))
7535 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7536
7537 size_t const cEnvVars = aEnvironmentChanges.size();
7538 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7539 for (size_t i = 0; i < cEnvVars; i++)
7540 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7541
7542 ULONG uPid = 0;
7543 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7544 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7545 idCallerSession, &uPid);
7546 if (FAILED(rc))
7547 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7548 pid = (RTPROCESS)uPid;
7549 }
7550 else
7551#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7552 {
7553 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7554 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7555 if (RT_FAILURE(vrc))
7556 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7557 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7558 }
7559
7560 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7561 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7562
7563 if (!fSeparate)
7564 {
7565 /*
7566 * Note that we don't release the lock here before calling the client,
7567 * because it doesn't need to call us back if called with a NULL argument.
7568 * Releasing the lock here is dangerous because we didn't prepare the
7569 * launch data yet, but the client we've just started may happen to be
7570 * too fast and call LockMachine() that will fail (because of PID, etc.),
7571 * so that the Machine will never get out of the Spawning session state.
7572 */
7573
7574 /* inform the session that it will be a remote one */
7575 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7576#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7577 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7578#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7579 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7580#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7581 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7582
7583 if (FAILED(rc))
7584 {
7585 /* restore the session state */
7586 mData->mSession.mState = SessionState_Unlocked;
7587 alock.release();
7588 mParent->i_addProcessToReap(pid);
7589 /* The failure may occur w/o any error info (from RPC), so provide one */
7590 return setError(VBOX_E_VM_ERROR,
7591 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7592 }
7593
7594 /* attach launch data to the machine */
7595 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7596 mData->mSession.mRemoteControls.push_back(aControl);
7597 mData->mSession.mProgress = aProgress;
7598 mData->mSession.mPID = pid;
7599 mData->mSession.mState = SessionState_Spawning;
7600 Assert(strCanonicalName.isNotEmpty());
7601 mData->mSession.mName = strCanonicalName;
7602 }
7603 else
7604 {
7605 /* For separate UI process we declare the launch as completed instantly, as the
7606 * actual headless VM start may or may not come. No point in remembering anything
7607 * yet, as what matters for us is when the headless VM gets started. */
7608 aProgress->i_notifyComplete(S_OK);
7609 }
7610
7611 alock.release();
7612 mParent->i_addProcessToReap(pid);
7613
7614 LogFlowThisFuncLeave();
7615 return S_OK;
7616}
7617
7618/**
7619 * Returns @c true if the given session machine instance has an open direct
7620 * session (and optionally also for direct sessions which are closing) and
7621 * returns the session control machine instance if so.
7622 *
7623 * Note that when the method returns @c false, the arguments remain unchanged.
7624 *
7625 * @param aMachine Session machine object.
7626 * @param aControl Direct session control object (optional).
7627 * @param aRequireVM If true then only allow VM sessions.
7628 * @param aAllowClosing If true then additionally a session which is currently
7629 * being closed will also be allowed.
7630 *
7631 * @note locks this object for reading.
7632 */
7633bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7634 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7635 bool aRequireVM /*= false*/,
7636 bool aAllowClosing /*= false*/)
7637{
7638 AutoLimitedCaller autoCaller(this);
7639 AssertComRCReturn(autoCaller.rc(), false);
7640
7641 /* just return false for inaccessible machines */
7642 if (getObjectState().getState() != ObjectState::Ready)
7643 return false;
7644
7645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7646
7647 if ( ( mData->mSession.mState == SessionState_Locked
7648 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7649 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7650 )
7651 {
7652 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7653
7654 aMachine = mData->mSession.mMachine;
7655
7656 if (aControl != NULL)
7657 *aControl = mData->mSession.mDirectControl;
7658
7659 return true;
7660 }
7661
7662 return false;
7663}
7664
7665/**
7666 * Returns @c true if the given machine has an spawning direct session.
7667 *
7668 * @note locks this object for reading.
7669 */
7670bool Machine::i_isSessionSpawning()
7671{
7672 AutoLimitedCaller autoCaller(this);
7673 AssertComRCReturn(autoCaller.rc(), false);
7674
7675 /* just return false for inaccessible machines */
7676 if (getObjectState().getState() != ObjectState::Ready)
7677 return false;
7678
7679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7680
7681 if (mData->mSession.mState == SessionState_Spawning)
7682 return true;
7683
7684 return false;
7685}
7686
7687/**
7688 * Called from the client watcher thread to check for unexpected client process
7689 * death during Session_Spawning state (e.g. before it successfully opened a
7690 * direct session).
7691 *
7692 * On Win32 and on OS/2, this method is called only when we've got the
7693 * direct client's process termination notification, so it always returns @c
7694 * true.
7695 *
7696 * On other platforms, this method returns @c true if the client process is
7697 * terminated and @c false if it's still alive.
7698 *
7699 * @note Locks this object for writing.
7700 */
7701bool Machine::i_checkForSpawnFailure()
7702{
7703 AutoCaller autoCaller(this);
7704 if (!autoCaller.isOk())
7705 {
7706 /* nothing to do */
7707 LogFlowThisFunc(("Already uninitialized!\n"));
7708 return true;
7709 }
7710
7711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7712
7713 if (mData->mSession.mState != SessionState_Spawning)
7714 {
7715 /* nothing to do */
7716 LogFlowThisFunc(("Not spawning any more!\n"));
7717 return true;
7718 }
7719
7720 HRESULT rc = S_OK;
7721
7722 /* PID not yet initialized, skip check. */
7723 if (mData->mSession.mPID == NIL_RTPROCESS)
7724 return false;
7725
7726 RTPROCSTATUS status;
7727 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7728
7729 if (vrc != VERR_PROCESS_RUNNING)
7730 {
7731 Utf8Str strExtraInfo;
7732
7733#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7734 /* If the startup logfile exists and is of non-zero length, tell the
7735 user to look there for more details to encourage them to attach it
7736 when reporting startup issues. */
7737 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7738 uint64_t cbStartupLogFile = 0;
7739 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7740 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7741 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7742#endif
7743
7744 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7745 rc = setError(E_FAIL,
7746 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7747 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7748 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7749 rc = setError(E_FAIL,
7750 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7751 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7752 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7753 rc = setError(E_FAIL,
7754 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7755 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7756 else
7757 rc = setErrorBoth(E_FAIL, vrc,
7758 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7759 i_getName().c_str(), vrc, strExtraInfo.c_str());
7760 }
7761
7762 if (FAILED(rc))
7763 {
7764 /* Close the remote session, remove the remote control from the list
7765 * and reset session state to Closed (@note keep the code in sync with
7766 * the relevant part in LockMachine()). */
7767
7768 Assert(mData->mSession.mRemoteControls.size() == 1);
7769 if (mData->mSession.mRemoteControls.size() == 1)
7770 {
7771 ErrorInfoKeeper eik;
7772 mData->mSession.mRemoteControls.front()->Uninitialize();
7773 }
7774
7775 mData->mSession.mRemoteControls.clear();
7776 mData->mSession.mState = SessionState_Unlocked;
7777
7778 /* finalize the progress after setting the state */
7779 if (!mData->mSession.mProgress.isNull())
7780 {
7781 mData->mSession.mProgress->notifyComplete(rc);
7782 mData->mSession.mProgress.setNull();
7783 }
7784
7785 mData->mSession.mPID = NIL_RTPROCESS;
7786
7787 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7788 return true;
7789 }
7790
7791 return false;
7792}
7793
7794/**
7795 * Checks whether the machine can be registered. If so, commits and saves
7796 * all settings.
7797 *
7798 * @note Must be called from mParent's write lock. Locks this object and
7799 * children for writing.
7800 */
7801HRESULT Machine::i_prepareRegister()
7802{
7803 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7804
7805 AutoLimitedCaller autoCaller(this);
7806 AssertComRCReturnRC(autoCaller.rc());
7807
7808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7809
7810 /* wait for state dependents to drop to zero */
7811 i_ensureNoStateDependencies();
7812
7813 if (!mData->mAccessible)
7814 return setError(VBOX_E_INVALID_OBJECT_STATE,
7815 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7816 mUserData->s.strName.c_str(),
7817 mData->mUuid.toString().c_str());
7818
7819 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7820
7821 if (mData->mRegistered)
7822 return setError(VBOX_E_INVALID_OBJECT_STATE,
7823 tr("The machine '%s' with UUID {%s} is already registered"),
7824 mUserData->s.strName.c_str(),
7825 mData->mUuid.toString().c_str());
7826
7827 HRESULT rc = S_OK;
7828
7829 // Ensure the settings are saved. If we are going to be registered and
7830 // no config file exists yet, create it by calling i_saveSettings() too.
7831 if ( (mData->flModifications)
7832 || (!mData->pMachineConfigFile->fileExists())
7833 )
7834 {
7835 rc = i_saveSettings(NULL);
7836 // no need to check whether VirtualBox.xml needs saving too since
7837 // we can't have a machine XML file rename pending
7838 if (FAILED(rc)) return rc;
7839 }
7840
7841 /* more config checking goes here */
7842
7843 if (SUCCEEDED(rc))
7844 {
7845 /* we may have had implicit modifications we want to fix on success */
7846 i_commit();
7847
7848 mData->mRegistered = true;
7849 }
7850 else
7851 {
7852 /* we may have had implicit modifications we want to cancel on failure*/
7853 i_rollback(false /* aNotify */);
7854 }
7855
7856 return rc;
7857}
7858
7859/**
7860 * Increases the number of objects dependent on the machine state or on the
7861 * registered state. Guarantees that these two states will not change at least
7862 * until #i_releaseStateDependency() is called.
7863 *
7864 * Depending on the @a aDepType value, additional state checks may be made.
7865 * These checks will set extended error info on failure. See
7866 * #i_checkStateDependency() for more info.
7867 *
7868 * If this method returns a failure, the dependency is not added and the caller
7869 * is not allowed to rely on any particular machine state or registration state
7870 * value and may return the failed result code to the upper level.
7871 *
7872 * @param aDepType Dependency type to add.
7873 * @param aState Current machine state (NULL if not interested).
7874 * @param aRegistered Current registered state (NULL if not interested).
7875 *
7876 * @note Locks this object for writing.
7877 */
7878HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7879 MachineState_T *aState /* = NULL */,
7880 BOOL *aRegistered /* = NULL */)
7881{
7882 AutoCaller autoCaller(this);
7883 AssertComRCReturnRC(autoCaller.rc());
7884
7885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7886
7887 HRESULT rc = i_checkStateDependency(aDepType);
7888 if (FAILED(rc)) return rc;
7889
7890 {
7891 if (mData->mMachineStateChangePending != 0)
7892 {
7893 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7894 * drop to zero so don't add more. It may make sense to wait a bit
7895 * and retry before reporting an error (since the pending state
7896 * transition should be really quick) but let's just assert for
7897 * now to see if it ever happens on practice. */
7898
7899 AssertFailed();
7900
7901 return setError(E_ACCESSDENIED,
7902 tr("Machine state change is in progress. Please retry the operation later."));
7903 }
7904
7905 ++mData->mMachineStateDeps;
7906 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7907 }
7908
7909 if (aState)
7910 *aState = mData->mMachineState;
7911 if (aRegistered)
7912 *aRegistered = mData->mRegistered;
7913
7914 return S_OK;
7915}
7916
7917/**
7918 * Decreases the number of objects dependent on the machine state.
7919 * Must always complete the #i_addStateDependency() call after the state
7920 * dependency is no more necessary.
7921 */
7922void Machine::i_releaseStateDependency()
7923{
7924 AutoCaller autoCaller(this);
7925 AssertComRCReturnVoid(autoCaller.rc());
7926
7927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7928
7929 /* releaseStateDependency() w/o addStateDependency()? */
7930 AssertReturnVoid(mData->mMachineStateDeps != 0);
7931 -- mData->mMachineStateDeps;
7932
7933 if (mData->mMachineStateDeps == 0)
7934 {
7935 /* inform i_ensureNoStateDependencies() that there are no more deps */
7936 if (mData->mMachineStateChangePending != 0)
7937 {
7938 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7939 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7940 }
7941 }
7942}
7943
7944Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7945{
7946 /* start with nothing found */
7947 Utf8Str strResult("");
7948
7949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7950
7951 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7952 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7953 // found:
7954 strResult = it->second; // source is a Utf8Str
7955
7956 return strResult;
7957}
7958
7959// protected methods
7960/////////////////////////////////////////////////////////////////////////////
7961
7962/**
7963 * Performs machine state checks based on the @a aDepType value. If a check
7964 * fails, this method will set extended error info, otherwise it will return
7965 * S_OK. It is supposed, that on failure, the caller will immediately return
7966 * the return value of this method to the upper level.
7967 *
7968 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7969 *
7970 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7971 * current state of this machine object allows to change settings of the
7972 * machine (i.e. the machine is not registered, or registered but not running
7973 * and not saved). It is useful to call this method from Machine setters
7974 * before performing any change.
7975 *
7976 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7977 * as for MutableStateDep except that if the machine is saved, S_OK is also
7978 * returned. This is useful in setters which allow changing machine
7979 * properties when it is in the saved state.
7980 *
7981 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7982 * if the current state of this machine object allows to change runtime
7983 * changeable settings of the machine (i.e. the machine is not registered, or
7984 * registered but either running or not running and not saved). It is useful
7985 * to call this method from Machine setters before performing any changes to
7986 * runtime changeable settings.
7987 *
7988 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7989 * the same as for MutableOrRunningStateDep except that if the machine is
7990 * saved, S_OK is also returned. This is useful in setters which allow
7991 * changing runtime and saved state changeable machine properties.
7992 *
7993 * @param aDepType Dependency type to check.
7994 *
7995 * @note Non Machine based classes should use #i_addStateDependency() and
7996 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7997 * template.
7998 *
7999 * @note This method must be called from under this object's read or write
8000 * lock.
8001 */
8002HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8003{
8004 switch (aDepType)
8005 {
8006 case AnyStateDep:
8007 {
8008 break;
8009 }
8010 case MutableStateDep:
8011 {
8012 if ( mData->mRegistered
8013 && ( !i_isSessionMachine()
8014 || ( mData->mMachineState != MachineState_Aborted
8015 && mData->mMachineState != MachineState_Teleported
8016 && mData->mMachineState != MachineState_PoweredOff
8017 )
8018 )
8019 )
8020 return setError(VBOX_E_INVALID_VM_STATE,
8021 tr("The machine is not mutable (state is %s)"),
8022 Global::stringifyMachineState(mData->mMachineState));
8023 break;
8024 }
8025 case MutableOrSavedStateDep:
8026 {
8027 if ( mData->mRegistered
8028 && ( !i_isSessionMachine()
8029 || ( mData->mMachineState != MachineState_Aborted
8030 && mData->mMachineState != MachineState_Teleported
8031 && mData->mMachineState != MachineState_Saved
8032 && mData->mMachineState != MachineState_PoweredOff
8033 )
8034 )
8035 )
8036 return setError(VBOX_E_INVALID_VM_STATE,
8037 tr("The machine is not mutable or saved (state is %s)"),
8038 Global::stringifyMachineState(mData->mMachineState));
8039 break;
8040 }
8041 case MutableOrRunningStateDep:
8042 {
8043 if ( mData->mRegistered
8044 && ( !i_isSessionMachine()
8045 || ( mData->mMachineState != MachineState_Aborted
8046 && mData->mMachineState != MachineState_Teleported
8047 && mData->mMachineState != MachineState_PoweredOff
8048 && !Global::IsOnline(mData->mMachineState)
8049 )
8050 )
8051 )
8052 return setError(VBOX_E_INVALID_VM_STATE,
8053 tr("The machine is not mutable or running (state is %s)"),
8054 Global::stringifyMachineState(mData->mMachineState));
8055 break;
8056 }
8057 case MutableOrSavedOrRunningStateDep:
8058 {
8059 if ( mData->mRegistered
8060 && ( !i_isSessionMachine()
8061 || ( mData->mMachineState != MachineState_Aborted
8062 && mData->mMachineState != MachineState_Teleported
8063 && mData->mMachineState != MachineState_Saved
8064 && mData->mMachineState != MachineState_PoweredOff
8065 && !Global::IsOnline(mData->mMachineState)
8066 )
8067 )
8068 )
8069 return setError(VBOX_E_INVALID_VM_STATE,
8070 tr("The machine is not mutable, saved or running (state is %s)"),
8071 Global::stringifyMachineState(mData->mMachineState));
8072 break;
8073 }
8074 }
8075
8076 return S_OK;
8077}
8078
8079/**
8080 * Helper to initialize all associated child objects and allocate data
8081 * structures.
8082 *
8083 * This method must be called as a part of the object's initialization procedure
8084 * (usually done in the #init() method).
8085 *
8086 * @note Must be called only from #init() or from #i_registeredInit().
8087 */
8088HRESULT Machine::initDataAndChildObjects()
8089{
8090 AutoCaller autoCaller(this);
8091 AssertComRCReturnRC(autoCaller.rc());
8092 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8093 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8094
8095 AssertReturn(!mData->mAccessible, E_FAIL);
8096
8097 /* allocate data structures */
8098 mSSData.allocate();
8099 mUserData.allocate();
8100 mHWData.allocate();
8101 mMediumAttachments.allocate();
8102 mStorageControllers.allocate();
8103 mUSBControllers.allocate();
8104
8105 /* initialize mOSTypeId */
8106 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8107
8108/** @todo r=bird: init() methods never fails, right? Why don't we make them
8109 * return void then! */
8110
8111 /* create associated BIOS settings object */
8112 unconst(mBIOSSettings).createObject();
8113 mBIOSSettings->init(this);
8114
8115 /* create associated record settings object */
8116 unconst(mRecordingSettings).createObject();
8117 mRecordingSettings->init(this);
8118
8119 /* create the graphics adapter object (always present) */
8120 unconst(mGraphicsAdapter).createObject();
8121 mGraphicsAdapter->init(this);
8122
8123 /* create an associated VRDE object (default is disabled) */
8124 unconst(mVRDEServer).createObject();
8125 mVRDEServer->init(this);
8126
8127 /* create associated serial port objects */
8128 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8129 {
8130 unconst(mSerialPorts[slot]).createObject();
8131 mSerialPorts[slot]->init(this, slot);
8132 }
8133
8134 /* create associated parallel port objects */
8135 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8136 {
8137 unconst(mParallelPorts[slot]).createObject();
8138 mParallelPorts[slot]->init(this, slot);
8139 }
8140
8141 /* create the audio adapter object (always present, default is disabled) */
8142 unconst(mAudioAdapter).createObject();
8143 mAudioAdapter->init(this);
8144
8145 /* create the USB device filters object (always present) */
8146 unconst(mUSBDeviceFilters).createObject();
8147 mUSBDeviceFilters->init(this);
8148
8149 /* create associated network adapter objects */
8150 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8151 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8152 {
8153 unconst(mNetworkAdapters[slot]).createObject();
8154 mNetworkAdapters[slot]->init(this, slot);
8155 }
8156
8157 /* create the bandwidth control */
8158 unconst(mBandwidthControl).createObject();
8159 mBandwidthControl->init(this);
8160
8161 return S_OK;
8162}
8163
8164/**
8165 * Helper to uninitialize all associated child objects and to free all data
8166 * structures.
8167 *
8168 * This method must be called as a part of the object's uninitialization
8169 * procedure (usually done in the #uninit() method).
8170 *
8171 * @note Must be called only from #uninit() or from #i_registeredInit().
8172 */
8173void Machine::uninitDataAndChildObjects()
8174{
8175 AutoCaller autoCaller(this);
8176 AssertComRCReturnVoid(autoCaller.rc());
8177 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8178 || getObjectState().getState() == ObjectState::Limited);
8179
8180 /* tell all our other child objects we've been uninitialized */
8181 if (mBandwidthControl)
8182 {
8183 mBandwidthControl->uninit();
8184 unconst(mBandwidthControl).setNull();
8185 }
8186
8187 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8188 {
8189 if (mNetworkAdapters[slot])
8190 {
8191 mNetworkAdapters[slot]->uninit();
8192 unconst(mNetworkAdapters[slot]).setNull();
8193 }
8194 }
8195
8196 if (mUSBDeviceFilters)
8197 {
8198 mUSBDeviceFilters->uninit();
8199 unconst(mUSBDeviceFilters).setNull();
8200 }
8201
8202 if (mAudioAdapter)
8203 {
8204 mAudioAdapter->uninit();
8205 unconst(mAudioAdapter).setNull();
8206 }
8207
8208 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8209 {
8210 if (mParallelPorts[slot])
8211 {
8212 mParallelPorts[slot]->uninit();
8213 unconst(mParallelPorts[slot]).setNull();
8214 }
8215 }
8216
8217 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8218 {
8219 if (mSerialPorts[slot])
8220 {
8221 mSerialPorts[slot]->uninit();
8222 unconst(mSerialPorts[slot]).setNull();
8223 }
8224 }
8225
8226 if (mVRDEServer)
8227 {
8228 mVRDEServer->uninit();
8229 unconst(mVRDEServer).setNull();
8230 }
8231
8232 if (mGraphicsAdapter)
8233 {
8234 mGraphicsAdapter->uninit();
8235 unconst(mGraphicsAdapter).setNull();
8236 }
8237
8238 if (mBIOSSettings)
8239 {
8240 mBIOSSettings->uninit();
8241 unconst(mBIOSSettings).setNull();
8242 }
8243
8244 if (mRecordingSettings)
8245 {
8246 mRecordingSettings->uninit();
8247 unconst(mRecordingSettings).setNull();
8248 }
8249
8250 /* Deassociate media (only when a real Machine or a SnapshotMachine
8251 * instance is uninitialized; SessionMachine instances refer to real
8252 * Machine media). This is necessary for a clean re-initialization of
8253 * the VM after successfully re-checking the accessibility state. Note
8254 * that in case of normal Machine or SnapshotMachine uninitialization (as
8255 * a result of unregistering or deleting the snapshot), outdated media
8256 * attachments will already be uninitialized and deleted, so this
8257 * code will not affect them. */
8258 if ( !mMediumAttachments.isNull()
8259 && !i_isSessionMachine()
8260 )
8261 {
8262 for (MediumAttachmentList::const_iterator
8263 it = mMediumAttachments->begin();
8264 it != mMediumAttachments->end();
8265 ++it)
8266 {
8267 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8268 if (pMedium.isNull())
8269 continue;
8270 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8271 AssertComRC(rc);
8272 }
8273 }
8274
8275 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8276 {
8277 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8278 if (mData->mFirstSnapshot)
8279 {
8280 // snapshots tree is protected by machine write lock; strictly
8281 // this isn't necessary here since we're deleting the entire
8282 // machine, but otherwise we assert in Snapshot::uninit()
8283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8284 mData->mFirstSnapshot->uninit();
8285 mData->mFirstSnapshot.setNull();
8286 }
8287
8288 mData->mCurrentSnapshot.setNull();
8289 }
8290
8291 /* free data structures (the essential mData structure is not freed here
8292 * since it may be still in use) */
8293 mMediumAttachments.free();
8294 mStorageControllers.free();
8295 mUSBControllers.free();
8296 mHWData.free();
8297 mUserData.free();
8298 mSSData.free();
8299}
8300
8301/**
8302 * Returns a pointer to the Machine object for this machine that acts like a
8303 * parent for complex machine data objects such as shared folders, etc.
8304 *
8305 * For primary Machine objects and for SnapshotMachine objects, returns this
8306 * object's pointer itself. For SessionMachine objects, returns the peer
8307 * (primary) machine pointer.
8308 */
8309Machine *Machine::i_getMachine()
8310{
8311 if (i_isSessionMachine())
8312 return (Machine*)mPeer;
8313 return this;
8314}
8315
8316/**
8317 * Makes sure that there are no machine state dependents. If necessary, waits
8318 * for the number of dependents to drop to zero.
8319 *
8320 * Make sure this method is called from under this object's write lock to
8321 * guarantee that no new dependents may be added when this method returns
8322 * control to the caller.
8323 *
8324 * @note Locks this object for writing. The lock will be released while waiting
8325 * (if necessary).
8326 *
8327 * @warning To be used only in methods that change the machine state!
8328 */
8329void Machine::i_ensureNoStateDependencies()
8330{
8331 AssertReturnVoid(isWriteLockOnCurrentThread());
8332
8333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8334
8335 /* Wait for all state dependents if necessary */
8336 if (mData->mMachineStateDeps != 0)
8337 {
8338 /* lazy semaphore creation */
8339 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8340 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8341
8342 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8343 mData->mMachineStateDeps));
8344
8345 ++mData->mMachineStateChangePending;
8346
8347 /* reset the semaphore before waiting, the last dependent will signal
8348 * it */
8349 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8350
8351 alock.release();
8352
8353 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8354
8355 alock.acquire();
8356
8357 -- mData->mMachineStateChangePending;
8358 }
8359}
8360
8361/**
8362 * Changes the machine state and informs callbacks.
8363 *
8364 * This method is not intended to fail so it either returns S_OK or asserts (and
8365 * returns a failure).
8366 *
8367 * @note Locks this object for writing.
8368 */
8369HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8370{
8371 LogFlowThisFuncEnter();
8372 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8373 Assert(aMachineState != MachineState_Null);
8374
8375 AutoCaller autoCaller(this);
8376 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8377
8378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8379
8380 /* wait for state dependents to drop to zero */
8381 i_ensureNoStateDependencies();
8382
8383 MachineState_T const enmOldState = mData->mMachineState;
8384 if (enmOldState != aMachineState)
8385 {
8386 mData->mMachineState = aMachineState;
8387 RTTimeNow(&mData->mLastStateChange);
8388
8389#ifdef VBOX_WITH_DTRACE_R3_MAIN
8390 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8391#endif
8392 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8393 }
8394
8395 LogFlowThisFuncLeave();
8396 return S_OK;
8397}
8398
8399/**
8400 * Searches for a shared folder with the given logical name
8401 * in the collection of shared folders.
8402 *
8403 * @param aName logical name of the shared folder
8404 * @param aSharedFolder where to return the found object
8405 * @param aSetError whether to set the error info if the folder is
8406 * not found
8407 * @return
8408 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8409 *
8410 * @note
8411 * must be called from under the object's lock!
8412 */
8413HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8414 ComObjPtr<SharedFolder> &aSharedFolder,
8415 bool aSetError /* = false */)
8416{
8417 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8418 for (HWData::SharedFolderList::const_iterator
8419 it = mHWData->mSharedFolders.begin();
8420 it != mHWData->mSharedFolders.end();
8421 ++it)
8422 {
8423 SharedFolder *pSF = *it;
8424 AutoCaller autoCaller(pSF);
8425 if (pSF->i_getName() == aName)
8426 {
8427 aSharedFolder = pSF;
8428 rc = S_OK;
8429 break;
8430 }
8431 }
8432
8433 if (aSetError && FAILED(rc))
8434 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8435
8436 return rc;
8437}
8438
8439/**
8440 * Initializes all machine instance data from the given settings structures
8441 * from XML. The exception is the machine UUID which needs special handling
8442 * depending on the caller's use case, so the caller needs to set that herself.
8443 *
8444 * This gets called in several contexts during machine initialization:
8445 *
8446 * -- When machine XML exists on disk already and needs to be loaded into memory,
8447 * for example, from #i_registeredInit() to load all registered machines on
8448 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8449 * attached to the machine should be part of some media registry already.
8450 *
8451 * -- During OVF import, when a machine config has been constructed from an
8452 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8453 * ensure that the media listed as attachments in the config (which have
8454 * been imported from the OVF) receive the correct registry ID.
8455 *
8456 * -- During VM cloning.
8457 *
8458 * @param config Machine settings from XML.
8459 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8460 * for each attached medium in the config.
8461 * @return
8462 */
8463HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8464 const Guid *puuidRegistry)
8465{
8466 // copy name, description, OS type, teleporter, UTC etc.
8467 mUserData->s = config.machineUserData;
8468
8469 // look up the object by Id to check it is valid
8470 ComObjPtr<GuestOSType> pGuestOSType;
8471 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8472 if (!pGuestOSType.isNull())
8473 mUserData->s.strOsType = pGuestOSType->i_id();
8474
8475 // stateFile (optional)
8476 if (config.strStateFile.isEmpty())
8477 mSSData->strStateFilePath.setNull();
8478 else
8479 {
8480 Utf8Str stateFilePathFull(config.strStateFile);
8481 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8482 if (RT_FAILURE(vrc))
8483 return setErrorBoth(E_FAIL, vrc,
8484 tr("Invalid saved state file path '%s' (%Rrc)"),
8485 config.strStateFile.c_str(),
8486 vrc);
8487 mSSData->strStateFilePath = stateFilePathFull;
8488 }
8489
8490 // snapshot folder needs special processing so set it again
8491 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8492 if (FAILED(rc)) return rc;
8493
8494 /* Copy the extra data items (config may or may not be the same as
8495 * mData->pMachineConfigFile) if necessary. When loading the XML files
8496 * from disk they are the same, but not for OVF import. */
8497 if (mData->pMachineConfigFile != &config)
8498 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8499
8500 /* currentStateModified (optional, default is true) */
8501 mData->mCurrentStateModified = config.fCurrentStateModified;
8502
8503 mData->mLastStateChange = config.timeLastStateChange;
8504
8505 /*
8506 * note: all mUserData members must be assigned prior this point because
8507 * we need to commit changes in order to let mUserData be shared by all
8508 * snapshot machine instances.
8509 */
8510 mUserData.commitCopy();
8511
8512 // machine registry, if present (must be loaded before snapshots)
8513 if (config.canHaveOwnMediaRegistry())
8514 {
8515 // determine machine folder
8516 Utf8Str strMachineFolder = i_getSettingsFileFull();
8517 strMachineFolder.stripFilename();
8518 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8519 config.mediaRegistry,
8520 strMachineFolder);
8521 if (FAILED(rc)) return rc;
8522 }
8523
8524 /* Snapshot node (optional) */
8525 size_t cRootSnapshots;
8526 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8527 {
8528 // there must be only one root snapshot
8529 Assert(cRootSnapshots == 1);
8530
8531 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8532
8533 rc = i_loadSnapshot(snap,
8534 config.uuidCurrentSnapshot,
8535 NULL); // no parent == first snapshot
8536 if (FAILED(rc)) return rc;
8537 }
8538
8539 // hardware data
8540 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8541 if (FAILED(rc)) return rc;
8542
8543 /*
8544 * NOTE: the assignment below must be the last thing to do,
8545 * otherwise it will be not possible to change the settings
8546 * somewhere in the code above because all setters will be
8547 * blocked by i_checkStateDependency(MutableStateDep).
8548 */
8549
8550 /* set the machine state to Aborted or Saved when appropriate */
8551 if (config.fAborted)
8552 {
8553 mSSData->strStateFilePath.setNull();
8554
8555 /* no need to use i_setMachineState() during init() */
8556 mData->mMachineState = MachineState_Aborted;
8557 }
8558 else if (!mSSData->strStateFilePath.isEmpty())
8559 {
8560 /* no need to use i_setMachineState() during init() */
8561 mData->mMachineState = MachineState_Saved;
8562 }
8563
8564 // after loading settings, we are no longer different from the XML on disk
8565 mData->flModifications = 0;
8566
8567 return S_OK;
8568}
8569
8570/**
8571 * Recursively loads all snapshots starting from the given.
8572 *
8573 * @param data snapshot settings.
8574 * @param aCurSnapshotId Current snapshot ID from the settings file.
8575 * @param aParentSnapshot Parent snapshot.
8576 */
8577HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8578 const Guid &aCurSnapshotId,
8579 Snapshot *aParentSnapshot)
8580{
8581 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8582 AssertReturn(!i_isSessionMachine(), E_FAIL);
8583
8584 HRESULT rc = S_OK;
8585
8586 Utf8Str strStateFile;
8587 if (!data.strStateFile.isEmpty())
8588 {
8589 /* optional */
8590 strStateFile = data.strStateFile;
8591 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8592 if (RT_FAILURE(vrc))
8593 return setErrorBoth(E_FAIL, vrc,
8594 tr("Invalid saved state file path '%s' (%Rrc)"),
8595 strStateFile.c_str(),
8596 vrc);
8597 }
8598
8599 /* create a snapshot machine object */
8600 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8601 pSnapshotMachine.createObject();
8602 rc = pSnapshotMachine->initFromSettings(this,
8603 data.hardware,
8604 &data.debugging,
8605 &data.autostart,
8606 data.uuid.ref(),
8607 strStateFile);
8608 if (FAILED(rc)) return rc;
8609
8610 /* create a snapshot object */
8611 ComObjPtr<Snapshot> pSnapshot;
8612 pSnapshot.createObject();
8613 /* initialize the snapshot */
8614 rc = pSnapshot->init(mParent, // VirtualBox object
8615 data.uuid,
8616 data.strName,
8617 data.strDescription,
8618 data.timestamp,
8619 pSnapshotMachine,
8620 aParentSnapshot);
8621 if (FAILED(rc)) return rc;
8622
8623 /* memorize the first snapshot if necessary */
8624 if (!mData->mFirstSnapshot)
8625 mData->mFirstSnapshot = pSnapshot;
8626
8627 /* memorize the current snapshot when appropriate */
8628 if ( !mData->mCurrentSnapshot
8629 && pSnapshot->i_getId() == aCurSnapshotId
8630 )
8631 mData->mCurrentSnapshot = pSnapshot;
8632
8633 // now create the children
8634 for (settings::SnapshotsList::const_iterator
8635 it = data.llChildSnapshots.begin();
8636 it != data.llChildSnapshots.end();
8637 ++it)
8638 {
8639 const settings::Snapshot &childData = *it;
8640 // recurse
8641 rc = i_loadSnapshot(childData,
8642 aCurSnapshotId,
8643 pSnapshot); // parent = the one we created above
8644 if (FAILED(rc)) return rc;
8645 }
8646
8647 return rc;
8648}
8649
8650/**
8651 * Loads settings into mHWData.
8652 *
8653 * @param puuidRegistry Registry ID.
8654 * @param puuidSnapshot Snapshot ID
8655 * @param data Reference to the hardware settings.
8656 * @param pDbg Pointer to the debugging settings.
8657 * @param pAutostart Pointer to the autostart settings.
8658 */
8659HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8660 const Guid *puuidSnapshot,
8661 const settings::Hardware &data,
8662 const settings::Debugging *pDbg,
8663 const settings::Autostart *pAutostart)
8664{
8665 AssertReturn(!i_isSessionMachine(), E_FAIL);
8666
8667 HRESULT rc = S_OK;
8668
8669 try
8670 {
8671 ComObjPtr<GuestOSType> pGuestOSType;
8672 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8673
8674 /* The hardware version attribute (optional). */
8675 mHWData->mHWVersion = data.strVersion;
8676 mHWData->mHardwareUUID = data.uuid;
8677
8678 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8679 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8680 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8681 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8682 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8683 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8684 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8685 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8686 mHWData->mPAEEnabled = data.fPAE;
8687 mHWData->mLongMode = data.enmLongMode;
8688 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8689 mHWData->mAPIC = data.fAPIC;
8690 mHWData->mX2APIC = data.fX2APIC;
8691 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8692 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8693 mHWData->mSpecCtrl = data.fSpecCtrl;
8694 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8695 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8696 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8697 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8698 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8699 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8700 mHWData->mCPUCount = data.cCPUs;
8701 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8702 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8703 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8704 mHWData->mCpuProfile = data.strCpuProfile;
8705
8706 // cpu
8707 if (mHWData->mCPUHotPlugEnabled)
8708 {
8709 for (settings::CpuList::const_iterator
8710 it = data.llCpus.begin();
8711 it != data.llCpus.end();
8712 ++it)
8713 {
8714 const settings::Cpu &cpu = *it;
8715
8716 mHWData->mCPUAttached[cpu.ulId] = true;
8717 }
8718 }
8719
8720 // cpuid leafs
8721 for (settings::CpuIdLeafsList::const_iterator
8722 it = data.llCpuIdLeafs.begin();
8723 it != data.llCpuIdLeafs.end();
8724 ++it)
8725 {
8726 const settings::CpuIdLeaf &rLeaf= *it;
8727 if ( rLeaf.idx < UINT32_C(0x20)
8728 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8729 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8730 mHWData->mCpuIdLeafList.push_back(rLeaf);
8731 /* else: just ignore */
8732 }
8733
8734 mHWData->mMemorySize = data.ulMemorySizeMB;
8735 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8736
8737 // boot order
8738 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8739 {
8740 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8741 if (it == data.mapBootOrder.end())
8742 mHWData->mBootOrder[i] = DeviceType_Null;
8743 else
8744 mHWData->mBootOrder[i] = it->second;
8745 }
8746
8747 mHWData->mFirmwareType = data.firmwareType;
8748 mHWData->mPointingHIDType = data.pointingHIDType;
8749 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8750 mHWData->mChipsetType = data.chipsetType;
8751 mHWData->mParavirtProvider = data.paravirtProvider;
8752 mHWData->mParavirtDebug = data.strParavirtDebug;
8753 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8754 mHWData->mHPETEnabled = data.fHPETEnabled;
8755
8756 /* GraphicsAdapter */
8757 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8758 if (FAILED(rc)) return rc;
8759
8760 /* VRDEServer */
8761 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8762 if (FAILED(rc)) return rc;
8763
8764 /* BIOS */
8765 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8766 if (FAILED(rc)) return rc;
8767
8768 /* Recording settings */
8769 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8770 if (FAILED(rc)) return rc;
8771
8772 // Bandwidth control (must come before network adapters)
8773 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8774 if (FAILED(rc)) return rc;
8775
8776 /* USB controllers */
8777 for (settings::USBControllerList::const_iterator
8778 it = data.usbSettings.llUSBControllers.begin();
8779 it != data.usbSettings.llUSBControllers.end();
8780 ++it)
8781 {
8782 const settings::USBController &settingsCtrl = *it;
8783 ComObjPtr<USBController> newCtrl;
8784
8785 newCtrl.createObject();
8786 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8787 mUSBControllers->push_back(newCtrl);
8788 }
8789
8790 /* USB device filters */
8791 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8792 if (FAILED(rc)) return rc;
8793
8794 // network adapters (establish array size first and apply defaults, to
8795 // ensure reading the same settings as we saved, since the list skips
8796 // adapters having defaults)
8797 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8798 size_t oldCount = mNetworkAdapters.size();
8799 if (newCount > oldCount)
8800 {
8801 mNetworkAdapters.resize(newCount);
8802 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8803 {
8804 unconst(mNetworkAdapters[slot]).createObject();
8805 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8806 }
8807 }
8808 else if (newCount < oldCount)
8809 mNetworkAdapters.resize(newCount);
8810 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8811 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8812 for (settings::NetworkAdaptersList::const_iterator
8813 it = data.llNetworkAdapters.begin();
8814 it != data.llNetworkAdapters.end();
8815 ++it)
8816 {
8817 const settings::NetworkAdapter &nic = *it;
8818
8819 /* slot uniqueness is guaranteed by XML Schema */
8820 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8821 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8822 if (FAILED(rc)) return rc;
8823 }
8824
8825 // serial ports (establish defaults first, to ensure reading the same
8826 // settings as we saved, since the list skips ports having defaults)
8827 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8828 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8829 for (settings::SerialPortsList::const_iterator
8830 it = data.llSerialPorts.begin();
8831 it != data.llSerialPorts.end();
8832 ++it)
8833 {
8834 const settings::SerialPort &s = *it;
8835
8836 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8837 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8838 if (FAILED(rc)) return rc;
8839 }
8840
8841 // parallel ports (establish defaults first, to ensure reading the same
8842 // settings as we saved, since the list skips ports having defaults)
8843 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8844 mParallelPorts[i]->i_applyDefaults();
8845 for (settings::ParallelPortsList::const_iterator
8846 it = data.llParallelPorts.begin();
8847 it != data.llParallelPorts.end();
8848 ++it)
8849 {
8850 const settings::ParallelPort &p = *it;
8851
8852 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8853 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8854 if (FAILED(rc)) return rc;
8855 }
8856
8857 /* AudioAdapter */
8858 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8859 if (FAILED(rc)) return rc;
8860
8861 /* storage controllers */
8862 rc = i_loadStorageControllers(data.storage,
8863 puuidRegistry,
8864 puuidSnapshot);
8865 if (FAILED(rc)) return rc;
8866
8867 /* Shared folders */
8868 for (settings::SharedFoldersList::const_iterator
8869 it = data.llSharedFolders.begin();
8870 it != data.llSharedFolders.end();
8871 ++it)
8872 {
8873 const settings::SharedFolder &sf = *it;
8874
8875 ComObjPtr<SharedFolder> sharedFolder;
8876 /* Check for double entries. Not allowed! */
8877 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8878 if (SUCCEEDED(rc))
8879 return setError(VBOX_E_OBJECT_IN_USE,
8880 tr("Shared folder named '%s' already exists"),
8881 sf.strName.c_str());
8882
8883 /* Create the new shared folder. Don't break on error. This will be
8884 * reported when the machine starts. */
8885 sharedFolder.createObject();
8886 rc = sharedFolder->init(i_getMachine(),
8887 sf.strName,
8888 sf.strHostPath,
8889 RT_BOOL(sf.fWritable),
8890 RT_BOOL(sf.fAutoMount),
8891 sf.strAutoMountPoint,
8892 false /* fFailOnError */);
8893 if (FAILED(rc)) return rc;
8894 mHWData->mSharedFolders.push_back(sharedFolder);
8895 }
8896
8897 // Clipboard
8898 mHWData->mClipboardMode = data.clipboardMode;
8899 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8900
8901 // drag'n'drop
8902 mHWData->mDnDMode = data.dndMode;
8903
8904 // guest settings
8905 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8906
8907 // IO settings
8908 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8909 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8910
8911 // Host PCI devices
8912 for (settings::HostPCIDeviceAttachmentList::const_iterator
8913 it = data.pciAttachments.begin();
8914 it != data.pciAttachments.end();
8915 ++it)
8916 {
8917 const settings::HostPCIDeviceAttachment &hpda = *it;
8918 ComObjPtr<PCIDeviceAttachment> pda;
8919
8920 pda.createObject();
8921 pda->i_loadSettings(this, hpda);
8922 mHWData->mPCIDeviceAssignments.push_back(pda);
8923 }
8924
8925 /*
8926 * (The following isn't really real hardware, but it lives in HWData
8927 * for reasons of convenience.)
8928 */
8929
8930#ifdef VBOX_WITH_GUEST_PROPS
8931 /* Guest properties (optional) */
8932
8933 /* Only load transient guest properties for configs which have saved
8934 * state, because there shouldn't be any for powered off VMs. The same
8935 * logic applies for snapshots, as offline snapshots shouldn't have
8936 * any such properties. They confuse the code in various places.
8937 * Note: can't rely on the machine state, as it isn't set yet. */
8938 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8939 /* apologies for the hacky unconst() usage, but this needs hacking
8940 * actually inconsistent settings into consistency, otherwise there
8941 * will be some corner cases where the inconsistency survives
8942 * surprisingly long without getting fixed, especially for snapshots
8943 * as there are no config changes. */
8944 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8945 for (settings::GuestPropertiesList::iterator
8946 it = llGuestProperties.begin();
8947 it != llGuestProperties.end();
8948 /*nothing*/)
8949 {
8950 const settings::GuestProperty &prop = *it;
8951 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8952 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8953 if ( fSkipTransientGuestProperties
8954 && ( fFlags & GUEST_PROP_F_TRANSIENT
8955 || fFlags & GUEST_PROP_F_TRANSRESET))
8956 {
8957 it = llGuestProperties.erase(it);
8958 continue;
8959 }
8960 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8961 mHWData->mGuestProperties[prop.strName] = property;
8962 ++it;
8963 }
8964#endif /* VBOX_WITH_GUEST_PROPS defined */
8965
8966 rc = i_loadDebugging(pDbg);
8967 if (FAILED(rc))
8968 return rc;
8969
8970 mHWData->mAutostart = *pAutostart;
8971
8972 /* default frontend */
8973 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8974 }
8975 catch (std::bad_alloc &)
8976 {
8977 return E_OUTOFMEMORY;
8978 }
8979
8980 AssertComRC(rc);
8981 return rc;
8982}
8983
8984/**
8985 * Called from i_loadHardware() to load the debugging settings of the
8986 * machine.
8987 *
8988 * @param pDbg Pointer to the settings.
8989 */
8990HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8991{
8992 mHWData->mDebugging = *pDbg;
8993 /* no more processing currently required, this will probably change. */
8994 return S_OK;
8995}
8996
8997/**
8998 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8999 *
9000 * @param data storage settings.
9001 * @param puuidRegistry media registry ID to set media to or NULL;
9002 * see Machine::i_loadMachineDataFromSettings()
9003 * @param puuidSnapshot snapshot ID
9004 * @return
9005 */
9006HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9007 const Guid *puuidRegistry,
9008 const Guid *puuidSnapshot)
9009{
9010 AssertReturn(!i_isSessionMachine(), E_FAIL);
9011
9012 HRESULT rc = S_OK;
9013
9014 for (settings::StorageControllersList::const_iterator
9015 it = data.llStorageControllers.begin();
9016 it != data.llStorageControllers.end();
9017 ++it)
9018 {
9019 const settings::StorageController &ctlData = *it;
9020
9021 ComObjPtr<StorageController> pCtl;
9022 /* Try to find one with the name first. */
9023 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9024 if (SUCCEEDED(rc))
9025 return setError(VBOX_E_OBJECT_IN_USE,
9026 tr("Storage controller named '%s' already exists"),
9027 ctlData.strName.c_str());
9028
9029 pCtl.createObject();
9030 rc = pCtl->init(this,
9031 ctlData.strName,
9032 ctlData.storageBus,
9033 ctlData.ulInstance,
9034 ctlData.fBootable);
9035 if (FAILED(rc)) return rc;
9036
9037 mStorageControllers->push_back(pCtl);
9038
9039 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9040 if (FAILED(rc)) return rc;
9041
9042 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9043 if (FAILED(rc)) return rc;
9044
9045 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9046 if (FAILED(rc)) return rc;
9047
9048 /* Load the attached devices now. */
9049 rc = i_loadStorageDevices(pCtl,
9050 ctlData,
9051 puuidRegistry,
9052 puuidSnapshot);
9053 if (FAILED(rc)) return rc;
9054 }
9055
9056 return S_OK;
9057}
9058
9059/**
9060 * Called from i_loadStorageControllers for a controller's devices.
9061 *
9062 * @param aStorageController
9063 * @param data
9064 * @param puuidRegistry media registry ID to set media to or NULL; see
9065 * Machine::i_loadMachineDataFromSettings()
9066 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9067 * @return
9068 */
9069HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9070 const settings::StorageController &data,
9071 const Guid *puuidRegistry,
9072 const Guid *puuidSnapshot)
9073{
9074 HRESULT rc = S_OK;
9075
9076 /* paranoia: detect duplicate attachments */
9077 for (settings::AttachedDevicesList::const_iterator
9078 it = data.llAttachedDevices.begin();
9079 it != data.llAttachedDevices.end();
9080 ++it)
9081 {
9082 const settings::AttachedDevice &ad = *it;
9083
9084 for (settings::AttachedDevicesList::const_iterator it2 = it;
9085 it2 != data.llAttachedDevices.end();
9086 ++it2)
9087 {
9088 if (it == it2)
9089 continue;
9090
9091 const settings::AttachedDevice &ad2 = *it2;
9092
9093 if ( ad.lPort == ad2.lPort
9094 && ad.lDevice == ad2.lDevice)
9095 {
9096 return setError(E_FAIL,
9097 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9098 aStorageController->i_getName().c_str(),
9099 ad.lPort,
9100 ad.lDevice,
9101 mUserData->s.strName.c_str());
9102 }
9103 }
9104 }
9105
9106 for (settings::AttachedDevicesList::const_iterator
9107 it = data.llAttachedDevices.begin();
9108 it != data.llAttachedDevices.end();
9109 ++it)
9110 {
9111 const settings::AttachedDevice &dev = *it;
9112 ComObjPtr<Medium> medium;
9113
9114 switch (dev.deviceType)
9115 {
9116 case DeviceType_Floppy:
9117 case DeviceType_DVD:
9118 if (dev.strHostDriveSrc.isNotEmpty())
9119 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9120 false /* fRefresh */, medium);
9121 else
9122 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9123 dev.uuid,
9124 false /* fRefresh */,
9125 false /* aSetError */,
9126 medium);
9127 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9128 // This is not an error. The host drive or UUID might have vanished, so just go
9129 // ahead without this removeable medium attachment
9130 rc = S_OK;
9131 break;
9132
9133 case DeviceType_HardDisk:
9134 {
9135 /* find a hard disk by UUID */
9136 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9137 if (FAILED(rc))
9138 {
9139 if (i_isSnapshotMachine())
9140 {
9141 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9142 // so the user knows that the bad disk is in a snapshot somewhere
9143 com::ErrorInfo info;
9144 return setError(E_FAIL,
9145 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9146 puuidSnapshot->raw(),
9147 info.getText().raw());
9148 }
9149 else
9150 return rc;
9151 }
9152
9153 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9154
9155 if (medium->i_getType() == MediumType_Immutable)
9156 {
9157 if (i_isSnapshotMachine())
9158 return setError(E_FAIL,
9159 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9160 "of the virtual machine '%s' ('%s')"),
9161 medium->i_getLocationFull().c_str(),
9162 dev.uuid.raw(),
9163 puuidSnapshot->raw(),
9164 mUserData->s.strName.c_str(),
9165 mData->m_strConfigFileFull.c_str());
9166
9167 return setError(E_FAIL,
9168 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9169 medium->i_getLocationFull().c_str(),
9170 dev.uuid.raw(),
9171 mUserData->s.strName.c_str(),
9172 mData->m_strConfigFileFull.c_str());
9173 }
9174
9175 if (medium->i_getType() == MediumType_MultiAttach)
9176 {
9177 if (i_isSnapshotMachine())
9178 return setError(E_FAIL,
9179 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9180 "of the virtual machine '%s' ('%s')"),
9181 medium->i_getLocationFull().c_str(),
9182 dev.uuid.raw(),
9183 puuidSnapshot->raw(),
9184 mUserData->s.strName.c_str(),
9185 mData->m_strConfigFileFull.c_str());
9186
9187 return setError(E_FAIL,
9188 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9189 medium->i_getLocationFull().c_str(),
9190 dev.uuid.raw(),
9191 mUserData->s.strName.c_str(),
9192 mData->m_strConfigFileFull.c_str());
9193 }
9194
9195 if ( !i_isSnapshotMachine()
9196 && medium->i_getChildren().size() != 0
9197 )
9198 return setError(E_FAIL,
9199 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9200 "because it has %d differencing child hard disks"),
9201 medium->i_getLocationFull().c_str(),
9202 dev.uuid.raw(),
9203 mUserData->s.strName.c_str(),
9204 mData->m_strConfigFileFull.c_str(),
9205 medium->i_getChildren().size());
9206
9207 if (i_findAttachment(*mMediumAttachments.data(),
9208 medium))
9209 return setError(E_FAIL,
9210 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9211 medium->i_getLocationFull().c_str(),
9212 dev.uuid.raw(),
9213 mUserData->s.strName.c_str(),
9214 mData->m_strConfigFileFull.c_str());
9215
9216 break;
9217 }
9218
9219 default:
9220 return setError(E_FAIL,
9221 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9222 medium->i_getLocationFull().c_str(),
9223 mUserData->s.strName.c_str(),
9224 mData->m_strConfigFileFull.c_str());
9225 }
9226
9227 if (FAILED(rc))
9228 break;
9229
9230 /* Bandwidth groups are loaded at this point. */
9231 ComObjPtr<BandwidthGroup> pBwGroup;
9232
9233 if (!dev.strBwGroup.isEmpty())
9234 {
9235 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9236 if (FAILED(rc))
9237 return setError(E_FAIL,
9238 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9239 medium->i_getLocationFull().c_str(),
9240 dev.strBwGroup.c_str(),
9241 mUserData->s.strName.c_str(),
9242 mData->m_strConfigFileFull.c_str());
9243 pBwGroup->i_reference();
9244 }
9245
9246 const Utf8Str controllerName = aStorageController->i_getName();
9247 ComObjPtr<MediumAttachment> pAttachment;
9248 pAttachment.createObject();
9249 rc = pAttachment->init(this,
9250 medium,
9251 controllerName,
9252 dev.lPort,
9253 dev.lDevice,
9254 dev.deviceType,
9255 false,
9256 dev.fPassThrough,
9257 dev.fTempEject,
9258 dev.fNonRotational,
9259 dev.fDiscard,
9260 dev.fHotPluggable,
9261 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9262 if (FAILED(rc)) break;
9263
9264 /* associate the medium with this machine and snapshot */
9265 if (!medium.isNull())
9266 {
9267 AutoCaller medCaller(medium);
9268 if (FAILED(medCaller.rc())) return medCaller.rc();
9269 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9270
9271 if (i_isSnapshotMachine())
9272 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9273 else
9274 rc = medium->i_addBackReference(mData->mUuid);
9275 /* If the medium->addBackReference fails it sets an appropriate
9276 * error message, so no need to do any guesswork here. */
9277
9278 if (puuidRegistry)
9279 // caller wants registry ID to be set on all attached media (OVF import case)
9280 medium->i_addRegistry(*puuidRegistry);
9281 }
9282
9283 if (FAILED(rc))
9284 break;
9285
9286 /* back up mMediumAttachments to let registeredInit() properly rollback
9287 * on failure (= limited accessibility) */
9288 i_setModified(IsModified_Storage);
9289 mMediumAttachments.backup();
9290 mMediumAttachments->push_back(pAttachment);
9291 }
9292
9293 return rc;
9294}
9295
9296/**
9297 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9298 *
9299 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9300 * @param aSnapshot where to return the found snapshot
9301 * @param aSetError true to set extended error info on failure
9302 */
9303HRESULT Machine::i_findSnapshotById(const Guid &aId,
9304 ComObjPtr<Snapshot> &aSnapshot,
9305 bool aSetError /* = false */)
9306{
9307 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9308
9309 if (!mData->mFirstSnapshot)
9310 {
9311 if (aSetError)
9312 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9313 return E_FAIL;
9314 }
9315
9316 if (aId.isZero())
9317 aSnapshot = mData->mFirstSnapshot;
9318 else
9319 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9320
9321 if (!aSnapshot)
9322 {
9323 if (aSetError)
9324 return setError(E_FAIL,
9325 tr("Could not find a snapshot with UUID {%s}"),
9326 aId.toString().c_str());
9327 return E_FAIL;
9328 }
9329
9330 return S_OK;
9331}
9332
9333/**
9334 * Returns the snapshot with the given name or fails of no such snapshot.
9335 *
9336 * @param strName snapshot name to find
9337 * @param aSnapshot where to return the found snapshot
9338 * @param aSetError true to set extended error info on failure
9339 */
9340HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9341 ComObjPtr<Snapshot> &aSnapshot,
9342 bool aSetError /* = false */)
9343{
9344 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9345
9346 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9347
9348 if (!mData->mFirstSnapshot)
9349 {
9350 if (aSetError)
9351 return setError(VBOX_E_OBJECT_NOT_FOUND,
9352 tr("This machine does not have any snapshots"));
9353 return VBOX_E_OBJECT_NOT_FOUND;
9354 }
9355
9356 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9357
9358 if (!aSnapshot)
9359 {
9360 if (aSetError)
9361 return setError(VBOX_E_OBJECT_NOT_FOUND,
9362 tr("Could not find a snapshot named '%s'"), strName.c_str());
9363 return VBOX_E_OBJECT_NOT_FOUND;
9364 }
9365
9366 return S_OK;
9367}
9368
9369/**
9370 * Returns a storage controller object with the given name.
9371 *
9372 * @param aName storage controller name to find
9373 * @param aStorageController where to return the found storage controller
9374 * @param aSetError true to set extended error info on failure
9375 */
9376HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9377 ComObjPtr<StorageController> &aStorageController,
9378 bool aSetError /* = false */)
9379{
9380 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9381
9382 for (StorageControllerList::const_iterator
9383 it = mStorageControllers->begin();
9384 it != mStorageControllers->end();
9385 ++it)
9386 {
9387 if ((*it)->i_getName() == aName)
9388 {
9389 aStorageController = (*it);
9390 return S_OK;
9391 }
9392 }
9393
9394 if (aSetError)
9395 return setError(VBOX_E_OBJECT_NOT_FOUND,
9396 tr("Could not find a storage controller named '%s'"),
9397 aName.c_str());
9398 return VBOX_E_OBJECT_NOT_FOUND;
9399}
9400
9401/**
9402 * Returns a USB controller object with the given name.
9403 *
9404 * @param aName USB controller name to find
9405 * @param aUSBController where to return the found USB controller
9406 * @param aSetError true to set extended error info on failure
9407 */
9408HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9409 ComObjPtr<USBController> &aUSBController,
9410 bool aSetError /* = false */)
9411{
9412 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9413
9414 for (USBControllerList::const_iterator
9415 it = mUSBControllers->begin();
9416 it != mUSBControllers->end();
9417 ++it)
9418 {
9419 if ((*it)->i_getName() == aName)
9420 {
9421 aUSBController = (*it);
9422 return S_OK;
9423 }
9424 }
9425
9426 if (aSetError)
9427 return setError(VBOX_E_OBJECT_NOT_FOUND,
9428 tr("Could not find a storage controller named '%s'"),
9429 aName.c_str());
9430 return VBOX_E_OBJECT_NOT_FOUND;
9431}
9432
9433/**
9434 * Returns the number of USB controller instance of the given type.
9435 *
9436 * @param enmType USB controller type.
9437 */
9438ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9439{
9440 ULONG cCtrls = 0;
9441
9442 for (USBControllerList::const_iterator
9443 it = mUSBControllers->begin();
9444 it != mUSBControllers->end();
9445 ++it)
9446 {
9447 if ((*it)->i_getControllerType() == enmType)
9448 cCtrls++;
9449 }
9450
9451 return cCtrls;
9452}
9453
9454HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9455 MediumAttachmentList &atts)
9456{
9457 AutoCaller autoCaller(this);
9458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9459
9460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9461
9462 for (MediumAttachmentList::const_iterator
9463 it = mMediumAttachments->begin();
9464 it != mMediumAttachments->end();
9465 ++it)
9466 {
9467 const ComObjPtr<MediumAttachment> &pAtt = *it;
9468 // should never happen, but deal with NULL pointers in the list.
9469 AssertContinue(!pAtt.isNull());
9470
9471 // getControllerName() needs caller+read lock
9472 AutoCaller autoAttCaller(pAtt);
9473 if (FAILED(autoAttCaller.rc()))
9474 {
9475 atts.clear();
9476 return autoAttCaller.rc();
9477 }
9478 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9479
9480 if (pAtt->i_getControllerName() == aName)
9481 atts.push_back(pAtt);
9482 }
9483
9484 return S_OK;
9485}
9486
9487
9488/**
9489 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9490 * file if the machine name was changed and about creating a new settings file
9491 * if this is a new machine.
9492 *
9493 * @note Must be never called directly but only from #saveSettings().
9494 */
9495HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9496{
9497 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9498
9499 HRESULT rc = S_OK;
9500
9501 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9502
9503 /// @todo need to handle primary group change, too
9504
9505 /* attempt to rename the settings file if machine name is changed */
9506 if ( mUserData->s.fNameSync
9507 && mUserData.isBackedUp()
9508 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9509 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9510 )
9511 {
9512 bool dirRenamed = false;
9513 bool fileRenamed = false;
9514
9515 Utf8Str configFile, newConfigFile;
9516 Utf8Str configFilePrev, newConfigFilePrev;
9517 Utf8Str NVRAMFile, newNVRAMFile;
9518 Utf8Str configDir, newConfigDir;
9519
9520 do
9521 {
9522 int vrc = VINF_SUCCESS;
9523
9524 Utf8Str name = mUserData.backedUpData()->s.strName;
9525 Utf8Str newName = mUserData->s.strName;
9526 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9527 if (group == "/")
9528 group.setNull();
9529 Utf8Str newGroup = mUserData->s.llGroups.front();
9530 if (newGroup == "/")
9531 newGroup.setNull();
9532
9533 configFile = mData->m_strConfigFileFull;
9534
9535 /* first, rename the directory if it matches the group and machine name */
9536 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9537 /** @todo hack, make somehow use of ComposeMachineFilename */
9538 if (mUserData->s.fDirectoryIncludesUUID)
9539 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9540 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9541 /** @todo hack, make somehow use of ComposeMachineFilename */
9542 if (mUserData->s.fDirectoryIncludesUUID)
9543 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9544 configDir = configFile;
9545 configDir.stripFilename();
9546 newConfigDir = configDir;
9547 if ( configDir.length() >= groupPlusName.length()
9548 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9549 groupPlusName.c_str()))
9550 {
9551 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9552 Utf8Str newConfigBaseDir(newConfigDir);
9553 newConfigDir.append(newGroupPlusName);
9554 /* consistency: use \ if appropriate on the platform */
9555 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9556 /* new dir and old dir cannot be equal here because of 'if'
9557 * above and because name != newName */
9558 Assert(configDir != newConfigDir);
9559 if (!fSettingsFileIsNew)
9560 {
9561 /* perform real rename only if the machine is not new */
9562 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9563 if ( vrc == VERR_FILE_NOT_FOUND
9564 || vrc == VERR_PATH_NOT_FOUND)
9565 {
9566 /* create the parent directory, then retry renaming */
9567 Utf8Str parent(newConfigDir);
9568 parent.stripFilename();
9569 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9570 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9571 }
9572 if (RT_FAILURE(vrc))
9573 {
9574 rc = setErrorBoth(E_FAIL, vrc,
9575 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9576 configDir.c_str(),
9577 newConfigDir.c_str(),
9578 vrc);
9579 break;
9580 }
9581 /* delete subdirectories which are no longer needed */
9582 Utf8Str dir(configDir);
9583 dir.stripFilename();
9584 while (dir != newConfigBaseDir && dir != ".")
9585 {
9586 vrc = RTDirRemove(dir.c_str());
9587 if (RT_FAILURE(vrc))
9588 break;
9589 dir.stripFilename();
9590 }
9591 dirRenamed = true;
9592 }
9593 }
9594
9595 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9596
9597 /* then try to rename the settings file itself */
9598 if (newConfigFile != configFile)
9599 {
9600 /* get the path to old settings file in renamed directory */
9601 configFile.printf("%s%c%s",
9602 newConfigDir.c_str(),
9603 RTPATH_DELIMITER,
9604 RTPathFilename(configFile.c_str()));
9605 if (!fSettingsFileIsNew)
9606 {
9607 /* perform real rename only if the machine is not new */
9608 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9609 if (RT_FAILURE(vrc))
9610 {
9611 rc = setErrorBoth(E_FAIL, vrc,
9612 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9613 configFile.c_str(),
9614 newConfigFile.c_str(),
9615 vrc);
9616 break;
9617 }
9618 fileRenamed = true;
9619 configFilePrev = configFile;
9620 configFilePrev += "-prev";
9621 newConfigFilePrev = newConfigFile;
9622 newConfigFilePrev += "-prev";
9623 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9624 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9625 if (NVRAMFile.isNotEmpty())
9626 {
9627 // in the NVRAM file path, replace the old directory with the new directory
9628 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9629 {
9630 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9631 NVRAMFile = newConfigDir + strNVRAMFile;
9632 }
9633 newNVRAMFile = newConfigFile;
9634 newNVRAMFile.stripSuffix();
9635 newNVRAMFile += ".nvram";
9636 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9637 }
9638 }
9639 }
9640
9641 // update m_strConfigFileFull amd mConfigFile
9642 mData->m_strConfigFileFull = newConfigFile;
9643 // compute the relative path too
9644 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9645
9646 // store the old and new so that VirtualBox::i_saveSettings() can update
9647 // the media registry
9648 if ( mData->mRegistered
9649 && (configDir != newConfigDir || configFile != newConfigFile))
9650 {
9651 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9652
9653 if (pfNeedsGlobalSaveSettings)
9654 *pfNeedsGlobalSaveSettings = true;
9655 }
9656
9657 // in the saved state file path, replace the old directory with the new directory
9658 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9659 {
9660 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9661 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9662 }
9663 if (newNVRAMFile.isNotEmpty())
9664 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9665
9666 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9667 if (mData->mFirstSnapshot)
9668 {
9669 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9670 newConfigDir.c_str());
9671 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9672 newConfigDir.c_str());
9673 }
9674 }
9675 while (0);
9676
9677 if (FAILED(rc))
9678 {
9679 /* silently try to rename everything back */
9680 if (fileRenamed)
9681 {
9682 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9683 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9684 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9685 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9686 }
9687 if (dirRenamed)
9688 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9689 }
9690
9691 if (FAILED(rc)) return rc;
9692 }
9693
9694 if (fSettingsFileIsNew)
9695 {
9696 /* create a virgin config file */
9697 int vrc = VINF_SUCCESS;
9698
9699 /* ensure the settings directory exists */
9700 Utf8Str path(mData->m_strConfigFileFull);
9701 path.stripFilename();
9702 if (!RTDirExists(path.c_str()))
9703 {
9704 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9705 if (RT_FAILURE(vrc))
9706 {
9707 return setErrorBoth(E_FAIL, vrc,
9708 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9709 path.c_str(),
9710 vrc);
9711 }
9712 }
9713
9714 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9715 path = Utf8Str(mData->m_strConfigFileFull);
9716 RTFILE f = NIL_RTFILE;
9717 vrc = RTFileOpen(&f, path.c_str(),
9718 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9719 if (RT_FAILURE(vrc))
9720 return setErrorBoth(E_FAIL, vrc,
9721 tr("Could not create the settings file '%s' (%Rrc)"),
9722 path.c_str(),
9723 vrc);
9724 RTFileClose(f);
9725 }
9726
9727 return rc;
9728}
9729
9730/**
9731 * Saves and commits machine data, user data and hardware data.
9732 *
9733 * Note that on failure, the data remains uncommitted.
9734 *
9735 * @a aFlags may combine the following flags:
9736 *
9737 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9738 * Used when saving settings after an operation that makes them 100%
9739 * correspond to the settings from the current snapshot.
9740 * - SaveS_Force: settings will be saved without doing a deep compare of the
9741 * settings structures. This is used when this is called because snapshots
9742 * have changed to avoid the overhead of the deep compare.
9743 *
9744 * @note Must be called from under this object's write lock. Locks children for
9745 * writing.
9746 *
9747 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9748 * initialized to false and that will be set to true by this function if
9749 * the caller must invoke VirtualBox::i_saveSettings() because the global
9750 * settings have changed. This will happen if a machine rename has been
9751 * saved and the global machine and media registries will therefore need
9752 * updating.
9753 * @param aFlags Flags.
9754 */
9755HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9756 int aFlags /*= 0*/)
9757{
9758 LogFlowThisFuncEnter();
9759
9760 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9761
9762 /* make sure child objects are unable to modify the settings while we are
9763 * saving them */
9764 i_ensureNoStateDependencies();
9765
9766 AssertReturn(!i_isSnapshotMachine(),
9767 E_FAIL);
9768
9769 if (!mData->mAccessible)
9770 return setError(VBOX_E_INVALID_VM_STATE,
9771 tr("The machine is not accessible, so cannot save settings"));
9772
9773 HRESULT rc = S_OK;
9774 bool fNeedsWrite = false;
9775
9776 /* First, prepare to save settings. It will care about renaming the
9777 * settings directory and file if the machine name was changed and about
9778 * creating a new settings file if this is a new machine. */
9779 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9780 if (FAILED(rc)) return rc;
9781
9782 // keep a pointer to the current settings structures
9783 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9784 settings::MachineConfigFile *pNewConfig = NULL;
9785
9786 try
9787 {
9788 // make a fresh one to have everyone write stuff into
9789 pNewConfig = new settings::MachineConfigFile(NULL);
9790 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9791
9792 // now go and copy all the settings data from COM to the settings structures
9793 // (this calls i_saveSettings() on all the COM objects in the machine)
9794 i_copyMachineDataToSettings(*pNewConfig);
9795
9796 if (aFlags & SaveS_ResetCurStateModified)
9797 {
9798 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9799 mData->mCurrentStateModified = FALSE;
9800 fNeedsWrite = true; // always, no need to compare
9801 }
9802 else if (aFlags & SaveS_Force)
9803 {
9804 fNeedsWrite = true; // always, no need to compare
9805 }
9806 else
9807 {
9808 if (!mData->mCurrentStateModified)
9809 {
9810 // do a deep compare of the settings that we just saved with the settings
9811 // previously stored in the config file; this invokes MachineConfigFile::operator==
9812 // which does a deep compare of all the settings, which is expensive but less expensive
9813 // than writing out XML in vain
9814 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9815
9816 // could still be modified if any settings changed
9817 mData->mCurrentStateModified = fAnySettingsChanged;
9818
9819 fNeedsWrite = fAnySettingsChanged;
9820 }
9821 else
9822 fNeedsWrite = true;
9823 }
9824
9825 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9826
9827 if (fNeedsWrite)
9828 // now spit it all out!
9829 pNewConfig->write(mData->m_strConfigFileFull);
9830
9831 mData->pMachineConfigFile = pNewConfig;
9832 delete pOldConfig;
9833 i_commit();
9834
9835 // after saving settings, we are no longer different from the XML on disk
9836 mData->flModifications = 0;
9837 }
9838 catch (HRESULT err)
9839 {
9840 // we assume that error info is set by the thrower
9841 rc = err;
9842
9843 // restore old config
9844 delete pNewConfig;
9845 mData->pMachineConfigFile = pOldConfig;
9846 }
9847 catch (...)
9848 {
9849 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9850 }
9851
9852 if (fNeedsWrite)
9853 {
9854 /* Fire the data change event, even on failure (since we've already
9855 * committed all data). This is done only for SessionMachines because
9856 * mutable Machine instances are always not registered (i.e. private
9857 * to the client process that creates them) and thus don't need to
9858 * inform callbacks. */
9859 if (i_isSessionMachine())
9860 mParent->i_onMachineDataChanged(mData->mUuid);
9861 }
9862
9863 LogFlowThisFunc(("rc=%08X\n", rc));
9864 LogFlowThisFuncLeave();
9865 return rc;
9866}
9867
9868/**
9869 * Implementation for saving the machine settings into the given
9870 * settings::MachineConfigFile instance. This copies machine extradata
9871 * from the previous machine config file in the instance data, if any.
9872 *
9873 * This gets called from two locations:
9874 *
9875 * -- Machine::i_saveSettings(), during the regular XML writing;
9876 *
9877 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9878 * exported to OVF and we write the VirtualBox proprietary XML
9879 * into a <vbox:Machine> tag.
9880 *
9881 * This routine fills all the fields in there, including snapshots, *except*
9882 * for the following:
9883 *
9884 * -- fCurrentStateModified. There is some special logic associated with that.
9885 *
9886 * The caller can then call MachineConfigFile::write() or do something else
9887 * with it.
9888 *
9889 * Caller must hold the machine lock!
9890 *
9891 * This throws XML errors and HRESULT, so the caller must have a catch block!
9892 */
9893void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9894{
9895 // deep copy extradata, being extra careful with self assignment (the STL
9896 // map assignment on Mac OS X clang based Xcode isn't checking)
9897 if (&config != mData->pMachineConfigFile)
9898 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9899
9900 config.uuid = mData->mUuid;
9901
9902 // copy name, description, OS type, teleport, UTC etc.
9903 config.machineUserData = mUserData->s;
9904
9905 if ( mData->mMachineState == MachineState_Saved
9906 || mData->mMachineState == MachineState_Restoring
9907 // when doing certain snapshot operations we may or may not have
9908 // a saved state in the current state, so keep everything as is
9909 || ( ( mData->mMachineState == MachineState_Snapshotting
9910 || mData->mMachineState == MachineState_DeletingSnapshot
9911 || mData->mMachineState == MachineState_RestoringSnapshot)
9912 && (!mSSData->strStateFilePath.isEmpty())
9913 )
9914 )
9915 {
9916 Assert(!mSSData->strStateFilePath.isEmpty());
9917 /* try to make the file name relative to the settings file dir */
9918 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9919 }
9920 else
9921 {
9922 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9923 config.strStateFile.setNull();
9924 }
9925
9926 if (mData->mCurrentSnapshot)
9927 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9928 else
9929 config.uuidCurrentSnapshot.clear();
9930
9931 config.timeLastStateChange = mData->mLastStateChange;
9932 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9933 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9934
9935 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9936 if (FAILED(rc)) throw rc;
9937
9938 // save machine's media registry if this is VirtualBox 4.0 or later
9939 if (config.canHaveOwnMediaRegistry())
9940 {
9941 // determine machine folder
9942 Utf8Str strMachineFolder = i_getSettingsFileFull();
9943 strMachineFolder.stripFilename();
9944 mParent->i_saveMediaRegistry(config.mediaRegistry,
9945 i_getId(), // only media with registry ID == machine UUID
9946 strMachineFolder);
9947 // this throws HRESULT
9948 }
9949
9950 // save snapshots
9951 rc = i_saveAllSnapshots(config);
9952 if (FAILED(rc)) throw rc;
9953}
9954
9955/**
9956 * Saves all snapshots of the machine into the given machine config file. Called
9957 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9958 * @param config
9959 * @return
9960 */
9961HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9962{
9963 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9964
9965 HRESULT rc = S_OK;
9966
9967 try
9968 {
9969 config.llFirstSnapshot.clear();
9970
9971 if (mData->mFirstSnapshot)
9972 {
9973 // the settings use a list for "the first snapshot"
9974 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9975
9976 // get reference to the snapshot on the list and work on that
9977 // element straight in the list to avoid excessive copying later
9978 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9979 if (FAILED(rc)) throw rc;
9980 }
9981
9982// if (mType == IsSessionMachine)
9983// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9984
9985 }
9986 catch (HRESULT err)
9987 {
9988 /* we assume that error info is set by the thrower */
9989 rc = err;
9990 }
9991 catch (...)
9992 {
9993 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9994 }
9995
9996 return rc;
9997}
9998
9999/**
10000 * Saves the VM hardware configuration. It is assumed that the
10001 * given node is empty.
10002 *
10003 * @param data Reference to the settings object for the hardware config.
10004 * @param pDbg Pointer to the settings object for the debugging config
10005 * which happens to live in mHWData.
10006 * @param pAutostart Pointer to the settings object for the autostart config
10007 * which happens to live in mHWData.
10008 */
10009HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10010 settings::Autostart *pAutostart)
10011{
10012 HRESULT rc = S_OK;
10013
10014 try
10015 {
10016 /* The hardware version attribute (optional).
10017 Automatically upgrade from 1 to current default hardware version
10018 when there is no saved state. (ugly!) */
10019 if ( mHWData->mHWVersion == "1"
10020 && mSSData->strStateFilePath.isEmpty()
10021 )
10022 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10023
10024 data.strVersion = mHWData->mHWVersion;
10025 data.uuid = mHWData->mHardwareUUID;
10026
10027 // CPU
10028 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10029 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10030 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10031 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10032 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10033 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10034 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10035 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10036 data.fPAE = !!mHWData->mPAEEnabled;
10037 data.enmLongMode = mHWData->mLongMode;
10038 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10039 data.fAPIC = !!mHWData->mAPIC;
10040 data.fX2APIC = !!mHWData->mX2APIC;
10041 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10042 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10043 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10044 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10045 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10046 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10047 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10048 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10049 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10050 data.cCPUs = mHWData->mCPUCount;
10051 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10052 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10053 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10054 data.strCpuProfile = mHWData->mCpuProfile;
10055
10056 data.llCpus.clear();
10057 if (data.fCpuHotPlug)
10058 {
10059 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10060 {
10061 if (mHWData->mCPUAttached[idx])
10062 {
10063 settings::Cpu cpu;
10064 cpu.ulId = idx;
10065 data.llCpus.push_back(cpu);
10066 }
10067 }
10068 }
10069
10070 /* Standard and Extended CPUID leafs. */
10071 data.llCpuIdLeafs.clear();
10072 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10073
10074 // memory
10075 data.ulMemorySizeMB = mHWData->mMemorySize;
10076 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10077
10078 // firmware
10079 data.firmwareType = mHWData->mFirmwareType;
10080
10081 // HID
10082 data.pointingHIDType = mHWData->mPointingHIDType;
10083 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10084
10085 // chipset
10086 data.chipsetType = mHWData->mChipsetType;
10087
10088 // paravirt
10089 data.paravirtProvider = mHWData->mParavirtProvider;
10090 data.strParavirtDebug = mHWData->mParavirtDebug;
10091
10092 // emulated USB card reader
10093 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10094
10095 // HPET
10096 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10097
10098 // boot order
10099 data.mapBootOrder.clear();
10100 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10101 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10102
10103 /* VRDEServer settings (optional) */
10104 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10105 if (FAILED(rc)) throw rc;
10106
10107 /* BIOS settings (required) */
10108 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10109 if (FAILED(rc)) throw rc;
10110
10111 /* Recording settings (required) */
10112 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10113 if (FAILED(rc)) throw rc;
10114
10115 /* GraphicsAdapter settings (required) */
10116 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10117 if (FAILED(rc)) throw rc;
10118
10119 /* USB Controller (required) */
10120 data.usbSettings.llUSBControllers.clear();
10121 for (USBControllerList::const_iterator
10122 it = mUSBControllers->begin();
10123 it != mUSBControllers->end();
10124 ++it)
10125 {
10126 ComObjPtr<USBController> ctrl = *it;
10127 settings::USBController settingsCtrl;
10128
10129 settingsCtrl.strName = ctrl->i_getName();
10130 settingsCtrl.enmType = ctrl->i_getControllerType();
10131
10132 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10133 }
10134
10135 /* USB device filters (required) */
10136 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10137 if (FAILED(rc)) throw rc;
10138
10139 /* Network adapters (required) */
10140 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10141 data.llNetworkAdapters.clear();
10142 /* Write out only the nominal number of network adapters for this
10143 * chipset type. Since Machine::commit() hasn't been called there
10144 * may be extra NIC settings in the vector. */
10145 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10146 {
10147 settings::NetworkAdapter nic;
10148 nic.ulSlot = (uint32_t)slot;
10149 /* paranoia check... must not be NULL, but must not crash either. */
10150 if (mNetworkAdapters[slot])
10151 {
10152 if (mNetworkAdapters[slot]->i_hasDefaults())
10153 continue;
10154
10155 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10156 if (FAILED(rc)) throw rc;
10157
10158 data.llNetworkAdapters.push_back(nic);
10159 }
10160 }
10161
10162 /* Serial ports */
10163 data.llSerialPorts.clear();
10164 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10165 {
10166 if (mSerialPorts[slot]->i_hasDefaults())
10167 continue;
10168
10169 settings::SerialPort s;
10170 s.ulSlot = slot;
10171 rc = mSerialPorts[slot]->i_saveSettings(s);
10172 if (FAILED(rc)) return rc;
10173
10174 data.llSerialPorts.push_back(s);
10175 }
10176
10177 /* Parallel ports */
10178 data.llParallelPorts.clear();
10179 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10180 {
10181 if (mParallelPorts[slot]->i_hasDefaults())
10182 continue;
10183
10184 settings::ParallelPort p;
10185 p.ulSlot = slot;
10186 rc = mParallelPorts[slot]->i_saveSettings(p);
10187 if (FAILED(rc)) return rc;
10188
10189 data.llParallelPorts.push_back(p);
10190 }
10191
10192 /* Audio adapter */
10193 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10194 if (FAILED(rc)) return rc;
10195
10196 rc = i_saveStorageControllers(data.storage);
10197 if (FAILED(rc)) return rc;
10198
10199 /* Shared folders */
10200 data.llSharedFolders.clear();
10201 for (HWData::SharedFolderList::const_iterator
10202 it = mHWData->mSharedFolders.begin();
10203 it != mHWData->mSharedFolders.end();
10204 ++it)
10205 {
10206 SharedFolder *pSF = *it;
10207 AutoCaller sfCaller(pSF);
10208 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10209 settings::SharedFolder sf;
10210 sf.strName = pSF->i_getName();
10211 sf.strHostPath = pSF->i_getHostPath();
10212 sf.fWritable = !!pSF->i_isWritable();
10213 sf.fAutoMount = !!pSF->i_isAutoMounted();
10214 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10215
10216 data.llSharedFolders.push_back(sf);
10217 }
10218
10219 // clipboard
10220 data.clipboardMode = mHWData->mClipboardMode;
10221 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10222
10223 // drag'n'drop
10224 data.dndMode = mHWData->mDnDMode;
10225
10226 /* Guest */
10227 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10228
10229 // IO settings
10230 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10231 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10232
10233 /* BandwidthControl (required) */
10234 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10235 if (FAILED(rc)) throw rc;
10236
10237 /* Host PCI devices */
10238 data.pciAttachments.clear();
10239 for (HWData::PCIDeviceAssignmentList::const_iterator
10240 it = mHWData->mPCIDeviceAssignments.begin();
10241 it != mHWData->mPCIDeviceAssignments.end();
10242 ++it)
10243 {
10244 ComObjPtr<PCIDeviceAttachment> pda = *it;
10245 settings::HostPCIDeviceAttachment hpda;
10246
10247 rc = pda->i_saveSettings(hpda);
10248 if (FAILED(rc)) throw rc;
10249
10250 data.pciAttachments.push_back(hpda);
10251 }
10252
10253 // guest properties
10254 data.llGuestProperties.clear();
10255#ifdef VBOX_WITH_GUEST_PROPS
10256 for (HWData::GuestPropertyMap::const_iterator
10257 it = mHWData->mGuestProperties.begin();
10258 it != mHWData->mGuestProperties.end();
10259 ++it)
10260 {
10261 HWData::GuestProperty property = it->second;
10262
10263 /* Remove transient guest properties at shutdown unless we
10264 * are saving state. Note that restoring snapshot intentionally
10265 * keeps them, they will be removed if appropriate once the final
10266 * machine state is set (as crashes etc. need to work). */
10267 if ( ( mData->mMachineState == MachineState_PoweredOff
10268 || mData->mMachineState == MachineState_Aborted
10269 || mData->mMachineState == MachineState_Teleported)
10270 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10271 continue;
10272 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10273 prop.strName = it->first;
10274 prop.strValue = property.strValue;
10275 prop.timestamp = (uint64_t)property.mTimestamp;
10276 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10277 GuestPropWriteFlags(property.mFlags, szFlags);
10278 prop.strFlags = szFlags;
10279
10280 data.llGuestProperties.push_back(prop);
10281 }
10282
10283 /* I presume this doesn't require a backup(). */
10284 mData->mGuestPropertiesModified = FALSE;
10285#endif /* VBOX_WITH_GUEST_PROPS defined */
10286
10287 *pDbg = mHWData->mDebugging;
10288 *pAutostart = mHWData->mAutostart;
10289
10290 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10291 }
10292 catch (std::bad_alloc &)
10293 {
10294 return E_OUTOFMEMORY;
10295 }
10296
10297 AssertComRC(rc);
10298 return rc;
10299}
10300
10301/**
10302 * Saves the storage controller configuration.
10303 *
10304 * @param data storage settings.
10305 */
10306HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10307{
10308 data.llStorageControllers.clear();
10309
10310 for (StorageControllerList::const_iterator
10311 it = mStorageControllers->begin();
10312 it != mStorageControllers->end();
10313 ++it)
10314 {
10315 HRESULT rc;
10316 ComObjPtr<StorageController> pCtl = *it;
10317
10318 settings::StorageController ctl;
10319 ctl.strName = pCtl->i_getName();
10320 ctl.controllerType = pCtl->i_getControllerType();
10321 ctl.storageBus = pCtl->i_getStorageBus();
10322 ctl.ulInstance = pCtl->i_getInstance();
10323 ctl.fBootable = pCtl->i_getBootable();
10324
10325 /* Save the port count. */
10326 ULONG portCount;
10327 rc = pCtl->COMGETTER(PortCount)(&portCount);
10328 ComAssertComRCRet(rc, rc);
10329 ctl.ulPortCount = portCount;
10330
10331 /* Save fUseHostIOCache */
10332 BOOL fUseHostIOCache;
10333 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10334 ComAssertComRCRet(rc, rc);
10335 ctl.fUseHostIOCache = !!fUseHostIOCache;
10336
10337 /* save the devices now. */
10338 rc = i_saveStorageDevices(pCtl, ctl);
10339 ComAssertComRCRet(rc, rc);
10340
10341 data.llStorageControllers.push_back(ctl);
10342 }
10343
10344 return S_OK;
10345}
10346
10347/**
10348 * Saves the hard disk configuration.
10349 */
10350HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10351 settings::StorageController &data)
10352{
10353 MediumAttachmentList atts;
10354
10355 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10356 if (FAILED(rc)) return rc;
10357
10358 data.llAttachedDevices.clear();
10359 for (MediumAttachmentList::const_iterator
10360 it = atts.begin();
10361 it != atts.end();
10362 ++it)
10363 {
10364 settings::AttachedDevice dev;
10365 IMediumAttachment *iA = *it;
10366 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10367 Medium *pMedium = pAttach->i_getMedium();
10368
10369 dev.deviceType = pAttach->i_getType();
10370 dev.lPort = pAttach->i_getPort();
10371 dev.lDevice = pAttach->i_getDevice();
10372 dev.fPassThrough = pAttach->i_getPassthrough();
10373 dev.fHotPluggable = pAttach->i_getHotPluggable();
10374 if (pMedium)
10375 {
10376 if (pMedium->i_isHostDrive())
10377 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10378 else
10379 dev.uuid = pMedium->i_getId();
10380 dev.fTempEject = pAttach->i_getTempEject();
10381 dev.fNonRotational = pAttach->i_getNonRotational();
10382 dev.fDiscard = pAttach->i_getDiscard();
10383 }
10384
10385 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10386
10387 data.llAttachedDevices.push_back(dev);
10388 }
10389
10390 return S_OK;
10391}
10392
10393/**
10394 * Saves machine state settings as defined by aFlags
10395 * (SaveSTS_* values).
10396 *
10397 * @param aFlags Combination of SaveSTS_* flags.
10398 *
10399 * @note Locks objects for writing.
10400 */
10401HRESULT Machine::i_saveStateSettings(int aFlags)
10402{
10403 if (aFlags == 0)
10404 return S_OK;
10405
10406 AutoCaller autoCaller(this);
10407 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10408
10409 /* This object's write lock is also necessary to serialize file access
10410 * (prevent concurrent reads and writes) */
10411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10412
10413 HRESULT rc = S_OK;
10414
10415 Assert(mData->pMachineConfigFile);
10416
10417 try
10418 {
10419 if (aFlags & SaveSTS_CurStateModified)
10420 mData->pMachineConfigFile->fCurrentStateModified = true;
10421
10422 if (aFlags & SaveSTS_StateFilePath)
10423 {
10424 if (!mSSData->strStateFilePath.isEmpty())
10425 /* try to make the file name relative to the settings file dir */
10426 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10427 else
10428 mData->pMachineConfigFile->strStateFile.setNull();
10429 }
10430
10431 if (aFlags & SaveSTS_StateTimeStamp)
10432 {
10433 Assert( mData->mMachineState != MachineState_Aborted
10434 || mSSData->strStateFilePath.isEmpty());
10435
10436 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10437
10438 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10439/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10440 }
10441
10442 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10443 }
10444 catch (...)
10445 {
10446 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10447 }
10448
10449 return rc;
10450}
10451
10452/**
10453 * Ensures that the given medium is added to a media registry. If this machine
10454 * was created with 4.0 or later, then the machine registry is used. Otherwise
10455 * the global VirtualBox media registry is used.
10456 *
10457 * Caller must NOT hold machine lock, media tree or any medium locks!
10458 *
10459 * @param pMedium
10460 */
10461void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10462{
10463 /* Paranoia checks: do not hold machine or media tree locks. */
10464 AssertReturnVoid(!isWriteLockOnCurrentThread());
10465 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10466
10467 ComObjPtr<Medium> pBase;
10468 {
10469 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10470 pBase = pMedium->i_getBase();
10471 }
10472
10473 /* Paranoia checks: do not hold medium locks. */
10474 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10475 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10476
10477 // decide which medium registry to use now that the medium is attached:
10478 Guid uuid;
10479 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10480 if (fCanHaveOwnMediaRegistry)
10481 // machine XML is VirtualBox 4.0 or higher:
10482 uuid = i_getId(); // machine UUID
10483 else
10484 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10485
10486 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10487 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10488 if (pMedium->i_addRegistry(uuid))
10489 mParent->i_markRegistryModified(uuid);
10490
10491 /* For more complex hard disk structures it can happen that the base
10492 * medium isn't yet associated with any medium registry. Do that now. */
10493 if (pMedium != pBase)
10494 {
10495 /* Tree lock needed by Medium::addRegistry when recursing. */
10496 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10497 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10498 {
10499 treeLock.release();
10500 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10501 treeLock.acquire();
10502 }
10503 if (pBase->i_addRegistryRecursive(uuid))
10504 {
10505 treeLock.release();
10506 mParent->i_markRegistryModified(uuid);
10507 }
10508 }
10509}
10510
10511/**
10512 * Creates differencing hard disks for all normal hard disks attached to this
10513 * machine and a new set of attachments to refer to created disks.
10514 *
10515 * Used when taking a snapshot or when deleting the current state. Gets called
10516 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10517 *
10518 * This method assumes that mMediumAttachments contains the original hard disk
10519 * attachments it needs to create diffs for. On success, these attachments will
10520 * be replaced with the created diffs.
10521 *
10522 * Attachments with non-normal hard disks are left as is.
10523 *
10524 * If @a aOnline is @c false then the original hard disks that require implicit
10525 * diffs will be locked for reading. Otherwise it is assumed that they are
10526 * already locked for writing (when the VM was started). Note that in the latter
10527 * case it is responsibility of the caller to lock the newly created diffs for
10528 * writing if this method succeeds.
10529 *
10530 * @param aProgress Progress object to run (must contain at least as
10531 * many operations left as the number of hard disks
10532 * attached).
10533 * @param aWeight Weight of this operation.
10534 * @param aOnline Whether the VM was online prior to this operation.
10535 *
10536 * @note The progress object is not marked as completed, neither on success nor
10537 * on failure. This is a responsibility of the caller.
10538 *
10539 * @note Locks this object and the media tree for writing.
10540 */
10541HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10542 ULONG aWeight,
10543 bool aOnline)
10544{
10545 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10546
10547 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10548 AssertReturn(!!pProgressControl, E_INVALIDARG);
10549
10550 AutoCaller autoCaller(this);
10551 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10552
10553 AutoMultiWriteLock2 alock(this->lockHandle(),
10554 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10555
10556 /* must be in a protective state because we release the lock below */
10557 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10558 || mData->mMachineState == MachineState_OnlineSnapshotting
10559 || mData->mMachineState == MachineState_LiveSnapshotting
10560 || mData->mMachineState == MachineState_RestoringSnapshot
10561 || mData->mMachineState == MachineState_DeletingSnapshot
10562 , E_FAIL);
10563
10564 HRESULT rc = S_OK;
10565
10566 // use appropriate locked media map (online or offline)
10567 MediumLockListMap lockedMediaOffline;
10568 MediumLockListMap *lockedMediaMap;
10569 if (aOnline)
10570 lockedMediaMap = &mData->mSession.mLockedMedia;
10571 else
10572 lockedMediaMap = &lockedMediaOffline;
10573
10574 try
10575 {
10576 if (!aOnline)
10577 {
10578 /* lock all attached hard disks early to detect "in use"
10579 * situations before creating actual diffs */
10580 for (MediumAttachmentList::const_iterator
10581 it = mMediumAttachments->begin();
10582 it != mMediumAttachments->end();
10583 ++it)
10584 {
10585 MediumAttachment *pAtt = *it;
10586 if (pAtt->i_getType() == DeviceType_HardDisk)
10587 {
10588 Medium *pMedium = pAtt->i_getMedium();
10589 Assert(pMedium);
10590
10591 MediumLockList *pMediumLockList(new MediumLockList());
10592 alock.release();
10593 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10594 NULL /* pToLockWrite */,
10595 false /* fMediumLockWriteAll */,
10596 NULL,
10597 *pMediumLockList);
10598 alock.acquire();
10599 if (FAILED(rc))
10600 {
10601 delete pMediumLockList;
10602 throw rc;
10603 }
10604 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10605 if (FAILED(rc))
10606 {
10607 throw setError(rc,
10608 tr("Collecting locking information for all attached media failed"));
10609 }
10610 }
10611 }
10612
10613 /* Now lock all media. If this fails, nothing is locked. */
10614 alock.release();
10615 rc = lockedMediaMap->Lock();
10616 alock.acquire();
10617 if (FAILED(rc))
10618 {
10619 throw setError(rc,
10620 tr("Locking of attached media failed"));
10621 }
10622 }
10623
10624 /* remember the current list (note that we don't use backup() since
10625 * mMediumAttachments may be already backed up) */
10626 MediumAttachmentList atts = *mMediumAttachments.data();
10627
10628 /* start from scratch */
10629 mMediumAttachments->clear();
10630
10631 /* go through remembered attachments and create diffs for normal hard
10632 * disks and attach them */
10633 for (MediumAttachmentList::const_iterator
10634 it = atts.begin();
10635 it != atts.end();
10636 ++it)
10637 {
10638 MediumAttachment *pAtt = *it;
10639
10640 DeviceType_T devType = pAtt->i_getType();
10641 Medium *pMedium = pAtt->i_getMedium();
10642
10643 if ( devType != DeviceType_HardDisk
10644 || pMedium == NULL
10645 || pMedium->i_getType() != MediumType_Normal)
10646 {
10647 /* copy the attachment as is */
10648
10649 /** @todo the progress object created in SessionMachine::TakeSnaphot
10650 * only expects operations for hard disks. Later other
10651 * device types need to show up in the progress as well. */
10652 if (devType == DeviceType_HardDisk)
10653 {
10654 if (pMedium == NULL)
10655 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10656 aWeight); // weight
10657 else
10658 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10659 pMedium->i_getBase()->i_getName().c_str()).raw(),
10660 aWeight); // weight
10661 }
10662
10663 mMediumAttachments->push_back(pAtt);
10664 continue;
10665 }
10666
10667 /* need a diff */
10668 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10669 pMedium->i_getBase()->i_getName().c_str()).raw(),
10670 aWeight); // weight
10671
10672 Utf8Str strFullSnapshotFolder;
10673 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10674
10675 ComObjPtr<Medium> diff;
10676 diff.createObject();
10677 // store the diff in the same registry as the parent
10678 // (this cannot fail here because we can't create implicit diffs for
10679 // unregistered images)
10680 Guid uuidRegistryParent;
10681 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10682 Assert(fInRegistry); NOREF(fInRegistry);
10683 rc = diff->init(mParent,
10684 pMedium->i_getPreferredDiffFormat(),
10685 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10686 uuidRegistryParent,
10687 DeviceType_HardDisk);
10688 if (FAILED(rc)) throw rc;
10689
10690 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10691 * the push_back? Looks like we're going to release medium with the
10692 * wrong kind of lock (general issue with if we fail anywhere at all)
10693 * and an orphaned VDI in the snapshots folder. */
10694
10695 /* update the appropriate lock list */
10696 MediumLockList *pMediumLockList;
10697 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10698 AssertComRCThrowRC(rc);
10699 if (aOnline)
10700 {
10701 alock.release();
10702 /* The currently attached medium will be read-only, change
10703 * the lock type to read. */
10704 rc = pMediumLockList->Update(pMedium, false);
10705 alock.acquire();
10706 AssertComRCThrowRC(rc);
10707 }
10708
10709 /* release the locks before the potentially lengthy operation */
10710 alock.release();
10711 rc = pMedium->i_createDiffStorage(diff,
10712 pMedium->i_getPreferredDiffVariant(),
10713 pMediumLockList,
10714 NULL /* aProgress */,
10715 true /* aWait */,
10716 false /* aNotify */);
10717 alock.acquire();
10718 if (FAILED(rc)) throw rc;
10719
10720 /* actual lock list update is done in Machine::i_commitMedia */
10721
10722 rc = diff->i_addBackReference(mData->mUuid);
10723 AssertComRCThrowRC(rc);
10724
10725 /* add a new attachment */
10726 ComObjPtr<MediumAttachment> attachment;
10727 attachment.createObject();
10728 rc = attachment->init(this,
10729 diff,
10730 pAtt->i_getControllerName(),
10731 pAtt->i_getPort(),
10732 pAtt->i_getDevice(),
10733 DeviceType_HardDisk,
10734 true /* aImplicit */,
10735 false /* aPassthrough */,
10736 false /* aTempEject */,
10737 pAtt->i_getNonRotational(),
10738 pAtt->i_getDiscard(),
10739 pAtt->i_getHotPluggable(),
10740 pAtt->i_getBandwidthGroup());
10741 if (FAILED(rc)) throw rc;
10742
10743 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10744 AssertComRCThrowRC(rc);
10745 mMediumAttachments->push_back(attachment);
10746 }
10747 }
10748 catch (HRESULT aRC) { rc = aRC; }
10749
10750 /* unlock all hard disks we locked when there is no VM */
10751 if (!aOnline)
10752 {
10753 ErrorInfoKeeper eik;
10754
10755 HRESULT rc1 = lockedMediaMap->Clear();
10756 AssertComRC(rc1);
10757 }
10758
10759 return rc;
10760}
10761
10762/**
10763 * Deletes implicit differencing hard disks created either by
10764 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10765 * mMediumAttachments.
10766 *
10767 * Note that to delete hard disks created by #attachDevice() this method is
10768 * called from #i_rollbackMedia() when the changes are rolled back.
10769 *
10770 * @note Locks this object and the media tree for writing.
10771 */
10772HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10773{
10774 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10775
10776 AutoCaller autoCaller(this);
10777 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10778
10779 AutoMultiWriteLock2 alock(this->lockHandle(),
10780 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10781
10782 /* We absolutely must have backed up state. */
10783 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10784
10785 /* Check if there are any implicitly created diff images. */
10786 bool fImplicitDiffs = false;
10787 for (MediumAttachmentList::const_iterator
10788 it = mMediumAttachments->begin();
10789 it != mMediumAttachments->end();
10790 ++it)
10791 {
10792 const ComObjPtr<MediumAttachment> &pAtt = *it;
10793 if (pAtt->i_isImplicit())
10794 {
10795 fImplicitDiffs = true;
10796 break;
10797 }
10798 }
10799 /* If there is nothing to do, leave early. This saves lots of image locking
10800 * effort. It also avoids a MachineStateChanged event without real reason.
10801 * This is important e.g. when loading a VM config, because there should be
10802 * no events. Otherwise API clients can become thoroughly confused for
10803 * inaccessible VMs (the code for loading VM configs uses this method for
10804 * cleanup if the config makes no sense), as they take such events as an
10805 * indication that the VM is alive, and they would force the VM config to
10806 * be reread, leading to an endless loop. */
10807 if (!fImplicitDiffs)
10808 return S_OK;
10809
10810 HRESULT rc = S_OK;
10811 MachineState_T oldState = mData->mMachineState;
10812
10813 /* will release the lock before the potentially lengthy operation,
10814 * so protect with the special state (unless already protected) */
10815 if ( oldState != MachineState_Snapshotting
10816 && oldState != MachineState_OnlineSnapshotting
10817 && oldState != MachineState_LiveSnapshotting
10818 && oldState != MachineState_RestoringSnapshot
10819 && oldState != MachineState_DeletingSnapshot
10820 && oldState != MachineState_DeletingSnapshotOnline
10821 && oldState != MachineState_DeletingSnapshotPaused
10822 )
10823 i_setMachineState(MachineState_SettingUp);
10824
10825 // use appropriate locked media map (online or offline)
10826 MediumLockListMap lockedMediaOffline;
10827 MediumLockListMap *lockedMediaMap;
10828 if (aOnline)
10829 lockedMediaMap = &mData->mSession.mLockedMedia;
10830 else
10831 lockedMediaMap = &lockedMediaOffline;
10832
10833 try
10834 {
10835 if (!aOnline)
10836 {
10837 /* lock all attached hard disks early to detect "in use"
10838 * situations before deleting actual diffs */
10839 for (MediumAttachmentList::const_iterator
10840 it = mMediumAttachments->begin();
10841 it != mMediumAttachments->end();
10842 ++it)
10843 {
10844 MediumAttachment *pAtt = *it;
10845 if (pAtt->i_getType() == DeviceType_HardDisk)
10846 {
10847 Medium *pMedium = pAtt->i_getMedium();
10848 Assert(pMedium);
10849
10850 MediumLockList *pMediumLockList(new MediumLockList());
10851 alock.release();
10852 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10853 NULL /* pToLockWrite */,
10854 false /* fMediumLockWriteAll */,
10855 NULL,
10856 *pMediumLockList);
10857 alock.acquire();
10858
10859 if (FAILED(rc))
10860 {
10861 delete pMediumLockList;
10862 throw rc;
10863 }
10864
10865 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10866 if (FAILED(rc))
10867 throw rc;
10868 }
10869 }
10870
10871 if (FAILED(rc))
10872 throw rc;
10873 } // end of offline
10874
10875 /* Lock lists are now up to date and include implicitly created media */
10876
10877 /* Go through remembered attachments and delete all implicitly created
10878 * diffs and fix up the attachment information */
10879 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10880 MediumAttachmentList implicitAtts;
10881 for (MediumAttachmentList::const_iterator
10882 it = mMediumAttachments->begin();
10883 it != mMediumAttachments->end();
10884 ++it)
10885 {
10886 ComObjPtr<MediumAttachment> pAtt = *it;
10887 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10888 if (pMedium.isNull())
10889 continue;
10890
10891 // Implicit attachments go on the list for deletion and back references are removed.
10892 if (pAtt->i_isImplicit())
10893 {
10894 /* Deassociate and mark for deletion */
10895 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10896 rc = pMedium->i_removeBackReference(mData->mUuid);
10897 if (FAILED(rc))
10898 throw rc;
10899 implicitAtts.push_back(pAtt);
10900 continue;
10901 }
10902
10903 /* Was this medium attached before? */
10904 if (!i_findAttachment(oldAtts, pMedium))
10905 {
10906 /* no: de-associate */
10907 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10908 rc = pMedium->i_removeBackReference(mData->mUuid);
10909 if (FAILED(rc))
10910 throw rc;
10911 continue;
10912 }
10913 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10914 }
10915
10916 /* If there are implicit attachments to delete, throw away the lock
10917 * map contents (which will unlock all media) since the medium
10918 * attachments will be rolled back. Below we need to completely
10919 * recreate the lock map anyway since it is infinitely complex to
10920 * do this incrementally (would need reconstructing each attachment
10921 * change, which would be extremely hairy). */
10922 if (implicitAtts.size() != 0)
10923 {
10924 ErrorInfoKeeper eik;
10925
10926 HRESULT rc1 = lockedMediaMap->Clear();
10927 AssertComRC(rc1);
10928 }
10929
10930 /* rollback hard disk changes */
10931 mMediumAttachments.rollback();
10932
10933 MultiResult mrc(S_OK);
10934
10935 // Delete unused implicit diffs.
10936 if (implicitAtts.size() != 0)
10937 {
10938 alock.release();
10939
10940 for (MediumAttachmentList::const_iterator
10941 it = implicitAtts.begin();
10942 it != implicitAtts.end();
10943 ++it)
10944 {
10945 // Remove medium associated with this attachment.
10946 ComObjPtr<MediumAttachment> pAtt = *it;
10947 Assert(pAtt);
10948 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10949 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10950 Assert(pMedium);
10951
10952 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10953 // continue on delete failure, just collect error messages
10954 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10955 pMedium->i_getLocationFull().c_str() ));
10956 mrc = rc;
10957 }
10958 // Clear the list of deleted implicit attachments now, while not
10959 // holding the lock, as it will ultimately trigger Medium::uninit()
10960 // calls which assume that the media tree lock isn't held.
10961 implicitAtts.clear();
10962
10963 alock.acquire();
10964
10965 /* if there is a VM recreate media lock map as mentioned above,
10966 * otherwise it is a waste of time and we leave things unlocked */
10967 if (aOnline)
10968 {
10969 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10970 /* must never be NULL, but better safe than sorry */
10971 if (!pMachine.isNull())
10972 {
10973 alock.release();
10974 rc = mData->mSession.mMachine->i_lockMedia();
10975 alock.acquire();
10976 if (FAILED(rc))
10977 throw rc;
10978 }
10979 }
10980 }
10981 }
10982 catch (HRESULT aRC) {rc = aRC;}
10983
10984 if (mData->mMachineState == MachineState_SettingUp)
10985 i_setMachineState(oldState);
10986
10987 /* unlock all hard disks we locked when there is no VM */
10988 if (!aOnline)
10989 {
10990 ErrorInfoKeeper eik;
10991
10992 HRESULT rc1 = lockedMediaMap->Clear();
10993 AssertComRC(rc1);
10994 }
10995
10996 return rc;
10997}
10998
10999
11000/**
11001 * Looks through the given list of media attachments for one with the given parameters
11002 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11003 * can be searched as well if needed.
11004 *
11005 * @param ll
11006 * @param aControllerName
11007 * @param aControllerPort
11008 * @param aDevice
11009 * @return
11010 */
11011MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11012 const Utf8Str &aControllerName,
11013 LONG aControllerPort,
11014 LONG aDevice)
11015{
11016 for (MediumAttachmentList::const_iterator
11017 it = ll.begin();
11018 it != ll.end();
11019 ++it)
11020 {
11021 MediumAttachment *pAttach = *it;
11022 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11023 return pAttach;
11024 }
11025
11026 return NULL;
11027}
11028
11029/**
11030 * Looks through the given list of media attachments for one with the given parameters
11031 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11032 * can be searched as well if needed.
11033 *
11034 * @param ll
11035 * @param pMedium
11036 * @return
11037 */
11038MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11039 ComObjPtr<Medium> pMedium)
11040{
11041 for (MediumAttachmentList::const_iterator
11042 it = ll.begin();
11043 it != ll.end();
11044 ++it)
11045 {
11046 MediumAttachment *pAttach = *it;
11047 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11048 if (pMediumThis == pMedium)
11049 return pAttach;
11050 }
11051
11052 return NULL;
11053}
11054
11055/**
11056 * Looks through the given list of media attachments for one with the given parameters
11057 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11058 * can be searched as well if needed.
11059 *
11060 * @param ll
11061 * @param id
11062 * @return
11063 */
11064MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11065 Guid &id)
11066{
11067 for (MediumAttachmentList::const_iterator
11068 it = ll.begin();
11069 it != ll.end();
11070 ++it)
11071 {
11072 MediumAttachment *pAttach = *it;
11073 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11074 if (pMediumThis->i_getId() == id)
11075 return pAttach;
11076 }
11077
11078 return NULL;
11079}
11080
11081/**
11082 * Main implementation for Machine::DetachDevice. This also gets called
11083 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11084 *
11085 * @param pAttach Medium attachment to detach.
11086 * @param writeLock Machine write lock which the caller must have locked once.
11087 * This may be released temporarily in here.
11088 * @param pSnapshot If NULL, then the detachment is for the current machine.
11089 * Otherwise this is for a SnapshotMachine, and this must be
11090 * its snapshot.
11091 * @return
11092 */
11093HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11094 AutoWriteLock &writeLock,
11095 Snapshot *pSnapshot)
11096{
11097 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11098 DeviceType_T mediumType = pAttach->i_getType();
11099
11100 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11101
11102 if (pAttach->i_isImplicit())
11103 {
11104 /* attempt to implicitly delete the implicitly created diff */
11105
11106 /// @todo move the implicit flag from MediumAttachment to Medium
11107 /// and forbid any hard disk operation when it is implicit. Or maybe
11108 /// a special media state for it to make it even more simple.
11109
11110 Assert(mMediumAttachments.isBackedUp());
11111
11112 /* will release the lock before the potentially lengthy operation, so
11113 * protect with the special state */
11114 MachineState_T oldState = mData->mMachineState;
11115 i_setMachineState(MachineState_SettingUp);
11116
11117 writeLock.release();
11118
11119 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11120 true /*aWait*/,
11121 false /*aNotify*/);
11122
11123 writeLock.acquire();
11124
11125 i_setMachineState(oldState);
11126
11127 if (FAILED(rc)) return rc;
11128 }
11129
11130 i_setModified(IsModified_Storage);
11131 mMediumAttachments.backup();
11132 mMediumAttachments->remove(pAttach);
11133
11134 if (!oldmedium.isNull())
11135 {
11136 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11137 if (pSnapshot)
11138 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11139 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11140 else if (mediumType != DeviceType_HardDisk)
11141 oldmedium->i_removeBackReference(mData->mUuid);
11142 }
11143
11144 return S_OK;
11145}
11146
11147/**
11148 * Goes thru all media of the given list and
11149 *
11150 * 1) calls i_detachDevice() on each of them for this machine and
11151 * 2) adds all Medium objects found in the process to the given list,
11152 * depending on cleanupMode.
11153 *
11154 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11155 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11156 * media to the list.
11157 *
11158 * This gets called from Machine::Unregister, both for the actual Machine and
11159 * the SnapshotMachine objects that might be found in the snapshots.
11160 *
11161 * Requires caller and locking. The machine lock must be passed in because it
11162 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11163 *
11164 * @param writeLock Machine lock from top-level caller; this gets passed to
11165 * i_detachDevice.
11166 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11167 * object if called for a SnapshotMachine.
11168 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11169 * added to llMedia; if Full, then all media get added;
11170 * otherwise no media get added.
11171 * @param llMedia Caller's list to receive Medium objects which got detached so
11172 * caller can close() them, depending on cleanupMode.
11173 * @return
11174 */
11175HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11176 Snapshot *pSnapshot,
11177 CleanupMode_T cleanupMode,
11178 MediaList &llMedia)
11179{
11180 Assert(isWriteLockOnCurrentThread());
11181
11182 HRESULT rc;
11183
11184 // make a temporary list because i_detachDevice invalidates iterators into
11185 // mMediumAttachments
11186 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11187
11188 for (MediumAttachmentList::iterator
11189 it = llAttachments2.begin();
11190 it != llAttachments2.end();
11191 ++it)
11192 {
11193 ComObjPtr<MediumAttachment> &pAttach = *it;
11194 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11195
11196 if (!pMedium.isNull())
11197 {
11198 AutoCaller mac(pMedium);
11199 if (FAILED(mac.rc())) return mac.rc();
11200 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11201 DeviceType_T devType = pMedium->i_getDeviceType();
11202 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11203 && devType == DeviceType_HardDisk)
11204 || (cleanupMode == CleanupMode_Full)
11205 )
11206 {
11207 llMedia.push_back(pMedium);
11208 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11209 /* Not allowed to keep this lock as below we need the parent
11210 * medium lock, and the lock order is parent to child. */
11211 lock.release();
11212 /*
11213 * Search for medias which are not attached to any machine, but
11214 * in the chain to an attached disk. Mediums are only consided
11215 * if they are:
11216 * - have only one child
11217 * - no references to any machines
11218 * - are of normal medium type
11219 */
11220 while (!pParent.isNull())
11221 {
11222 AutoCaller mac1(pParent);
11223 if (FAILED(mac1.rc())) return mac1.rc();
11224 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11225 if (pParent->i_getChildren().size() == 1)
11226 {
11227 if ( pParent->i_getMachineBackRefCount() == 0
11228 && pParent->i_getType() == MediumType_Normal
11229 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11230 llMedia.push_back(pParent);
11231 }
11232 else
11233 break;
11234 pParent = pParent->i_getParent();
11235 }
11236 }
11237 }
11238
11239 // real machine: then we need to use the proper method
11240 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11241
11242 if (FAILED(rc))
11243 return rc;
11244 }
11245
11246 return S_OK;
11247}
11248
11249/**
11250 * Perform deferred hard disk detachments.
11251 *
11252 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11253 * changed (not backed up).
11254 *
11255 * If @a aOnline is @c true then this method will also unlock the old hard
11256 * disks for which the new implicit diffs were created and will lock these new
11257 * diffs for writing.
11258 *
11259 * @param aOnline Whether the VM was online prior to this operation.
11260 *
11261 * @note Locks this object for writing!
11262 */
11263void Machine::i_commitMedia(bool aOnline /*= false*/)
11264{
11265 AutoCaller autoCaller(this);
11266 AssertComRCReturnVoid(autoCaller.rc());
11267
11268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11269
11270 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11271
11272 HRESULT rc = S_OK;
11273
11274 /* no attach/detach operations -- nothing to do */
11275 if (!mMediumAttachments.isBackedUp())
11276 return;
11277
11278 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11279 bool fMediaNeedsLocking = false;
11280
11281 /* enumerate new attachments */
11282 for (MediumAttachmentList::const_iterator
11283 it = mMediumAttachments->begin();
11284 it != mMediumAttachments->end();
11285 ++it)
11286 {
11287 MediumAttachment *pAttach = *it;
11288
11289 pAttach->i_commit();
11290
11291 Medium *pMedium = pAttach->i_getMedium();
11292 bool fImplicit = pAttach->i_isImplicit();
11293
11294 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11295 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11296 fImplicit));
11297
11298 /** @todo convert all this Machine-based voodoo to MediumAttachment
11299 * based commit logic. */
11300 if (fImplicit)
11301 {
11302 /* convert implicit attachment to normal */
11303 pAttach->i_setImplicit(false);
11304
11305 if ( aOnline
11306 && pMedium
11307 && pAttach->i_getType() == DeviceType_HardDisk
11308 )
11309 {
11310 /* update the appropriate lock list */
11311 MediumLockList *pMediumLockList;
11312 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11313 AssertComRC(rc);
11314 if (pMediumLockList)
11315 {
11316 /* unlock if there's a need to change the locking */
11317 if (!fMediaNeedsLocking)
11318 {
11319 rc = mData->mSession.mLockedMedia.Unlock();
11320 AssertComRC(rc);
11321 fMediaNeedsLocking = true;
11322 }
11323 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11324 AssertComRC(rc);
11325 rc = pMediumLockList->Append(pMedium, true);
11326 AssertComRC(rc);
11327 }
11328 }
11329
11330 continue;
11331 }
11332
11333 if (pMedium)
11334 {
11335 /* was this medium attached before? */
11336 for (MediumAttachmentList::iterator
11337 oldIt = oldAtts.begin();
11338 oldIt != oldAtts.end();
11339 ++oldIt)
11340 {
11341 MediumAttachment *pOldAttach = *oldIt;
11342 if (pOldAttach->i_getMedium() == pMedium)
11343 {
11344 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11345
11346 /* yes: remove from old to avoid de-association */
11347 oldAtts.erase(oldIt);
11348 break;
11349 }
11350 }
11351 }
11352 }
11353
11354 /* enumerate remaining old attachments and de-associate from the
11355 * current machine state */
11356 for (MediumAttachmentList::const_iterator
11357 it = oldAtts.begin();
11358 it != oldAtts.end();
11359 ++it)
11360 {
11361 MediumAttachment *pAttach = *it;
11362 Medium *pMedium = pAttach->i_getMedium();
11363
11364 /* Detach only hard disks, since DVD/floppy media is detached
11365 * instantly in MountMedium. */
11366 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11367 {
11368 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11369
11370 /* now de-associate from the current machine state */
11371 rc = pMedium->i_removeBackReference(mData->mUuid);
11372 AssertComRC(rc);
11373
11374 if (aOnline)
11375 {
11376 /* unlock since medium is not used anymore */
11377 MediumLockList *pMediumLockList;
11378 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11379 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11380 {
11381 /* this happens for online snapshots, there the attachment
11382 * is changing, but only to a diff image created under
11383 * the old one, so there is no separate lock list */
11384 Assert(!pMediumLockList);
11385 }
11386 else
11387 {
11388 AssertComRC(rc);
11389 if (pMediumLockList)
11390 {
11391 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11392 AssertComRC(rc);
11393 }
11394 }
11395 }
11396 }
11397 }
11398
11399 /* take media locks again so that the locking state is consistent */
11400 if (fMediaNeedsLocking)
11401 {
11402 Assert(aOnline);
11403 rc = mData->mSession.mLockedMedia.Lock();
11404 AssertComRC(rc);
11405 }
11406
11407 /* commit the hard disk changes */
11408 mMediumAttachments.commit();
11409
11410 if (i_isSessionMachine())
11411 {
11412 /*
11413 * Update the parent machine to point to the new owner.
11414 * This is necessary because the stored parent will point to the
11415 * session machine otherwise and cause crashes or errors later
11416 * when the session machine gets invalid.
11417 */
11418 /** @todo Change the MediumAttachment class to behave like any other
11419 * class in this regard by creating peer MediumAttachment
11420 * objects for session machines and share the data with the peer
11421 * machine.
11422 */
11423 for (MediumAttachmentList::const_iterator
11424 it = mMediumAttachments->begin();
11425 it != mMediumAttachments->end();
11426 ++it)
11427 (*it)->i_updateParentMachine(mPeer);
11428
11429 /* attach new data to the primary machine and reshare it */
11430 mPeer->mMediumAttachments.attach(mMediumAttachments);
11431 }
11432
11433 return;
11434}
11435
11436/**
11437 * Perform deferred deletion of implicitly created diffs.
11438 *
11439 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11440 * changed (not backed up).
11441 *
11442 * @note Locks this object for writing!
11443 */
11444void Machine::i_rollbackMedia()
11445{
11446 AutoCaller autoCaller(this);
11447 AssertComRCReturnVoid(autoCaller.rc());
11448
11449 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11450 LogFlowThisFunc(("Entering rollbackMedia\n"));
11451
11452 HRESULT rc = S_OK;
11453
11454 /* no attach/detach operations -- nothing to do */
11455 if (!mMediumAttachments.isBackedUp())
11456 return;
11457
11458 /* enumerate new attachments */
11459 for (MediumAttachmentList::const_iterator
11460 it = mMediumAttachments->begin();
11461 it != mMediumAttachments->end();
11462 ++it)
11463 {
11464 MediumAttachment *pAttach = *it;
11465 /* Fix up the backrefs for DVD/floppy media. */
11466 if (pAttach->i_getType() != DeviceType_HardDisk)
11467 {
11468 Medium *pMedium = pAttach->i_getMedium();
11469 if (pMedium)
11470 {
11471 rc = pMedium->i_removeBackReference(mData->mUuid);
11472 AssertComRC(rc);
11473 }
11474 }
11475
11476 (*it)->i_rollback();
11477
11478 pAttach = *it;
11479 /* Fix up the backrefs for DVD/floppy media. */
11480 if (pAttach->i_getType() != DeviceType_HardDisk)
11481 {
11482 Medium *pMedium = pAttach->i_getMedium();
11483 if (pMedium)
11484 {
11485 rc = pMedium->i_addBackReference(mData->mUuid);
11486 AssertComRC(rc);
11487 }
11488 }
11489 }
11490
11491 /** @todo convert all this Machine-based voodoo to MediumAttachment
11492 * based rollback logic. */
11493 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11494
11495 return;
11496}
11497
11498/**
11499 * Returns true if the settings file is located in the directory named exactly
11500 * as the machine; this means, among other things, that the machine directory
11501 * should be auto-renamed.
11502 *
11503 * @param aSettingsDir if not NULL, the full machine settings file directory
11504 * name will be assigned there.
11505 *
11506 * @note Doesn't lock anything.
11507 * @note Not thread safe (must be called from this object's lock).
11508 */
11509bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11510{
11511 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11512 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11513 if (aSettingsDir)
11514 *aSettingsDir = strMachineDirName;
11515 strMachineDirName.stripPath(); // vmname
11516 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11517 strConfigFileOnly.stripPath() // vmname.vbox
11518 .stripSuffix(); // vmname
11519 /** @todo hack, make somehow use of ComposeMachineFilename */
11520 if (mUserData->s.fDirectoryIncludesUUID)
11521 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11522
11523 AssertReturn(!strMachineDirName.isEmpty(), false);
11524 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11525
11526 return strMachineDirName == strConfigFileOnly;
11527}
11528
11529/**
11530 * Discards all changes to machine settings.
11531 *
11532 * @param aNotify Whether to notify the direct session about changes or not.
11533 *
11534 * @note Locks objects for writing!
11535 */
11536void Machine::i_rollback(bool aNotify)
11537{
11538 AutoCaller autoCaller(this);
11539 AssertComRCReturn(autoCaller.rc(), (void)0);
11540
11541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11542
11543 if (!mStorageControllers.isNull())
11544 {
11545 if (mStorageControllers.isBackedUp())
11546 {
11547 /* unitialize all new devices (absent in the backed up list). */
11548 StorageControllerList *backedList = mStorageControllers.backedUpData();
11549 for (StorageControllerList::const_iterator
11550 it = mStorageControllers->begin();
11551 it != mStorageControllers->end();
11552 ++it)
11553 {
11554 if ( std::find(backedList->begin(), backedList->end(), *it)
11555 == backedList->end()
11556 )
11557 {
11558 (*it)->uninit();
11559 }
11560 }
11561
11562 /* restore the list */
11563 mStorageControllers.rollback();
11564 }
11565
11566 /* rollback any changes to devices after restoring the list */
11567 if (mData->flModifications & IsModified_Storage)
11568 {
11569 for (StorageControllerList::const_iterator
11570 it = mStorageControllers->begin();
11571 it != mStorageControllers->end();
11572 ++it)
11573 {
11574 (*it)->i_rollback();
11575 }
11576 }
11577 }
11578
11579 if (!mUSBControllers.isNull())
11580 {
11581 if (mUSBControllers.isBackedUp())
11582 {
11583 /* unitialize all new devices (absent in the backed up list). */
11584 USBControllerList *backedList = mUSBControllers.backedUpData();
11585 for (USBControllerList::const_iterator
11586 it = mUSBControllers->begin();
11587 it != mUSBControllers->end();
11588 ++it)
11589 {
11590 if ( std::find(backedList->begin(), backedList->end(), *it)
11591 == backedList->end()
11592 )
11593 {
11594 (*it)->uninit();
11595 }
11596 }
11597
11598 /* restore the list */
11599 mUSBControllers.rollback();
11600 }
11601
11602 /* rollback any changes to devices after restoring the list */
11603 if (mData->flModifications & IsModified_USB)
11604 {
11605 for (USBControllerList::const_iterator
11606 it = mUSBControllers->begin();
11607 it != mUSBControllers->end();
11608 ++it)
11609 {
11610 (*it)->i_rollback();
11611 }
11612 }
11613 }
11614
11615 mUserData.rollback();
11616
11617 mHWData.rollback();
11618
11619 if (mData->flModifications & IsModified_Storage)
11620 i_rollbackMedia();
11621
11622 if (mBIOSSettings)
11623 mBIOSSettings->i_rollback();
11624
11625 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11626 mRecordingSettings->i_rollback();
11627
11628 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11629 mGraphicsAdapter->i_rollback();
11630
11631 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11632 mVRDEServer->i_rollback();
11633
11634 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11635 mAudioAdapter->i_rollback();
11636
11637 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11638 mUSBDeviceFilters->i_rollback();
11639
11640 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11641 mBandwidthControl->i_rollback();
11642
11643 if (!mHWData.isNull())
11644 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11645 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11646 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11647 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11648
11649 if (mData->flModifications & IsModified_NetworkAdapters)
11650 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11651 if ( mNetworkAdapters[slot]
11652 && mNetworkAdapters[slot]->i_isModified())
11653 {
11654 mNetworkAdapters[slot]->i_rollback();
11655 networkAdapters[slot] = mNetworkAdapters[slot];
11656 }
11657
11658 if (mData->flModifications & IsModified_SerialPorts)
11659 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11660 if ( mSerialPorts[slot]
11661 && mSerialPorts[slot]->i_isModified())
11662 {
11663 mSerialPorts[slot]->i_rollback();
11664 serialPorts[slot] = mSerialPorts[slot];
11665 }
11666
11667 if (mData->flModifications & IsModified_ParallelPorts)
11668 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11669 if ( mParallelPorts[slot]
11670 && mParallelPorts[slot]->i_isModified())
11671 {
11672 mParallelPorts[slot]->i_rollback();
11673 parallelPorts[slot] = mParallelPorts[slot];
11674 }
11675
11676 if (aNotify)
11677 {
11678 /* inform the direct session about changes */
11679
11680 ComObjPtr<Machine> that = this;
11681 uint32_t flModifications = mData->flModifications;
11682 alock.release();
11683
11684 if (flModifications & IsModified_SharedFolders)
11685 that->i_onSharedFolderChange();
11686
11687 if (flModifications & IsModified_VRDEServer)
11688 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11689 if (flModifications & IsModified_USB)
11690 that->i_onUSBControllerChange();
11691
11692 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11693 if (networkAdapters[slot])
11694 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11695 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11696 if (serialPorts[slot])
11697 that->i_onSerialPortChange(serialPorts[slot]);
11698 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11699 if (parallelPorts[slot])
11700 that->i_onParallelPortChange(parallelPorts[slot]);
11701
11702 if (flModifications & IsModified_Storage)
11703 {
11704 for (StorageControllerList::const_iterator
11705 it = mStorageControllers->begin();
11706 it != mStorageControllers->end();
11707 ++it)
11708 {
11709 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11710 }
11711 }
11712
11713
11714#if 0
11715 if (flModifications & IsModified_BandwidthControl)
11716 that->onBandwidthControlChange();
11717#endif
11718 }
11719}
11720
11721/**
11722 * Commits all the changes to machine settings.
11723 *
11724 * Note that this operation is supposed to never fail.
11725 *
11726 * @note Locks this object and children for writing.
11727 */
11728void Machine::i_commit()
11729{
11730 AutoCaller autoCaller(this);
11731 AssertComRCReturnVoid(autoCaller.rc());
11732
11733 AutoCaller peerCaller(mPeer);
11734 AssertComRCReturnVoid(peerCaller.rc());
11735
11736 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11737
11738 /*
11739 * use safe commit to ensure Snapshot machines (that share mUserData)
11740 * will still refer to a valid memory location
11741 */
11742 mUserData.commitCopy();
11743
11744 mHWData.commit();
11745
11746 if (mMediumAttachments.isBackedUp())
11747 i_commitMedia(Global::IsOnline(mData->mMachineState));
11748
11749 mBIOSSettings->i_commit();
11750 mRecordingSettings->i_commit();
11751 mGraphicsAdapter->i_commit();
11752 mVRDEServer->i_commit();
11753 mAudioAdapter->i_commit();
11754 mUSBDeviceFilters->i_commit();
11755 mBandwidthControl->i_commit();
11756
11757 /* Since mNetworkAdapters is a list which might have been changed (resized)
11758 * without using the Backupable<> template we need to handle the copying
11759 * of the list entries manually, including the creation of peers for the
11760 * new objects. */
11761 bool commitNetworkAdapters = false;
11762 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11763 if (mPeer)
11764 {
11765 /* commit everything, even the ones which will go away */
11766 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11767 mNetworkAdapters[slot]->i_commit();
11768 /* copy over the new entries, creating a peer and uninit the original */
11769 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11770 for (size_t slot = 0; slot < newSize; slot++)
11771 {
11772 /* look if this adapter has a peer device */
11773 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11774 if (!peer)
11775 {
11776 /* no peer means the adapter is a newly created one;
11777 * create a peer owning data this data share it with */
11778 peer.createObject();
11779 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11780 }
11781 mPeer->mNetworkAdapters[slot] = peer;
11782 }
11783 /* uninit any no longer needed network adapters */
11784 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11785 mNetworkAdapters[slot]->uninit();
11786 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11787 {
11788 if (mPeer->mNetworkAdapters[slot])
11789 mPeer->mNetworkAdapters[slot]->uninit();
11790 }
11791 /* Keep the original network adapter count until this point, so that
11792 * discarding a chipset type change will not lose settings. */
11793 mNetworkAdapters.resize(newSize);
11794 mPeer->mNetworkAdapters.resize(newSize);
11795 }
11796 else
11797 {
11798 /* we have no peer (our parent is the newly created machine);
11799 * just commit changes to the network adapters */
11800 commitNetworkAdapters = true;
11801 }
11802 if (commitNetworkAdapters)
11803 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11804 mNetworkAdapters[slot]->i_commit();
11805
11806 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11807 mSerialPorts[slot]->i_commit();
11808 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11809 mParallelPorts[slot]->i_commit();
11810
11811 bool commitStorageControllers = false;
11812
11813 if (mStorageControllers.isBackedUp())
11814 {
11815 mStorageControllers.commit();
11816
11817 if (mPeer)
11818 {
11819 /* Commit all changes to new controllers (this will reshare data with
11820 * peers for those who have peers) */
11821 StorageControllerList *newList = new StorageControllerList();
11822 for (StorageControllerList::const_iterator
11823 it = mStorageControllers->begin();
11824 it != mStorageControllers->end();
11825 ++it)
11826 {
11827 (*it)->i_commit();
11828
11829 /* look if this controller has a peer device */
11830 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11831 if (!peer)
11832 {
11833 /* no peer means the device is a newly created one;
11834 * create a peer owning data this device share it with */
11835 peer.createObject();
11836 peer->init(mPeer, *it, true /* aReshare */);
11837 }
11838 else
11839 {
11840 /* remove peer from the old list */
11841 mPeer->mStorageControllers->remove(peer);
11842 }
11843 /* and add it to the new list */
11844 newList->push_back(peer);
11845 }
11846
11847 /* uninit old peer's controllers that are left */
11848 for (StorageControllerList::const_iterator
11849 it = mPeer->mStorageControllers->begin();
11850 it != mPeer->mStorageControllers->end();
11851 ++it)
11852 {
11853 (*it)->uninit();
11854 }
11855
11856 /* attach new list of controllers to our peer */
11857 mPeer->mStorageControllers.attach(newList);
11858 }
11859 else
11860 {
11861 /* we have no peer (our parent is the newly created machine);
11862 * just commit changes to devices */
11863 commitStorageControllers = true;
11864 }
11865 }
11866 else
11867 {
11868 /* the list of controllers itself is not changed,
11869 * just commit changes to controllers themselves */
11870 commitStorageControllers = true;
11871 }
11872
11873 if (commitStorageControllers)
11874 {
11875 for (StorageControllerList::const_iterator
11876 it = mStorageControllers->begin();
11877 it != mStorageControllers->end();
11878 ++it)
11879 {
11880 (*it)->i_commit();
11881 }
11882 }
11883
11884 bool commitUSBControllers = false;
11885
11886 if (mUSBControllers.isBackedUp())
11887 {
11888 mUSBControllers.commit();
11889
11890 if (mPeer)
11891 {
11892 /* Commit all changes to new controllers (this will reshare data with
11893 * peers for those who have peers) */
11894 USBControllerList *newList = new USBControllerList();
11895 for (USBControllerList::const_iterator
11896 it = mUSBControllers->begin();
11897 it != mUSBControllers->end();
11898 ++it)
11899 {
11900 (*it)->i_commit();
11901
11902 /* look if this controller has a peer device */
11903 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11904 if (!peer)
11905 {
11906 /* no peer means the device is a newly created one;
11907 * create a peer owning data this device share it with */
11908 peer.createObject();
11909 peer->init(mPeer, *it, true /* aReshare */);
11910 }
11911 else
11912 {
11913 /* remove peer from the old list */
11914 mPeer->mUSBControllers->remove(peer);
11915 }
11916 /* and add it to the new list */
11917 newList->push_back(peer);
11918 }
11919
11920 /* uninit old peer's controllers that are left */
11921 for (USBControllerList::const_iterator
11922 it = mPeer->mUSBControllers->begin();
11923 it != mPeer->mUSBControllers->end();
11924 ++it)
11925 {
11926 (*it)->uninit();
11927 }
11928
11929 /* attach new list of controllers to our peer */
11930 mPeer->mUSBControllers.attach(newList);
11931 }
11932 else
11933 {
11934 /* we have no peer (our parent is the newly created machine);
11935 * just commit changes to devices */
11936 commitUSBControllers = true;
11937 }
11938 }
11939 else
11940 {
11941 /* the list of controllers itself is not changed,
11942 * just commit changes to controllers themselves */
11943 commitUSBControllers = true;
11944 }
11945
11946 if (commitUSBControllers)
11947 {
11948 for (USBControllerList::const_iterator
11949 it = mUSBControllers->begin();
11950 it != mUSBControllers->end();
11951 ++it)
11952 {
11953 (*it)->i_commit();
11954 }
11955 }
11956
11957 if (i_isSessionMachine())
11958 {
11959 /* attach new data to the primary machine and reshare it */
11960 mPeer->mUserData.attach(mUserData);
11961 mPeer->mHWData.attach(mHWData);
11962 /* mmMediumAttachments is reshared by fixupMedia */
11963 // mPeer->mMediumAttachments.attach(mMediumAttachments);
11964 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
11965 }
11966}
11967
11968/**
11969 * Copies all the hardware data from the given machine.
11970 *
11971 * Currently, only called when the VM is being restored from a snapshot. In
11972 * particular, this implies that the VM is not running during this method's
11973 * call.
11974 *
11975 * @note This method must be called from under this object's lock.
11976 *
11977 * @note This method doesn't call #i_commit(), so all data remains backed up and
11978 * unsaved.
11979 */
11980void Machine::i_copyFrom(Machine *aThat)
11981{
11982 AssertReturnVoid(!i_isSnapshotMachine());
11983 AssertReturnVoid(aThat->i_isSnapshotMachine());
11984
11985 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11986
11987 mHWData.assignCopy(aThat->mHWData);
11988
11989 // create copies of all shared folders (mHWData after attaching a copy
11990 // contains just references to original objects)
11991 for (HWData::SharedFolderList::iterator
11992 it = mHWData->mSharedFolders.begin();
11993 it != mHWData->mSharedFolders.end();
11994 ++it)
11995 {
11996 ComObjPtr<SharedFolder> folder;
11997 folder.createObject();
11998 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11999 AssertComRC(rc);
12000 *it = folder;
12001 }
12002
12003 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12004 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12005 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12006 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12007 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12008 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12009 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12010
12011 /* create private copies of all controllers */
12012 mStorageControllers.backup();
12013 mStorageControllers->clear();
12014 for (StorageControllerList::const_iterator
12015 it = aThat->mStorageControllers->begin();
12016 it != aThat->mStorageControllers->end();
12017 ++it)
12018 {
12019 ComObjPtr<StorageController> ctrl;
12020 ctrl.createObject();
12021 ctrl->initCopy(this, *it);
12022 mStorageControllers->push_back(ctrl);
12023 }
12024
12025 /* create private copies of all USB controllers */
12026 mUSBControllers.backup();
12027 mUSBControllers->clear();
12028 for (USBControllerList::const_iterator
12029 it = aThat->mUSBControllers->begin();
12030 it != aThat->mUSBControllers->end();
12031 ++it)
12032 {
12033 ComObjPtr<USBController> ctrl;
12034 ctrl.createObject();
12035 ctrl->initCopy(this, *it);
12036 mUSBControllers->push_back(ctrl);
12037 }
12038
12039 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12040 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12041 {
12042 if (mNetworkAdapters[slot].isNotNull())
12043 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12044 else
12045 {
12046 unconst(mNetworkAdapters[slot]).createObject();
12047 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12048 }
12049 }
12050 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12051 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12052 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12053 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12054}
12055
12056/**
12057 * Returns whether the given storage controller is hotplug capable.
12058 *
12059 * @returns true if the controller supports hotplugging
12060 * false otherwise.
12061 * @param enmCtrlType The controller type to check for.
12062 */
12063bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12064{
12065 ComPtr<ISystemProperties> systemProperties;
12066 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12067 if (FAILED(rc))
12068 return false;
12069
12070 BOOL aHotplugCapable = FALSE;
12071 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12072
12073 return RT_BOOL(aHotplugCapable);
12074}
12075
12076#ifdef VBOX_WITH_RESOURCE_USAGE_API
12077
12078void Machine::i_getDiskList(MediaList &list)
12079{
12080 for (MediumAttachmentList::const_iterator
12081 it = mMediumAttachments->begin();
12082 it != mMediumAttachments->end();
12083 ++it)
12084 {
12085 MediumAttachment *pAttach = *it;
12086 /* just in case */
12087 AssertContinue(pAttach);
12088
12089 AutoCaller localAutoCallerA(pAttach);
12090 if (FAILED(localAutoCallerA.rc())) continue;
12091
12092 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12093
12094 if (pAttach->i_getType() == DeviceType_HardDisk)
12095 list.push_back(pAttach->i_getMedium());
12096 }
12097}
12098
12099void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12100{
12101 AssertReturnVoid(isWriteLockOnCurrentThread());
12102 AssertPtrReturnVoid(aCollector);
12103
12104 pm::CollectorHAL *hal = aCollector->getHAL();
12105 /* Create sub metrics */
12106 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12107 "Percentage of processor time spent in user mode by the VM process.");
12108 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12109 "Percentage of processor time spent in kernel mode by the VM process.");
12110 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12111 "Size of resident portion of VM process in memory.");
12112 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12113 "Actual size of all VM disks combined.");
12114 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12115 "Network receive rate.");
12116 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12117 "Network transmit rate.");
12118 /* Create and register base metrics */
12119 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12120 cpuLoadUser, cpuLoadKernel);
12121 aCollector->registerBaseMetric(cpuLoad);
12122 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12123 ramUsageUsed);
12124 aCollector->registerBaseMetric(ramUsage);
12125 MediaList disks;
12126 i_getDiskList(disks);
12127 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12128 diskUsageUsed);
12129 aCollector->registerBaseMetric(diskUsage);
12130
12131 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12132 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12133 new pm::AggregateAvg()));
12134 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12135 new pm::AggregateMin()));
12136 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12137 new pm::AggregateMax()));
12138 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12139 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12140 new pm::AggregateAvg()));
12141 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12142 new pm::AggregateMin()));
12143 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12144 new pm::AggregateMax()));
12145
12146 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12147 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12148 new pm::AggregateAvg()));
12149 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12150 new pm::AggregateMin()));
12151 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12152 new pm::AggregateMax()));
12153
12154 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12155 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12156 new pm::AggregateAvg()));
12157 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12158 new pm::AggregateMin()));
12159 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12160 new pm::AggregateMax()));
12161
12162
12163 /* Guest metrics collector */
12164 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12165 aCollector->registerGuest(mCollectorGuest);
12166 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12167
12168 /* Create sub metrics */
12169 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12170 "Percentage of processor time spent in user mode as seen by the guest.");
12171 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12172 "Percentage of processor time spent in kernel mode as seen by the guest.");
12173 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12174 "Percentage of processor time spent idling as seen by the guest.");
12175
12176 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12177 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12178 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12179 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12180 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12181 pm::SubMetric *guestMemCache = new pm::SubMetric(
12182 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12183
12184 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12185 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12186
12187 /* Create and register base metrics */
12188 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12189 machineNetRx, machineNetTx);
12190 aCollector->registerBaseMetric(machineNetRate);
12191
12192 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12193 guestLoadUser, guestLoadKernel, guestLoadIdle);
12194 aCollector->registerBaseMetric(guestCpuLoad);
12195
12196 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12197 guestMemTotal, guestMemFree,
12198 guestMemBalloon, guestMemShared,
12199 guestMemCache, guestPagedTotal);
12200 aCollector->registerBaseMetric(guestCpuMem);
12201
12202 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12203 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12204 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12205 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12206
12207 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12208 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12209 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12210 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12211
12212 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12213 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12214 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12215 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12216
12217 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12218 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12219 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12220 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12221
12222 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12223 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12225 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12226
12227 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12228 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12230 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12231
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12233 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12236
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12241
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12246
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12251
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12256}
12257
12258void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12259{
12260 AssertReturnVoid(isWriteLockOnCurrentThread());
12261
12262 if (aCollector)
12263 {
12264 aCollector->unregisterMetricsFor(aMachine);
12265 aCollector->unregisterBaseMetricsFor(aMachine);
12266 }
12267}
12268
12269#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12270
12271
12272////////////////////////////////////////////////////////////////////////////////
12273
12274DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12275
12276HRESULT SessionMachine::FinalConstruct()
12277{
12278 LogFlowThisFunc(("\n"));
12279
12280 mClientToken = NULL;
12281
12282 return BaseFinalConstruct();
12283}
12284
12285void SessionMachine::FinalRelease()
12286{
12287 LogFlowThisFunc(("\n"));
12288
12289 Assert(!mClientToken);
12290 /* paranoia, should not hang around any more */
12291 if (mClientToken)
12292 {
12293 delete mClientToken;
12294 mClientToken = NULL;
12295 }
12296
12297 uninit(Uninit::Unexpected);
12298
12299 BaseFinalRelease();
12300}
12301
12302/**
12303 * @note Must be called only by Machine::LockMachine() from its own write lock.
12304 */
12305HRESULT SessionMachine::init(Machine *aMachine)
12306{
12307 LogFlowThisFuncEnter();
12308 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12309
12310 AssertReturn(aMachine, E_INVALIDARG);
12311
12312 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12313
12314 /* Enclose the state transition NotReady->InInit->Ready */
12315 AutoInitSpan autoInitSpan(this);
12316 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12317
12318 HRESULT rc = S_OK;
12319
12320 RT_ZERO(mAuthLibCtx);
12321
12322 /* create the machine client token */
12323 try
12324 {
12325 mClientToken = new ClientToken(aMachine, this);
12326 if (!mClientToken->isReady())
12327 {
12328 delete mClientToken;
12329 mClientToken = NULL;
12330 rc = E_FAIL;
12331 }
12332 }
12333 catch (std::bad_alloc &)
12334 {
12335 rc = E_OUTOFMEMORY;
12336 }
12337 if (FAILED(rc))
12338 return rc;
12339
12340 /* memorize the peer Machine */
12341 unconst(mPeer) = aMachine;
12342 /* share the parent pointer */
12343 unconst(mParent) = aMachine->mParent;
12344
12345 /* take the pointers to data to share */
12346 mData.share(aMachine->mData);
12347 mSSData.share(aMachine->mSSData);
12348
12349 mUserData.share(aMachine->mUserData);
12350 mHWData.share(aMachine->mHWData);
12351 mMediumAttachments.share(aMachine->mMediumAttachments);
12352
12353 mStorageControllers.allocate();
12354 for (StorageControllerList::const_iterator
12355 it = aMachine->mStorageControllers->begin();
12356 it != aMachine->mStorageControllers->end();
12357 ++it)
12358 {
12359 ComObjPtr<StorageController> ctl;
12360 ctl.createObject();
12361 ctl->init(this, *it);
12362 mStorageControllers->push_back(ctl);
12363 }
12364
12365 mUSBControllers.allocate();
12366 for (USBControllerList::const_iterator
12367 it = aMachine->mUSBControllers->begin();
12368 it != aMachine->mUSBControllers->end();
12369 ++it)
12370 {
12371 ComObjPtr<USBController> ctl;
12372 ctl.createObject();
12373 ctl->init(this, *it);
12374 mUSBControllers->push_back(ctl);
12375 }
12376
12377 unconst(mBIOSSettings).createObject();
12378 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12379 unconst(mRecordingSettings).createObject();
12380 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12381 /* create another GraphicsAdapter object that will be mutable */
12382 unconst(mGraphicsAdapter).createObject();
12383 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12384 /* create another VRDEServer object that will be mutable */
12385 unconst(mVRDEServer).createObject();
12386 mVRDEServer->init(this, aMachine->mVRDEServer);
12387 /* create another audio adapter object that will be mutable */
12388 unconst(mAudioAdapter).createObject();
12389 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12390 /* create a list of serial ports that will be mutable */
12391 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12392 {
12393 unconst(mSerialPorts[slot]).createObject();
12394 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12395 }
12396 /* create a list of parallel ports that will be mutable */
12397 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12398 {
12399 unconst(mParallelPorts[slot]).createObject();
12400 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12401 }
12402
12403 /* create another USB device filters object that will be mutable */
12404 unconst(mUSBDeviceFilters).createObject();
12405 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12406
12407 /* create a list of network adapters that will be mutable */
12408 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12409 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12410 {
12411 unconst(mNetworkAdapters[slot]).createObject();
12412 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12413 }
12414
12415 /* create another bandwidth control object that will be mutable */
12416 unconst(mBandwidthControl).createObject();
12417 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12418
12419 /* default is to delete saved state on Saved -> PoweredOff transition */
12420 mRemoveSavedState = true;
12421
12422 /* Confirm a successful initialization when it's the case */
12423 autoInitSpan.setSucceeded();
12424
12425 miNATNetworksStarted = 0;
12426
12427 LogFlowThisFuncLeave();
12428 return rc;
12429}
12430
12431/**
12432 * Uninitializes this session object. If the reason is other than
12433 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12434 * or the client watcher code.
12435 *
12436 * @param aReason uninitialization reason
12437 *
12438 * @note Locks mParent + this object for writing.
12439 */
12440void SessionMachine::uninit(Uninit::Reason aReason)
12441{
12442 LogFlowThisFuncEnter();
12443 LogFlowThisFunc(("reason=%d\n", aReason));
12444
12445 /*
12446 * Strongly reference ourselves to prevent this object deletion after
12447 * mData->mSession.mMachine.setNull() below (which can release the last
12448 * reference and call the destructor). Important: this must be done before
12449 * accessing any members (and before AutoUninitSpan that does it as well).
12450 * This self reference will be released as the very last step on return.
12451 */
12452 ComObjPtr<SessionMachine> selfRef;
12453 if (aReason != Uninit::Unexpected)
12454 selfRef = this;
12455
12456 /* Enclose the state transition Ready->InUninit->NotReady */
12457 AutoUninitSpan autoUninitSpan(this);
12458 if (autoUninitSpan.uninitDone())
12459 {
12460 LogFlowThisFunc(("Already uninitialized\n"));
12461 LogFlowThisFuncLeave();
12462 return;
12463 }
12464
12465 if (autoUninitSpan.initFailed())
12466 {
12467 /* We've been called by init() because it's failed. It's not really
12468 * necessary (nor it's safe) to perform the regular uninit sequence
12469 * below, the following is enough.
12470 */
12471 LogFlowThisFunc(("Initialization failed.\n"));
12472 /* destroy the machine client token */
12473 if (mClientToken)
12474 {
12475 delete mClientToken;
12476 mClientToken = NULL;
12477 }
12478 uninitDataAndChildObjects();
12479 mData.free();
12480 unconst(mParent) = NULL;
12481 unconst(mPeer) = NULL;
12482 LogFlowThisFuncLeave();
12483 return;
12484 }
12485
12486 MachineState_T lastState;
12487 {
12488 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12489 lastState = mData->mMachineState;
12490 }
12491 NOREF(lastState);
12492
12493#ifdef VBOX_WITH_USB
12494 // release all captured USB devices, but do this before requesting the locks below
12495 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12496 {
12497 /* Console::captureUSBDevices() is called in the VM process only after
12498 * setting the machine state to Starting or Restoring.
12499 * Console::detachAllUSBDevices() will be called upon successful
12500 * termination. So, we need to release USB devices only if there was
12501 * an abnormal termination of a running VM.
12502 *
12503 * This is identical to SessionMachine::DetachAllUSBDevices except
12504 * for the aAbnormal argument. */
12505 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12506 AssertComRC(rc);
12507 NOREF(rc);
12508
12509 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12510 if (service)
12511 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12512 }
12513#endif /* VBOX_WITH_USB */
12514
12515 // we need to lock this object in uninit() because the lock is shared
12516 // with mPeer (as well as data we modify below). mParent lock is needed
12517 // by several calls to it.
12518 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12519
12520#ifdef VBOX_WITH_RESOURCE_USAGE_API
12521 /*
12522 * It is safe to call Machine::i_unregisterMetrics() here because
12523 * PerformanceCollector::samplerCallback no longer accesses guest methods
12524 * holding the lock.
12525 */
12526 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12527 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12528 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12529 if (mCollectorGuest)
12530 {
12531 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12532 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12533 mCollectorGuest = NULL;
12534 }
12535#endif
12536
12537 if (aReason == Uninit::Abnormal)
12538 {
12539 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12540
12541 /* reset the state to Aborted */
12542 if (mData->mMachineState != MachineState_Aborted)
12543 i_setMachineState(MachineState_Aborted);
12544 }
12545
12546 // any machine settings modified?
12547 if (mData->flModifications)
12548 {
12549 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12550 i_rollback(false /* aNotify */);
12551 }
12552
12553 mData->mSession.mPID = NIL_RTPROCESS;
12554
12555 if (aReason == Uninit::Unexpected)
12556 {
12557 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12558 * client watcher thread to update the set of machines that have open
12559 * sessions. */
12560 mParent->i_updateClientWatcher();
12561 }
12562
12563 /* uninitialize all remote controls */
12564 if (mData->mSession.mRemoteControls.size())
12565 {
12566 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12567 mData->mSession.mRemoteControls.size()));
12568
12569 /* Always restart a the beginning, since the iterator is invalidated
12570 * by using erase(). */
12571 for (Data::Session::RemoteControlList::iterator
12572 it = mData->mSession.mRemoteControls.begin();
12573 it != mData->mSession.mRemoteControls.end();
12574 it = mData->mSession.mRemoteControls.begin())
12575 {
12576 ComPtr<IInternalSessionControl> pControl = *it;
12577 mData->mSession.mRemoteControls.erase(it);
12578 multilock.release();
12579 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12580 HRESULT rc = pControl->Uninitialize();
12581 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12582 if (FAILED(rc))
12583 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12584 multilock.acquire();
12585 }
12586 mData->mSession.mRemoteControls.clear();
12587 }
12588
12589 /* Remove all references to the NAT network service. The service will stop
12590 * if all references (also from other VMs) are removed. */
12591 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12592 {
12593 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12594 {
12595 BOOL enabled;
12596 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12597 if ( FAILED(hrc)
12598 || !enabled)
12599 continue;
12600
12601 NetworkAttachmentType_T type;
12602 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12603 if ( SUCCEEDED(hrc)
12604 && type == NetworkAttachmentType_NATNetwork)
12605 {
12606 Bstr name;
12607 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12608 if (SUCCEEDED(hrc))
12609 {
12610 multilock.release();
12611 Utf8Str strName(name);
12612 LogRel(("VM '%s' stops using NAT network '%s'\n",
12613 mUserData->s.strName.c_str(), strName.c_str()));
12614 mParent->i_natNetworkRefDec(strName);
12615 multilock.acquire();
12616 }
12617 }
12618 }
12619 }
12620
12621 /*
12622 * An expected uninitialization can come only from #i_checkForDeath().
12623 * Otherwise it means that something's gone really wrong (for example,
12624 * the Session implementation has released the VirtualBox reference
12625 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12626 * etc). However, it's also possible, that the client releases the IPC
12627 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12628 * but the VirtualBox release event comes first to the server process.
12629 * This case is practically possible, so we should not assert on an
12630 * unexpected uninit, just log a warning.
12631 */
12632
12633 if (aReason == Uninit::Unexpected)
12634 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12635
12636 if (aReason != Uninit::Normal)
12637 {
12638 mData->mSession.mDirectControl.setNull();
12639 }
12640 else
12641 {
12642 /* this must be null here (see #OnSessionEnd()) */
12643 Assert(mData->mSession.mDirectControl.isNull());
12644 Assert(mData->mSession.mState == SessionState_Unlocking);
12645 Assert(!mData->mSession.mProgress.isNull());
12646 }
12647 if (mData->mSession.mProgress)
12648 {
12649 if (aReason == Uninit::Normal)
12650 mData->mSession.mProgress->i_notifyComplete(S_OK);
12651 else
12652 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12653 COM_IIDOF(ISession),
12654 getComponentName(),
12655 tr("The VM session was aborted"));
12656 mData->mSession.mProgress.setNull();
12657 }
12658
12659 if (mConsoleTaskData.mProgress)
12660 {
12661 Assert(aReason == Uninit::Abnormal);
12662 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12663 COM_IIDOF(ISession),
12664 getComponentName(),
12665 tr("The VM session was aborted"));
12666 mConsoleTaskData.mProgress.setNull();
12667 }
12668
12669 /* remove the association between the peer machine and this session machine */
12670 Assert( (SessionMachine*)mData->mSession.mMachine == this
12671 || aReason == Uninit::Unexpected);
12672
12673 /* reset the rest of session data */
12674 mData->mSession.mLockType = LockType_Null;
12675 mData->mSession.mMachine.setNull();
12676 mData->mSession.mState = SessionState_Unlocked;
12677 mData->mSession.mName.setNull();
12678
12679 /* destroy the machine client token before leaving the exclusive lock */
12680 if (mClientToken)
12681 {
12682 delete mClientToken;
12683 mClientToken = NULL;
12684 }
12685
12686 /* fire an event */
12687 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12688
12689 uninitDataAndChildObjects();
12690
12691 /* free the essential data structure last */
12692 mData.free();
12693
12694 /* release the exclusive lock before setting the below two to NULL */
12695 multilock.release();
12696
12697 unconst(mParent) = NULL;
12698 unconst(mPeer) = NULL;
12699
12700 AuthLibUnload(&mAuthLibCtx);
12701
12702 LogFlowThisFuncLeave();
12703}
12704
12705// util::Lockable interface
12706////////////////////////////////////////////////////////////////////////////////
12707
12708/**
12709 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12710 * with the primary Machine instance (mPeer).
12711 */
12712RWLockHandle *SessionMachine::lockHandle() const
12713{
12714 AssertReturn(mPeer != NULL, NULL);
12715 return mPeer->lockHandle();
12716}
12717
12718// IInternalMachineControl methods
12719////////////////////////////////////////////////////////////////////////////////
12720
12721/**
12722 * Passes collected guest statistics to performance collector object
12723 */
12724HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12725 ULONG aCpuKernel, ULONG aCpuIdle,
12726 ULONG aMemTotal, ULONG aMemFree,
12727 ULONG aMemBalloon, ULONG aMemShared,
12728 ULONG aMemCache, ULONG aPageTotal,
12729 ULONG aAllocVMM, ULONG aFreeVMM,
12730 ULONG aBalloonedVMM, ULONG aSharedVMM,
12731 ULONG aVmNetRx, ULONG aVmNetTx)
12732{
12733#ifdef VBOX_WITH_RESOURCE_USAGE_API
12734 if (mCollectorGuest)
12735 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12736 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12737 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12738 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12739
12740 return S_OK;
12741#else
12742 NOREF(aValidStats);
12743 NOREF(aCpuUser);
12744 NOREF(aCpuKernel);
12745 NOREF(aCpuIdle);
12746 NOREF(aMemTotal);
12747 NOREF(aMemFree);
12748 NOREF(aMemBalloon);
12749 NOREF(aMemShared);
12750 NOREF(aMemCache);
12751 NOREF(aPageTotal);
12752 NOREF(aAllocVMM);
12753 NOREF(aFreeVMM);
12754 NOREF(aBalloonedVMM);
12755 NOREF(aSharedVMM);
12756 NOREF(aVmNetRx);
12757 NOREF(aVmNetTx);
12758 return E_NOTIMPL;
12759#endif
12760}
12761
12762////////////////////////////////////////////////////////////////////////////////
12763//
12764// SessionMachine task records
12765//
12766////////////////////////////////////////////////////////////////////////////////
12767
12768/**
12769 * Task record for saving the machine state.
12770 */
12771class SessionMachine::SaveStateTask
12772 : public Machine::Task
12773{
12774public:
12775 SaveStateTask(SessionMachine *m,
12776 Progress *p,
12777 const Utf8Str &t,
12778 Reason_T enmReason,
12779 const Utf8Str &strStateFilePath)
12780 : Task(m, p, t),
12781 m_enmReason(enmReason),
12782 m_strStateFilePath(strStateFilePath)
12783 {}
12784
12785private:
12786 void handler()
12787 {
12788 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12789 }
12790
12791 Reason_T m_enmReason;
12792 Utf8Str m_strStateFilePath;
12793
12794 friend class SessionMachine;
12795};
12796
12797/**
12798 * Task thread implementation for SessionMachine::SaveState(), called from
12799 * SessionMachine::taskHandler().
12800 *
12801 * @note Locks this object for writing.
12802 *
12803 * @param task
12804 * @return
12805 */
12806void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12807{
12808 LogFlowThisFuncEnter();
12809
12810 AutoCaller autoCaller(this);
12811 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12812 if (FAILED(autoCaller.rc()))
12813 {
12814 /* we might have been uninitialized because the session was accidentally
12815 * closed by the client, so don't assert */
12816 HRESULT rc = setError(E_FAIL,
12817 tr("The session has been accidentally closed"));
12818 task.m_pProgress->i_notifyComplete(rc);
12819 LogFlowThisFuncLeave();
12820 return;
12821 }
12822
12823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12824
12825 HRESULT rc = S_OK;
12826
12827 try
12828 {
12829 ComPtr<IInternalSessionControl> directControl;
12830 if (mData->mSession.mLockType == LockType_VM)
12831 directControl = mData->mSession.mDirectControl;
12832 if (directControl.isNull())
12833 throw setError(VBOX_E_INVALID_VM_STATE,
12834 tr("Trying to save state without a running VM"));
12835 alock.release();
12836 BOOL fSuspendedBySave;
12837 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12838 Assert(!fSuspendedBySave);
12839 alock.acquire();
12840
12841 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12842 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12843 throw E_FAIL);
12844
12845 if (SUCCEEDED(rc))
12846 {
12847 mSSData->strStateFilePath = task.m_strStateFilePath;
12848
12849 /* save all VM settings */
12850 rc = i_saveSettings(NULL);
12851 // no need to check whether VirtualBox.xml needs saving also since
12852 // we can't have a name change pending at this point
12853 }
12854 else
12855 {
12856 // On failure, set the state to the state we had at the beginning.
12857 i_setMachineState(task.m_machineStateBackup);
12858 i_updateMachineStateOnClient();
12859
12860 // Delete the saved state file (might have been already created).
12861 // No need to check whether this is shared with a snapshot here
12862 // because we certainly created a fresh saved state file here.
12863 RTFileDelete(task.m_strStateFilePath.c_str());
12864 }
12865 }
12866 catch (HRESULT aRC) { rc = aRC; }
12867
12868 task.m_pProgress->i_notifyComplete(rc);
12869
12870 LogFlowThisFuncLeave();
12871}
12872
12873/**
12874 * @note Locks this object for writing.
12875 */
12876HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12877{
12878 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12879}
12880
12881HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12882{
12883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12884
12885 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12886 if (FAILED(rc)) return rc;
12887
12888 if ( mData->mMachineState != MachineState_Running
12889 && mData->mMachineState != MachineState_Paused
12890 )
12891 return setError(VBOX_E_INVALID_VM_STATE,
12892 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12893 Global::stringifyMachineState(mData->mMachineState));
12894
12895 ComObjPtr<Progress> pProgress;
12896 pProgress.createObject();
12897 rc = pProgress->init(i_getVirtualBox(),
12898 static_cast<IMachine *>(this) /* aInitiator */,
12899 tr("Saving the execution state of the virtual machine"),
12900 FALSE /* aCancelable */);
12901 if (FAILED(rc))
12902 return rc;
12903
12904 Utf8Str strStateFilePath;
12905 i_composeSavedStateFilename(strStateFilePath);
12906
12907 /* create and start the task on a separate thread (note that it will not
12908 * start working until we release alock) */
12909 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12910 rc = pTask->createThread();
12911 if (FAILED(rc))
12912 return rc;
12913
12914 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12915 i_setMachineState(MachineState_Saving);
12916 i_updateMachineStateOnClient();
12917
12918 pProgress.queryInterfaceTo(aProgress.asOutParam());
12919
12920 return S_OK;
12921}
12922
12923/**
12924 * @note Locks this object for writing.
12925 */
12926HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12927{
12928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12929
12930 HRESULT rc = i_checkStateDependency(MutableStateDep);
12931 if (FAILED(rc)) return rc;
12932
12933 if ( mData->mMachineState != MachineState_PoweredOff
12934 && mData->mMachineState != MachineState_Teleported
12935 && mData->mMachineState != MachineState_Aborted
12936 )
12937 return setError(VBOX_E_INVALID_VM_STATE,
12938 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12939 Global::stringifyMachineState(mData->mMachineState));
12940
12941 com::Utf8Str stateFilePathFull;
12942 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12943 if (RT_FAILURE(vrc))
12944 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12945 tr("Invalid saved state file path '%s' (%Rrc)"),
12946 aSavedStateFile.c_str(),
12947 vrc);
12948
12949 mSSData->strStateFilePath = stateFilePathFull;
12950
12951 /* The below i_setMachineState() will detect the state transition and will
12952 * update the settings file */
12953
12954 return i_setMachineState(MachineState_Saved);
12955}
12956
12957/**
12958 * @note Locks this object for writing.
12959 */
12960HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12961{
12962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12963
12964 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12965 if (FAILED(rc)) return rc;
12966
12967 if (mData->mMachineState != MachineState_Saved)
12968 return setError(VBOX_E_INVALID_VM_STATE,
12969 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12970 Global::stringifyMachineState(mData->mMachineState));
12971
12972 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12973
12974 /*
12975 * Saved -> PoweredOff transition will be detected in the SessionMachine
12976 * and properly handled.
12977 */
12978 rc = i_setMachineState(MachineState_PoweredOff);
12979 return rc;
12980}
12981
12982
12983/**
12984 * @note Locks the same as #i_setMachineState() does.
12985 */
12986HRESULT SessionMachine::updateState(MachineState_T aState)
12987{
12988 return i_setMachineState(aState);
12989}
12990
12991/**
12992 * @note Locks this object for writing.
12993 */
12994HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12995{
12996 IProgress *pProgress(aProgress);
12997
12998 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12999
13000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13001
13002 if (mData->mSession.mState != SessionState_Locked)
13003 return VBOX_E_INVALID_OBJECT_STATE;
13004
13005 if (!mData->mSession.mProgress.isNull())
13006 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13007
13008 /* If we didn't reference the NAT network service yet, add a reference to
13009 * force a start */
13010 if (miNATNetworksStarted < 1)
13011 {
13012 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13013 {
13014 BOOL enabled;
13015 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13016 if ( FAILED(hrc)
13017 || !enabled)
13018 continue;
13019
13020 NetworkAttachmentType_T type;
13021 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13022 if ( SUCCEEDED(hrc)
13023 && type == NetworkAttachmentType_NATNetwork)
13024 {
13025 Bstr name;
13026 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13027 if (SUCCEEDED(hrc))
13028 {
13029 Utf8Str strName(name);
13030 LogRel(("VM '%s' starts using NAT network '%s'\n",
13031 mUserData->s.strName.c_str(), strName.c_str()));
13032 mPeer->lockHandle()->unlockWrite();
13033 mParent->i_natNetworkRefInc(strName);
13034#ifdef RT_LOCK_STRICT
13035 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13036#else
13037 mPeer->lockHandle()->lockWrite();
13038#endif
13039 }
13040 }
13041 }
13042 miNATNetworksStarted++;
13043 }
13044
13045 LogFlowThisFunc(("returns S_OK.\n"));
13046 return S_OK;
13047}
13048
13049/**
13050 * @note Locks this object for writing.
13051 */
13052HRESULT SessionMachine::endPowerUp(LONG aResult)
13053{
13054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13055
13056 if (mData->mSession.mState != SessionState_Locked)
13057 return VBOX_E_INVALID_OBJECT_STATE;
13058
13059 /* Finalize the LaunchVMProcess progress object. */
13060 if (mData->mSession.mProgress)
13061 {
13062 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13063 mData->mSession.mProgress.setNull();
13064 }
13065
13066 if (SUCCEEDED((HRESULT)aResult))
13067 {
13068#ifdef VBOX_WITH_RESOURCE_USAGE_API
13069 /* The VM has been powered up successfully, so it makes sense
13070 * now to offer the performance metrics for a running machine
13071 * object. Doing it earlier wouldn't be safe. */
13072 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13073 mData->mSession.mPID);
13074#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13075 }
13076
13077 return S_OK;
13078}
13079
13080/**
13081 * @note Locks this object for writing.
13082 */
13083HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13084{
13085 LogFlowThisFuncEnter();
13086
13087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13088
13089 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13090 E_FAIL);
13091
13092 /* create a progress object to track operation completion */
13093 ComObjPtr<Progress> pProgress;
13094 pProgress.createObject();
13095 pProgress->init(i_getVirtualBox(),
13096 static_cast<IMachine *>(this) /* aInitiator */,
13097 tr("Stopping the virtual machine"),
13098 FALSE /* aCancelable */);
13099
13100 /* fill in the console task data */
13101 mConsoleTaskData.mLastState = mData->mMachineState;
13102 mConsoleTaskData.mProgress = pProgress;
13103
13104 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13105 i_setMachineState(MachineState_Stopping);
13106
13107 pProgress.queryInterfaceTo(aProgress.asOutParam());
13108
13109 return S_OK;
13110}
13111
13112/**
13113 * @note Locks this object for writing.
13114 */
13115HRESULT SessionMachine::endPoweringDown(LONG aResult,
13116 const com::Utf8Str &aErrMsg)
13117{
13118 HRESULT const hrcResult = (HRESULT)aResult;
13119 LogFlowThisFuncEnter();
13120
13121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13122
13123 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13124 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13125 && mConsoleTaskData.mLastState != MachineState_Null,
13126 E_FAIL);
13127
13128 /*
13129 * On failure, set the state to the state we had when BeginPoweringDown()
13130 * was called (this is expected by Console::PowerDown() and the associated
13131 * task). On success the VM process already changed the state to
13132 * MachineState_PoweredOff, so no need to do anything.
13133 */
13134 if (FAILED(hrcResult))
13135 i_setMachineState(mConsoleTaskData.mLastState);
13136
13137 /* notify the progress object about operation completion */
13138 Assert(mConsoleTaskData.mProgress);
13139 if (SUCCEEDED(hrcResult))
13140 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13141 else
13142 {
13143 if (aErrMsg.length())
13144 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13145 COM_IIDOF(ISession),
13146 getComponentName(),
13147 aErrMsg.c_str());
13148 else
13149 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13150 }
13151
13152 /* clear out the temporary saved state data */
13153 mConsoleTaskData.mLastState = MachineState_Null;
13154 mConsoleTaskData.mProgress.setNull();
13155
13156 LogFlowThisFuncLeave();
13157 return S_OK;
13158}
13159
13160
13161/**
13162 * Goes through the USB filters of the given machine to see if the given
13163 * device matches any filter or not.
13164 *
13165 * @note Locks the same as USBController::hasMatchingFilter() does.
13166 */
13167HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13168 BOOL *aMatched,
13169 ULONG *aMaskedInterfaces)
13170{
13171 LogFlowThisFunc(("\n"));
13172
13173#ifdef VBOX_WITH_USB
13174 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13175#else
13176 NOREF(aDevice);
13177 NOREF(aMaskedInterfaces);
13178 *aMatched = FALSE;
13179#endif
13180
13181 return S_OK;
13182}
13183
13184/**
13185 * @note Locks the same as Host::captureUSBDevice() does.
13186 */
13187HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13188{
13189 LogFlowThisFunc(("\n"));
13190
13191#ifdef VBOX_WITH_USB
13192 /* if captureDeviceForVM() fails, it must have set extended error info */
13193 clearError();
13194 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13195 if (FAILED(rc)) return rc;
13196
13197 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13198 AssertReturn(service, E_FAIL);
13199 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13200#else
13201 RT_NOREF(aId, aCaptureFilename);
13202 return E_NOTIMPL;
13203#endif
13204}
13205
13206/**
13207 * @note Locks the same as Host::detachUSBDevice() does.
13208 */
13209HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13210 BOOL aDone)
13211{
13212 LogFlowThisFunc(("\n"));
13213
13214#ifdef VBOX_WITH_USB
13215 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13216 AssertReturn(service, E_FAIL);
13217 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13218#else
13219 NOREF(aId);
13220 NOREF(aDone);
13221 return E_NOTIMPL;
13222#endif
13223}
13224
13225/**
13226 * Inserts all machine filters to the USB proxy service and then calls
13227 * Host::autoCaptureUSBDevices().
13228 *
13229 * Called by Console from the VM process upon VM startup.
13230 *
13231 * @note Locks what called methods lock.
13232 */
13233HRESULT SessionMachine::autoCaptureUSBDevices()
13234{
13235 LogFlowThisFunc(("\n"));
13236
13237#ifdef VBOX_WITH_USB
13238 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13239 AssertComRC(rc);
13240 NOREF(rc);
13241
13242 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13243 AssertReturn(service, E_FAIL);
13244 return service->autoCaptureDevicesForVM(this);
13245#else
13246 return S_OK;
13247#endif
13248}
13249
13250/**
13251 * Removes all machine filters from the USB proxy service and then calls
13252 * Host::detachAllUSBDevices().
13253 *
13254 * Called by Console from the VM process upon normal VM termination or by
13255 * SessionMachine::uninit() upon abnormal VM termination (from under the
13256 * Machine/SessionMachine lock).
13257 *
13258 * @note Locks what called methods lock.
13259 */
13260HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13261{
13262 LogFlowThisFunc(("\n"));
13263
13264#ifdef VBOX_WITH_USB
13265 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13266 AssertComRC(rc);
13267 NOREF(rc);
13268
13269 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13270 AssertReturn(service, E_FAIL);
13271 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13272#else
13273 NOREF(aDone);
13274 return S_OK;
13275#endif
13276}
13277
13278/**
13279 * @note Locks this object for writing.
13280 */
13281HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13282 ComPtr<IProgress> &aProgress)
13283{
13284 LogFlowThisFuncEnter();
13285
13286 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13287 /*
13288 * We don't assert below because it might happen that a non-direct session
13289 * informs us it is closed right after we've been uninitialized -- it's ok.
13290 */
13291
13292 /* get IInternalSessionControl interface */
13293 ComPtr<IInternalSessionControl> control(aSession);
13294
13295 ComAssertRet(!control.isNull(), E_INVALIDARG);
13296
13297 /* Creating a Progress object requires the VirtualBox lock, and
13298 * thus locking it here is required by the lock order rules. */
13299 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13300
13301 if (control == mData->mSession.mDirectControl)
13302 {
13303 /* The direct session is being normally closed by the client process
13304 * ----------------------------------------------------------------- */
13305
13306 /* go to the closing state (essential for all open*Session() calls and
13307 * for #i_checkForDeath()) */
13308 Assert(mData->mSession.mState == SessionState_Locked);
13309 mData->mSession.mState = SessionState_Unlocking;
13310
13311 /* set direct control to NULL to release the remote instance */
13312 mData->mSession.mDirectControl.setNull();
13313 LogFlowThisFunc(("Direct control is set to NULL\n"));
13314
13315 if (mData->mSession.mProgress)
13316 {
13317 /* finalize the progress, someone might wait if a frontend
13318 * closes the session before powering on the VM. */
13319 mData->mSession.mProgress->notifyComplete(E_FAIL,
13320 COM_IIDOF(ISession),
13321 getComponentName(),
13322 tr("The VM session was closed before any attempt to power it on"));
13323 mData->mSession.mProgress.setNull();
13324 }
13325
13326 /* Create the progress object the client will use to wait until
13327 * #i_checkForDeath() is called to uninitialize this session object after
13328 * it releases the IPC semaphore.
13329 * Note! Because we're "reusing" mProgress here, this must be a proxy
13330 * object just like for LaunchVMProcess. */
13331 Assert(mData->mSession.mProgress.isNull());
13332 ComObjPtr<ProgressProxy> progress;
13333 progress.createObject();
13334 ComPtr<IUnknown> pPeer(mPeer);
13335 progress->init(mParent, pPeer,
13336 Bstr(tr("Closing session")).raw(),
13337 FALSE /* aCancelable */);
13338 progress.queryInterfaceTo(aProgress.asOutParam());
13339 mData->mSession.mProgress = progress;
13340 }
13341 else
13342 {
13343 /* the remote session is being normally closed */
13344 bool found = false;
13345 for (Data::Session::RemoteControlList::iterator
13346 it = mData->mSession.mRemoteControls.begin();
13347 it != mData->mSession.mRemoteControls.end();
13348 ++it)
13349 {
13350 if (control == *it)
13351 {
13352 found = true;
13353 // This MUST be erase(it), not remove(*it) as the latter
13354 // triggers a very nasty use after free due to the place where
13355 // the value "lives".
13356 mData->mSession.mRemoteControls.erase(it);
13357 break;
13358 }
13359 }
13360 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13361 E_INVALIDARG);
13362 }
13363
13364 /* signal the client watcher thread, because the client is going away */
13365 mParent->i_updateClientWatcher();
13366
13367 LogFlowThisFuncLeave();
13368 return S_OK;
13369}
13370
13371HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13372{
13373#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13374 ULONG uID;
13375 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13376 if (RT_SUCCESS(rc))
13377 {
13378 if (aID)
13379 *aID = uID;
13380 return S_OK;
13381 }
13382 return E_FAIL;
13383#else
13384 RT_NOREF(aParms, aID);
13385 ReturnComNotImplemented();
13386#endif
13387}
13388
13389HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13390{
13391#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13392 return mParent->i_onClipboardAreaUnregister(aID);
13393#else
13394 RT_NOREF(aID);
13395 ReturnComNotImplemented();
13396#endif
13397}
13398
13399HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13400{
13401#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13402 return mParent->i_onClipboardAreaAttach(aID);
13403#else
13404 RT_NOREF(aID);
13405 ReturnComNotImplemented();
13406#endif
13407}
13408HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13409{
13410#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13411 return mParent->i_onClipboardAreaDetach(aID);
13412#else
13413 RT_NOREF(aID);
13414 ReturnComNotImplemented();
13415#endif
13416}
13417
13418HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13419{
13420#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13421 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13422 if (aID)
13423 *aID = uID;
13424 return S_OK;
13425#else
13426 RT_NOREF(aID);
13427 ReturnComNotImplemented();
13428#endif
13429}
13430
13431HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13432{
13433#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13434 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13435 if (aRefCount)
13436 *aRefCount = uRefCount;
13437 return S_OK;
13438#else
13439 RT_NOREF(aID, aRefCount);
13440 ReturnComNotImplemented();
13441#endif
13442}
13443
13444HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13445 std::vector<com::Utf8Str> &aValues,
13446 std::vector<LONG64> &aTimestamps,
13447 std::vector<com::Utf8Str> &aFlags)
13448{
13449 LogFlowThisFunc(("\n"));
13450
13451#ifdef VBOX_WITH_GUEST_PROPS
13452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13453
13454 size_t cEntries = mHWData->mGuestProperties.size();
13455 aNames.resize(cEntries);
13456 aValues.resize(cEntries);
13457 aTimestamps.resize(cEntries);
13458 aFlags.resize(cEntries);
13459
13460 size_t i = 0;
13461 for (HWData::GuestPropertyMap::const_iterator
13462 it = mHWData->mGuestProperties.begin();
13463 it != mHWData->mGuestProperties.end();
13464 ++it, ++i)
13465 {
13466 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13467 aNames[i] = it->first;
13468 aValues[i] = it->second.strValue;
13469 aTimestamps[i] = it->second.mTimestamp;
13470
13471 /* If it is NULL, keep it NULL. */
13472 if (it->second.mFlags)
13473 {
13474 GuestPropWriteFlags(it->second.mFlags, szFlags);
13475 aFlags[i] = szFlags;
13476 }
13477 else
13478 aFlags[i] = "";
13479 }
13480 return S_OK;
13481#else
13482 ReturnComNotImplemented();
13483#endif
13484}
13485
13486HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13487 const com::Utf8Str &aValue,
13488 LONG64 aTimestamp,
13489 const com::Utf8Str &aFlags)
13490{
13491 LogFlowThisFunc(("\n"));
13492
13493#ifdef VBOX_WITH_GUEST_PROPS
13494 try
13495 {
13496 /*
13497 * Convert input up front.
13498 */
13499 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13500 if (aFlags.length())
13501 {
13502 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13503 AssertRCReturn(vrc, E_INVALIDARG);
13504 }
13505
13506 /*
13507 * Now grab the object lock, validate the state and do the update.
13508 */
13509
13510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13511
13512 if (!Global::IsOnline(mData->mMachineState))
13513 {
13514 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13515 VBOX_E_INVALID_VM_STATE);
13516 }
13517
13518 i_setModified(IsModified_MachineData);
13519 mHWData.backup();
13520
13521 bool fDelete = !aValue.length();
13522 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13523 if (it != mHWData->mGuestProperties.end())
13524 {
13525 if (!fDelete)
13526 {
13527 it->second.strValue = aValue;
13528 it->second.mTimestamp = aTimestamp;
13529 it->second.mFlags = fFlags;
13530 }
13531 else
13532 mHWData->mGuestProperties.erase(it);
13533
13534 mData->mGuestPropertiesModified = TRUE;
13535 }
13536 else if (!fDelete)
13537 {
13538 HWData::GuestProperty prop;
13539 prop.strValue = aValue;
13540 prop.mTimestamp = aTimestamp;
13541 prop.mFlags = fFlags;
13542
13543 mHWData->mGuestProperties[aName] = prop;
13544 mData->mGuestPropertiesModified = TRUE;
13545 }
13546
13547 alock.release();
13548
13549 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13550 }
13551 catch (...)
13552 {
13553 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13554 }
13555 return S_OK;
13556#else
13557 ReturnComNotImplemented();
13558#endif
13559}
13560
13561
13562HRESULT SessionMachine::lockMedia()
13563{
13564 AutoMultiWriteLock2 alock(this->lockHandle(),
13565 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13566
13567 AssertReturn( mData->mMachineState == MachineState_Starting
13568 || mData->mMachineState == MachineState_Restoring
13569 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13570
13571 clearError();
13572 alock.release();
13573 return i_lockMedia();
13574}
13575
13576HRESULT SessionMachine::unlockMedia()
13577{
13578 HRESULT hrc = i_unlockMedia();
13579 return hrc;
13580}
13581
13582HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13583 ComPtr<IMediumAttachment> &aNewAttachment)
13584{
13585 // request the host lock first, since might be calling Host methods for getting host drives;
13586 // next, protect the media tree all the while we're in here, as well as our member variables
13587 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13588 this->lockHandle(),
13589 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13590
13591 IMediumAttachment *iAttach = aAttachment;
13592 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13593
13594 Utf8Str ctrlName;
13595 LONG lPort;
13596 LONG lDevice;
13597 bool fTempEject;
13598 {
13599 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13600
13601 /* Need to query the details first, as the IMediumAttachment reference
13602 * might be to the original settings, which we are going to change. */
13603 ctrlName = pAttach->i_getControllerName();
13604 lPort = pAttach->i_getPort();
13605 lDevice = pAttach->i_getDevice();
13606 fTempEject = pAttach->i_getTempEject();
13607 }
13608
13609 if (!fTempEject)
13610 {
13611 /* Remember previously mounted medium. The medium before taking the
13612 * backup is not necessarily the same thing. */
13613 ComObjPtr<Medium> oldmedium;
13614 oldmedium = pAttach->i_getMedium();
13615
13616 i_setModified(IsModified_Storage);
13617 mMediumAttachments.backup();
13618
13619 // The backup operation makes the pAttach reference point to the
13620 // old settings. Re-get the correct reference.
13621 pAttach = i_findAttachment(*mMediumAttachments.data(),
13622 ctrlName,
13623 lPort,
13624 lDevice);
13625
13626 {
13627 AutoCaller autoAttachCaller(this);
13628 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13629
13630 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13631 if (!oldmedium.isNull())
13632 oldmedium->i_removeBackReference(mData->mUuid);
13633
13634 pAttach->i_updateMedium(NULL);
13635 pAttach->i_updateEjected();
13636 }
13637
13638 i_setModified(IsModified_Storage);
13639 }
13640 else
13641 {
13642 {
13643 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13644 pAttach->i_updateEjected();
13645 }
13646 }
13647
13648 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13649
13650 return S_OK;
13651}
13652
13653HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13654 com::Utf8Str &aResult)
13655{
13656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13657
13658 HRESULT hr = S_OK;
13659
13660 if (!mAuthLibCtx.hAuthLibrary)
13661 {
13662 /* Load the external authentication library. */
13663 Bstr authLibrary;
13664 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13665
13666 Utf8Str filename = authLibrary;
13667
13668 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13669 if (RT_FAILURE(vrc))
13670 hr = setErrorBoth(E_FAIL, vrc,
13671 tr("Could not load the external authentication library '%s' (%Rrc)"),
13672 filename.c_str(), vrc);
13673 }
13674
13675 /* The auth library might need the machine lock. */
13676 alock.release();
13677
13678 if (FAILED(hr))
13679 return hr;
13680
13681 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13682 {
13683 enum VRDEAuthParams
13684 {
13685 parmUuid = 1,
13686 parmGuestJudgement,
13687 parmUser,
13688 parmPassword,
13689 parmDomain,
13690 parmClientId
13691 };
13692
13693 AuthResult result = AuthResultAccessDenied;
13694
13695 Guid uuid(aAuthParams[parmUuid]);
13696 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13697 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13698
13699 result = AuthLibAuthenticate(&mAuthLibCtx,
13700 uuid.raw(), guestJudgement,
13701 aAuthParams[parmUser].c_str(),
13702 aAuthParams[parmPassword].c_str(),
13703 aAuthParams[parmDomain].c_str(),
13704 u32ClientId);
13705
13706 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13707 size_t cbPassword = aAuthParams[parmPassword].length();
13708 if (cbPassword)
13709 {
13710 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13711 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13712 }
13713
13714 if (result == AuthResultAccessGranted)
13715 aResult = "granted";
13716 else
13717 aResult = "denied";
13718
13719 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13720 aAuthParams[parmUser].c_str(), aResult.c_str()));
13721 }
13722 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13723 {
13724 enum VRDEAuthDisconnectParams
13725 {
13726 parmUuid = 1,
13727 parmClientId
13728 };
13729
13730 Guid uuid(aAuthParams[parmUuid]);
13731 uint32_t u32ClientId = 0;
13732 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13733 }
13734 else
13735 {
13736 hr = E_INVALIDARG;
13737 }
13738
13739 return hr;
13740}
13741
13742// public methods only for internal purposes
13743/////////////////////////////////////////////////////////////////////////////
13744
13745#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13746/**
13747 * Called from the client watcher thread to check for expected or unexpected
13748 * death of the client process that has a direct session to this machine.
13749 *
13750 * On Win32 and on OS/2, this method is called only when we've got the
13751 * mutex (i.e. the client has either died or terminated normally) so it always
13752 * returns @c true (the client is terminated, the session machine is
13753 * uninitialized).
13754 *
13755 * On other platforms, the method returns @c true if the client process has
13756 * terminated normally or abnormally and the session machine was uninitialized,
13757 * and @c false if the client process is still alive.
13758 *
13759 * @note Locks this object for writing.
13760 */
13761bool SessionMachine::i_checkForDeath()
13762{
13763 Uninit::Reason reason;
13764 bool terminated = false;
13765
13766 /* Enclose autoCaller with a block because calling uninit() from under it
13767 * will deadlock. */
13768 {
13769 AutoCaller autoCaller(this);
13770 if (!autoCaller.isOk())
13771 {
13772 /* return true if not ready, to cause the client watcher to exclude
13773 * the corresponding session from watching */
13774 LogFlowThisFunc(("Already uninitialized!\n"));
13775 return true;
13776 }
13777
13778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13779
13780 /* Determine the reason of death: if the session state is Closing here,
13781 * everything is fine. Otherwise it means that the client did not call
13782 * OnSessionEnd() before it released the IPC semaphore. This may happen
13783 * either because the client process has abnormally terminated, or
13784 * because it simply forgot to call ISession::Close() before exiting. We
13785 * threat the latter also as an abnormal termination (see
13786 * Session::uninit() for details). */
13787 reason = mData->mSession.mState == SessionState_Unlocking ?
13788 Uninit::Normal :
13789 Uninit::Abnormal;
13790
13791 if (mClientToken)
13792 terminated = mClientToken->release();
13793 } /* AutoCaller block */
13794
13795 if (terminated)
13796 uninit(reason);
13797
13798 return terminated;
13799}
13800
13801void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13802{
13803 LogFlowThisFunc(("\n"));
13804
13805 strTokenId.setNull();
13806
13807 AutoCaller autoCaller(this);
13808 AssertComRCReturnVoid(autoCaller.rc());
13809
13810 Assert(mClientToken);
13811 if (mClientToken)
13812 mClientToken->getId(strTokenId);
13813}
13814#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13815IToken *SessionMachine::i_getToken()
13816{
13817 LogFlowThisFunc(("\n"));
13818
13819 AutoCaller autoCaller(this);
13820 AssertComRCReturn(autoCaller.rc(), NULL);
13821
13822 Assert(mClientToken);
13823 if (mClientToken)
13824 return mClientToken->getToken();
13825 else
13826 return NULL;
13827}
13828#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13829
13830Machine::ClientToken *SessionMachine::i_getClientToken()
13831{
13832 LogFlowThisFunc(("\n"));
13833
13834 AutoCaller autoCaller(this);
13835 AssertComRCReturn(autoCaller.rc(), NULL);
13836
13837 return mClientToken;
13838}
13839
13840
13841/**
13842 * @note Locks this object for reading.
13843 */
13844HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13845{
13846 LogFlowThisFunc(("\n"));
13847
13848 AutoCaller autoCaller(this);
13849 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13850
13851 ComPtr<IInternalSessionControl> directControl;
13852 {
13853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13854 if (mData->mSession.mLockType == LockType_VM)
13855 directControl = mData->mSession.mDirectControl;
13856 }
13857
13858 /* ignore notifications sent after #OnSessionEnd() is called */
13859 if (!directControl)
13860 return S_OK;
13861
13862 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13863}
13864
13865/**
13866 * @note Locks this object for reading.
13867 */
13868HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13869 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13870 const Utf8Str &aGuestIp, LONG aGuestPort)
13871{
13872 LogFlowThisFunc(("\n"));
13873
13874 AutoCaller autoCaller(this);
13875 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13876
13877 ComPtr<IInternalSessionControl> directControl;
13878 {
13879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13880 if (mData->mSession.mLockType == LockType_VM)
13881 directControl = mData->mSession.mDirectControl;
13882 }
13883
13884 /* ignore notifications sent after #OnSessionEnd() is called */
13885 if (!directControl)
13886 return S_OK;
13887 /*
13888 * instead acting like callback we ask IVirtualBox deliver corresponding event
13889 */
13890
13891 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13892 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13893 return S_OK;
13894}
13895
13896/**
13897 * @note Locks this object for reading.
13898 */
13899HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13900{
13901 LogFlowThisFunc(("\n"));
13902
13903 AutoCaller autoCaller(this);
13904 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13905
13906 ComPtr<IInternalSessionControl> directControl;
13907 {
13908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13909 if (mData->mSession.mLockType == LockType_VM)
13910 directControl = mData->mSession.mDirectControl;
13911 }
13912
13913 /* ignore notifications sent after #OnSessionEnd() is called */
13914 if (!directControl)
13915 return S_OK;
13916
13917 return directControl->OnAudioAdapterChange(audioAdapter);
13918}
13919
13920/**
13921 * @note Locks this object for reading.
13922 */
13923HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13924{
13925 LogFlowThisFunc(("\n"));
13926
13927 AutoCaller autoCaller(this);
13928 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13929
13930 ComPtr<IInternalSessionControl> directControl;
13931 {
13932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13933 if (mData->mSession.mLockType == LockType_VM)
13934 directControl = mData->mSession.mDirectControl;
13935 }
13936
13937 /* ignore notifications sent after #OnSessionEnd() is called */
13938 if (!directControl)
13939 return S_OK;
13940
13941 return directControl->OnSerialPortChange(serialPort);
13942}
13943
13944/**
13945 * @note Locks this object for reading.
13946 */
13947HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13948{
13949 LogFlowThisFunc(("\n"));
13950
13951 AutoCaller autoCaller(this);
13952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13953
13954 ComPtr<IInternalSessionControl> directControl;
13955 {
13956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13957 if (mData->mSession.mLockType == LockType_VM)
13958 directControl = mData->mSession.mDirectControl;
13959 }
13960
13961 /* ignore notifications sent after #OnSessionEnd() is called */
13962 if (!directControl)
13963 return S_OK;
13964
13965 return directControl->OnParallelPortChange(parallelPort);
13966}
13967
13968/**
13969 * @note Locks this object for reading.
13970 */
13971HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13972{
13973 LogFlowThisFunc(("\n"));
13974
13975 AutoCaller autoCaller(this);
13976 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13977
13978 ComPtr<IInternalSessionControl> directControl;
13979 {
13980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13981 if (mData->mSession.mLockType == LockType_VM)
13982 directControl = mData->mSession.mDirectControl;
13983 }
13984
13985 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
13986
13987 /* ignore notifications sent after #OnSessionEnd() is called */
13988 if (!directControl)
13989 return S_OK;
13990
13991 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
13992}
13993
13994/**
13995 * @note Locks this object for reading.
13996 */
13997HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13998{
13999 LogFlowThisFunc(("\n"));
14000
14001 AutoCaller autoCaller(this);
14002 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14003
14004 ComPtr<IInternalSessionControl> directControl;
14005 {
14006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14007 if (mData->mSession.mLockType == LockType_VM)
14008 directControl = mData->mSession.mDirectControl;
14009 }
14010
14011 mParent->i_onMediumChanged(aAttachment);
14012
14013 /* ignore notifications sent after #OnSessionEnd() is called */
14014 if (!directControl)
14015 return S_OK;
14016
14017 return directControl->OnMediumChange(aAttachment, aForce);
14018}
14019
14020HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14021{
14022 LogFlowThisFunc(("\n"));
14023
14024 AutoCaller autoCaller(this);
14025 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14026
14027 ComPtr<IInternalSessionControl> directControl;
14028 {
14029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14030 if (mData->mSession.mLockType == LockType_VM)
14031 directControl = mData->mSession.mDirectControl;
14032 }
14033
14034 /* ignore notifications sent after #OnSessionEnd() is called */
14035 if (!directControl)
14036 return S_OK;
14037
14038 return directControl->OnVMProcessPriorityChange(aPriority);
14039}
14040
14041/**
14042 * @note Locks this object for reading.
14043 */
14044HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14045{
14046 LogFlowThisFunc(("\n"));
14047
14048 AutoCaller autoCaller(this);
14049 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14050
14051 ComPtr<IInternalSessionControl> directControl;
14052 {
14053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14054 if (mData->mSession.mLockType == LockType_VM)
14055 directControl = mData->mSession.mDirectControl;
14056 }
14057
14058 /* ignore notifications sent after #OnSessionEnd() is called */
14059 if (!directControl)
14060 return S_OK;
14061
14062 return directControl->OnCPUChange(aCPU, aRemove);
14063}
14064
14065HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14066{
14067 LogFlowThisFunc(("\n"));
14068
14069 AutoCaller autoCaller(this);
14070 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14071
14072 ComPtr<IInternalSessionControl> directControl;
14073 {
14074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14075 if (mData->mSession.mLockType == LockType_VM)
14076 directControl = mData->mSession.mDirectControl;
14077 }
14078
14079 /* ignore notifications sent after #OnSessionEnd() is called */
14080 if (!directControl)
14081 return S_OK;
14082
14083 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14084}
14085
14086/**
14087 * @note Locks this object for reading.
14088 */
14089HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14090{
14091 LogFlowThisFunc(("\n"));
14092
14093 AutoCaller autoCaller(this);
14094 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14095
14096 ComPtr<IInternalSessionControl> directControl;
14097 {
14098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14099 if (mData->mSession.mLockType == LockType_VM)
14100 directControl = mData->mSession.mDirectControl;
14101 }
14102
14103 /* ignore notifications sent after #OnSessionEnd() is called */
14104 if (!directControl)
14105 return S_OK;
14106
14107 return directControl->OnVRDEServerChange(aRestart);
14108}
14109
14110/**
14111 * @note Locks this object for reading.
14112 */
14113HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14114{
14115 LogFlowThisFunc(("\n"));
14116
14117 AutoCaller autoCaller(this);
14118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14119
14120 ComPtr<IInternalSessionControl> directControl;
14121 {
14122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14123 if (mData->mSession.mLockType == LockType_VM)
14124 directControl = mData->mSession.mDirectControl;
14125 }
14126
14127 /* ignore notifications sent after #OnSessionEnd() is called */
14128 if (!directControl)
14129 return S_OK;
14130
14131 return directControl->OnRecordingChange(aEnable);
14132}
14133
14134/**
14135 * @note Locks this object for reading.
14136 */
14137HRESULT SessionMachine::i_onUSBControllerChange()
14138{
14139 LogFlowThisFunc(("\n"));
14140
14141 AutoCaller autoCaller(this);
14142 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14143
14144 ComPtr<IInternalSessionControl> directControl;
14145 {
14146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14147 if (mData->mSession.mLockType == LockType_VM)
14148 directControl = mData->mSession.mDirectControl;
14149 }
14150
14151 /* ignore notifications sent after #OnSessionEnd() is called */
14152 if (!directControl)
14153 return S_OK;
14154
14155 return directControl->OnUSBControllerChange();
14156}
14157
14158/**
14159 * @note Locks this object for reading.
14160 */
14161HRESULT SessionMachine::i_onSharedFolderChange()
14162{
14163 LogFlowThisFunc(("\n"));
14164
14165 AutoCaller autoCaller(this);
14166 AssertComRCReturnRC(autoCaller.rc());
14167
14168 ComPtr<IInternalSessionControl> directControl;
14169 {
14170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14171 if (mData->mSession.mLockType == LockType_VM)
14172 directControl = mData->mSession.mDirectControl;
14173 }
14174
14175 /* ignore notifications sent after #OnSessionEnd() is called */
14176 if (!directControl)
14177 return S_OK;
14178
14179 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14180}
14181
14182/**
14183 * @note Locks this object for reading.
14184 */
14185HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14186{
14187 LogFlowThisFunc(("\n"));
14188
14189 AutoCaller autoCaller(this);
14190 AssertComRCReturnRC(autoCaller.rc());
14191
14192 ComPtr<IInternalSessionControl> directControl;
14193 {
14194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14195 if (mData->mSession.mLockType == LockType_VM)
14196 directControl = mData->mSession.mDirectControl;
14197 }
14198
14199 /* ignore notifications sent after #OnSessionEnd() is called */
14200 if (!directControl)
14201 return S_OK;
14202
14203 return directControl->OnClipboardModeChange(aClipboardMode);
14204}
14205
14206/**
14207 * @note Locks this object for reading.
14208 */
14209HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14210{
14211 LogFlowThisFunc(("\n"));
14212
14213 AutoCaller autoCaller(this);
14214 AssertComRCReturnRC(autoCaller.rc());
14215
14216 ComPtr<IInternalSessionControl> directControl;
14217 {
14218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14219 if (mData->mSession.mLockType == LockType_VM)
14220 directControl = mData->mSession.mDirectControl;
14221 }
14222
14223 /* ignore notifications sent after #OnSessionEnd() is called */
14224 if (!directControl)
14225 return S_OK;
14226
14227 return directControl->OnClipboardFileTransferModeChange(aEnable);
14228}
14229
14230/**
14231 * @note Locks this object for reading.
14232 */
14233HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14234{
14235 LogFlowThisFunc(("\n"));
14236
14237 AutoCaller autoCaller(this);
14238 AssertComRCReturnRC(autoCaller.rc());
14239
14240 ComPtr<IInternalSessionControl> directControl;
14241 {
14242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14243 if (mData->mSession.mLockType == LockType_VM)
14244 directControl = mData->mSession.mDirectControl;
14245 }
14246
14247 /* ignore notifications sent after #OnSessionEnd() is called */
14248 if (!directControl)
14249 return S_OK;
14250
14251 return directControl->OnDnDModeChange(aDnDMode);
14252}
14253
14254/**
14255 * @note Locks this object for reading.
14256 */
14257HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14258{
14259 LogFlowThisFunc(("\n"));
14260
14261 AutoCaller autoCaller(this);
14262 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14263
14264 ComPtr<IInternalSessionControl> directControl;
14265 {
14266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14267 if (mData->mSession.mLockType == LockType_VM)
14268 directControl = mData->mSession.mDirectControl;
14269 }
14270
14271 /* ignore notifications sent after #OnSessionEnd() is called */
14272 if (!directControl)
14273 return S_OK;
14274
14275 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14276}
14277
14278/**
14279 * @note Locks this object for reading.
14280 */
14281HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14282{
14283 LogFlowThisFunc(("\n"));
14284
14285 AutoCaller autoCaller(this);
14286 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14287
14288 ComPtr<IInternalSessionControl> directControl;
14289 {
14290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14291 if (mData->mSession.mLockType == LockType_VM)
14292 directControl = mData->mSession.mDirectControl;
14293 }
14294
14295 /* ignore notifications sent after #OnSessionEnd() is called */
14296 if (!directControl)
14297 return S_OK;
14298
14299 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14300}
14301
14302/**
14303 * Returns @c true if this machine's USB controller reports it has a matching
14304 * filter for the given USB device and @c false otherwise.
14305 *
14306 * @note locks this object for reading.
14307 */
14308bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14309{
14310 AutoCaller autoCaller(this);
14311 /* silently return if not ready -- this method may be called after the
14312 * direct machine session has been called */
14313 if (!autoCaller.isOk())
14314 return false;
14315
14316#ifdef VBOX_WITH_USB
14317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14318
14319 switch (mData->mMachineState)
14320 {
14321 case MachineState_Starting:
14322 case MachineState_Restoring:
14323 case MachineState_TeleportingIn:
14324 case MachineState_Paused:
14325 case MachineState_Running:
14326 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14327 * elsewhere... */
14328 alock.release();
14329 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14330 default: break;
14331 }
14332#else
14333 NOREF(aDevice);
14334 NOREF(aMaskedIfs);
14335#endif
14336 return false;
14337}
14338
14339/**
14340 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14341 */
14342HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14343 IVirtualBoxErrorInfo *aError,
14344 ULONG aMaskedIfs,
14345 const com::Utf8Str &aCaptureFilename)
14346{
14347 LogFlowThisFunc(("\n"));
14348
14349 AutoCaller autoCaller(this);
14350
14351 /* This notification may happen after the machine object has been
14352 * uninitialized (the session was closed), so don't assert. */
14353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14354
14355 ComPtr<IInternalSessionControl> directControl;
14356 {
14357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14358 if (mData->mSession.mLockType == LockType_VM)
14359 directControl = mData->mSession.mDirectControl;
14360 }
14361
14362 /* fail on notifications sent after #OnSessionEnd() is called, it is
14363 * expected by the caller */
14364 if (!directControl)
14365 return E_FAIL;
14366
14367 /* No locks should be held at this point. */
14368 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14369 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14370
14371 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14372}
14373
14374/**
14375 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14376 */
14377HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14378 IVirtualBoxErrorInfo *aError)
14379{
14380 LogFlowThisFunc(("\n"));
14381
14382 AutoCaller autoCaller(this);
14383
14384 /* This notification may happen after the machine object has been
14385 * uninitialized (the session was closed), so don't assert. */
14386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14387
14388 ComPtr<IInternalSessionControl> directControl;
14389 {
14390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14391 if (mData->mSession.mLockType == LockType_VM)
14392 directControl = mData->mSession.mDirectControl;
14393 }
14394
14395 /* fail on notifications sent after #OnSessionEnd() is called, it is
14396 * expected by the caller */
14397 if (!directControl)
14398 return E_FAIL;
14399
14400 /* No locks should be held at this point. */
14401 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14402 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14403
14404 return directControl->OnUSBDeviceDetach(aId, aError);
14405}
14406
14407// protected methods
14408/////////////////////////////////////////////////////////////////////////////
14409
14410/**
14411 * Deletes the given file if it is no longer in use by either the current machine state
14412 * (if the machine is "saved") or any of the machine's snapshots.
14413 *
14414 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14415 * but is different for each SnapshotMachine. When calling this, the order of calling this
14416 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14417 * is therefore critical. I know, it's all rather messy.
14418 *
14419 * @param strStateFile
14420 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14421 * the test for whether the saved state file is in use.
14422 */
14423void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14424 Snapshot *pSnapshotToIgnore)
14425{
14426 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14427 if ( (strStateFile.isNotEmpty())
14428 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14429 )
14430 // ... and it must also not be shared with other snapshots
14431 if ( !mData->mFirstSnapshot
14432 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14433 // this checks the SnapshotMachine's state file paths
14434 )
14435 RTFileDelete(strStateFile.c_str());
14436}
14437
14438/**
14439 * Locks the attached media.
14440 *
14441 * All attached hard disks are locked for writing and DVD/floppy are locked for
14442 * reading. Parents of attached hard disks (if any) are locked for reading.
14443 *
14444 * This method also performs accessibility check of all media it locks: if some
14445 * media is inaccessible, the method will return a failure and a bunch of
14446 * extended error info objects per each inaccessible medium.
14447 *
14448 * Note that this method is atomic: if it returns a success, all media are
14449 * locked as described above; on failure no media is locked at all (all
14450 * succeeded individual locks will be undone).
14451 *
14452 * The caller is responsible for doing the necessary state sanity checks.
14453 *
14454 * The locks made by this method must be undone by calling #unlockMedia() when
14455 * no more needed.
14456 */
14457HRESULT SessionMachine::i_lockMedia()
14458{
14459 AutoCaller autoCaller(this);
14460 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14461
14462 AutoMultiWriteLock2 alock(this->lockHandle(),
14463 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14464
14465 /* bail out if trying to lock things with already set up locking */
14466 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14467
14468 MultiResult mrc(S_OK);
14469
14470 /* Collect locking information for all medium objects attached to the VM. */
14471 for (MediumAttachmentList::const_iterator
14472 it = mMediumAttachments->begin();
14473 it != mMediumAttachments->end();
14474 ++it)
14475 {
14476 MediumAttachment *pAtt = *it;
14477 DeviceType_T devType = pAtt->i_getType();
14478 Medium *pMedium = pAtt->i_getMedium();
14479
14480 MediumLockList *pMediumLockList(new MediumLockList());
14481 // There can be attachments without a medium (floppy/dvd), and thus
14482 // it's impossible to create a medium lock list. It still makes sense
14483 // to have the empty medium lock list in the map in case a medium is
14484 // attached later.
14485 if (pMedium != NULL)
14486 {
14487 MediumType_T mediumType = pMedium->i_getType();
14488 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14489 || mediumType == MediumType_Shareable;
14490 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14491
14492 alock.release();
14493 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14494 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14495 false /* fMediumLockWriteAll */,
14496 NULL,
14497 *pMediumLockList);
14498 alock.acquire();
14499 if (FAILED(mrc))
14500 {
14501 delete pMediumLockList;
14502 mData->mSession.mLockedMedia.Clear();
14503 break;
14504 }
14505 }
14506
14507 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14508 if (FAILED(rc))
14509 {
14510 mData->mSession.mLockedMedia.Clear();
14511 mrc = setError(rc,
14512 tr("Collecting locking information for all attached media failed"));
14513 break;
14514 }
14515 }
14516
14517 if (SUCCEEDED(mrc))
14518 {
14519 /* Now lock all media. If this fails, nothing is locked. */
14520 alock.release();
14521 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14522 alock.acquire();
14523 if (FAILED(rc))
14524 {
14525 mrc = setError(rc,
14526 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14527 }
14528 }
14529
14530 return mrc;
14531}
14532
14533/**
14534 * Undoes the locks made by by #lockMedia().
14535 */
14536HRESULT SessionMachine::i_unlockMedia()
14537{
14538 AutoCaller autoCaller(this);
14539 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14540
14541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14542
14543 /* we may be holding important error info on the current thread;
14544 * preserve it */
14545 ErrorInfoKeeper eik;
14546
14547 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14548 AssertComRC(rc);
14549 return rc;
14550}
14551
14552/**
14553 * Helper to change the machine state (reimplementation).
14554 *
14555 * @note Locks this object for writing.
14556 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14557 * it can cause crashes in random places due to unexpectedly committing
14558 * the current settings. The caller is responsible for that. The call
14559 * to saveStateSettings is fine, because this method does not commit.
14560 */
14561HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14562{
14563 LogFlowThisFuncEnter();
14564 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14565
14566 AutoCaller autoCaller(this);
14567 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14568
14569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14570
14571 MachineState_T oldMachineState = mData->mMachineState;
14572
14573 AssertMsgReturn(oldMachineState != aMachineState,
14574 ("oldMachineState=%s, aMachineState=%s\n",
14575 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14576 E_FAIL);
14577
14578 HRESULT rc = S_OK;
14579
14580 int stsFlags = 0;
14581 bool deleteSavedState = false;
14582
14583 /* detect some state transitions */
14584
14585 if ( ( oldMachineState == MachineState_Saved
14586 && aMachineState == MachineState_Restoring)
14587 || ( ( oldMachineState == MachineState_PoweredOff
14588 || oldMachineState == MachineState_Teleported
14589 || oldMachineState == MachineState_Aborted
14590 )
14591 && ( aMachineState == MachineState_TeleportingIn
14592 || aMachineState == MachineState_Starting
14593 )
14594 )
14595 )
14596 {
14597 /* The EMT thread is about to start */
14598
14599 /* Nothing to do here for now... */
14600
14601 /// @todo NEWMEDIA don't let mDVDDrive and other children
14602 /// change anything when in the Starting/Restoring state
14603 }
14604 else if ( ( oldMachineState == MachineState_Running
14605 || oldMachineState == MachineState_Paused
14606 || oldMachineState == MachineState_Teleporting
14607 || oldMachineState == MachineState_OnlineSnapshotting
14608 || oldMachineState == MachineState_LiveSnapshotting
14609 || oldMachineState == MachineState_Stuck
14610 || oldMachineState == MachineState_Starting
14611 || oldMachineState == MachineState_Stopping
14612 || oldMachineState == MachineState_Saving
14613 || oldMachineState == MachineState_Restoring
14614 || oldMachineState == MachineState_TeleportingPausedVM
14615 || oldMachineState == MachineState_TeleportingIn
14616 )
14617 && ( aMachineState == MachineState_PoweredOff
14618 || aMachineState == MachineState_Saved
14619 || aMachineState == MachineState_Teleported
14620 || aMachineState == MachineState_Aborted
14621 )
14622 )
14623 {
14624 /* The EMT thread has just stopped, unlock attached media. Note that as
14625 * opposed to locking that is done from Console, we do unlocking here
14626 * because the VM process may have aborted before having a chance to
14627 * properly unlock all media it locked. */
14628
14629 unlockMedia();
14630 }
14631
14632 if (oldMachineState == MachineState_Restoring)
14633 {
14634 if (aMachineState != MachineState_Saved)
14635 {
14636 /*
14637 * delete the saved state file once the machine has finished
14638 * restoring from it (note that Console sets the state from
14639 * Restoring to Saved if the VM couldn't restore successfully,
14640 * to give the user an ability to fix an error and retry --
14641 * we keep the saved state file in this case)
14642 */
14643 deleteSavedState = true;
14644 }
14645 }
14646 else if ( oldMachineState == MachineState_Saved
14647 && ( aMachineState == MachineState_PoweredOff
14648 || aMachineState == MachineState_Aborted
14649 || aMachineState == MachineState_Teleported
14650 )
14651 )
14652 {
14653 /*
14654 * delete the saved state after SessionMachine::ForgetSavedState() is called
14655 * or if the VM process (owning a direct VM session) crashed while the
14656 * VM was Saved
14657 */
14658
14659 /// @todo (dmik)
14660 // Not sure that deleting the saved state file just because of the
14661 // client death before it attempted to restore the VM is a good
14662 // thing. But when it crashes we need to go to the Aborted state
14663 // which cannot have the saved state file associated... The only
14664 // way to fix this is to make the Aborted condition not a VM state
14665 // but a bool flag: i.e., when a crash occurs, set it to true and
14666 // change the state to PoweredOff or Saved depending on the
14667 // saved state presence.
14668
14669 deleteSavedState = true;
14670 mData->mCurrentStateModified = TRUE;
14671 stsFlags |= SaveSTS_CurStateModified;
14672 }
14673
14674 if ( aMachineState == MachineState_Starting
14675 || aMachineState == MachineState_Restoring
14676 || aMachineState == MachineState_TeleportingIn
14677 )
14678 {
14679 /* set the current state modified flag to indicate that the current
14680 * state is no more identical to the state in the
14681 * current snapshot */
14682 if (!mData->mCurrentSnapshot.isNull())
14683 {
14684 mData->mCurrentStateModified = TRUE;
14685 stsFlags |= SaveSTS_CurStateModified;
14686 }
14687 }
14688
14689 if (deleteSavedState)
14690 {
14691 if (mRemoveSavedState)
14692 {
14693 Assert(!mSSData->strStateFilePath.isEmpty());
14694
14695 // it is safe to delete the saved state file if ...
14696 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14697 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14698 // ... none of the snapshots share the saved state file
14699 )
14700 RTFileDelete(mSSData->strStateFilePath.c_str());
14701 }
14702
14703 mSSData->strStateFilePath.setNull();
14704 stsFlags |= SaveSTS_StateFilePath;
14705 }
14706
14707 /* redirect to the underlying peer machine */
14708 mPeer->i_setMachineState(aMachineState);
14709
14710 if ( oldMachineState != MachineState_RestoringSnapshot
14711 && ( aMachineState == MachineState_PoweredOff
14712 || aMachineState == MachineState_Teleported
14713 || aMachineState == MachineState_Aborted
14714 || aMachineState == MachineState_Saved))
14715 {
14716 /* the machine has stopped execution
14717 * (or the saved state file was adopted) */
14718 stsFlags |= SaveSTS_StateTimeStamp;
14719 }
14720
14721 if ( ( oldMachineState == MachineState_PoweredOff
14722 || oldMachineState == MachineState_Aborted
14723 || oldMachineState == MachineState_Teleported
14724 )
14725 && aMachineState == MachineState_Saved)
14726 {
14727 /* the saved state file was adopted */
14728 Assert(!mSSData->strStateFilePath.isEmpty());
14729 stsFlags |= SaveSTS_StateFilePath;
14730 }
14731
14732#ifdef VBOX_WITH_GUEST_PROPS
14733 if ( aMachineState == MachineState_PoweredOff
14734 || aMachineState == MachineState_Aborted
14735 || aMachineState == MachineState_Teleported)
14736 {
14737 /* Make sure any transient guest properties get removed from the
14738 * property store on shutdown. */
14739 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14740
14741 /* remove it from the settings representation */
14742 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14743 for (settings::GuestPropertiesList::iterator
14744 it = llGuestProperties.begin();
14745 it != llGuestProperties.end();
14746 /*nothing*/)
14747 {
14748 const settings::GuestProperty &prop = *it;
14749 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14750 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14751 {
14752 it = llGuestProperties.erase(it);
14753 fNeedsSaving = true;
14754 }
14755 else
14756 {
14757 ++it;
14758 }
14759 }
14760
14761 /* Additionally remove it from the HWData representation. Required to
14762 * keep everything in sync, as this is what the API keeps using. */
14763 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14764 for (HWData::GuestPropertyMap::iterator
14765 it = llHWGuestProperties.begin();
14766 it != llHWGuestProperties.end();
14767 /*nothing*/)
14768 {
14769 uint32_t fFlags = it->second.mFlags;
14770 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14771 {
14772 /* iterator where we need to continue after the erase call
14773 * (C++03 is a fact still, and it doesn't return the iterator
14774 * which would allow continuing) */
14775 HWData::GuestPropertyMap::iterator it2 = it;
14776 ++it2;
14777 llHWGuestProperties.erase(it);
14778 it = it2;
14779 fNeedsSaving = true;
14780 }
14781 else
14782 {
14783 ++it;
14784 }
14785 }
14786
14787 if (fNeedsSaving)
14788 {
14789 mData->mCurrentStateModified = TRUE;
14790 stsFlags |= SaveSTS_CurStateModified;
14791 }
14792 }
14793#endif /* VBOX_WITH_GUEST_PROPS */
14794
14795 rc = i_saveStateSettings(stsFlags);
14796
14797 if ( ( oldMachineState != MachineState_PoweredOff
14798 && oldMachineState != MachineState_Aborted
14799 && oldMachineState != MachineState_Teleported
14800 )
14801 && ( aMachineState == MachineState_PoweredOff
14802 || aMachineState == MachineState_Aborted
14803 || aMachineState == MachineState_Teleported
14804 )
14805 )
14806 {
14807 /* we've been shut down for any reason */
14808 /* no special action so far */
14809 }
14810
14811 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14812 LogFlowThisFuncLeave();
14813 return rc;
14814}
14815
14816/**
14817 * Sends the current machine state value to the VM process.
14818 *
14819 * @note Locks this object for reading, then calls a client process.
14820 */
14821HRESULT SessionMachine::i_updateMachineStateOnClient()
14822{
14823 AutoCaller autoCaller(this);
14824 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14825
14826 ComPtr<IInternalSessionControl> directControl;
14827 {
14828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14829 AssertReturn(!!mData, E_FAIL);
14830 if (mData->mSession.mLockType == LockType_VM)
14831 directControl = mData->mSession.mDirectControl;
14832
14833 /* directControl may be already set to NULL here in #OnSessionEnd()
14834 * called too early by the direct session process while there is still
14835 * some operation (like deleting the snapshot) in progress. The client
14836 * process in this case is waiting inside Session::close() for the
14837 * "end session" process object to complete, while #uninit() called by
14838 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14839 * operation to complete. For now, we accept this inconsistent behavior
14840 * and simply do nothing here. */
14841
14842 if (mData->mSession.mState == SessionState_Unlocking)
14843 return S_OK;
14844 }
14845
14846 /* ignore notifications sent after #OnSessionEnd() is called */
14847 if (!directControl)
14848 return S_OK;
14849
14850 return directControl->UpdateMachineState(mData->mMachineState);
14851}
14852
14853
14854/*static*/
14855HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14856{
14857 va_list args;
14858 va_start(args, pcszMsg);
14859 HRESULT rc = setErrorInternal(aResultCode,
14860 getStaticClassIID(),
14861 getStaticComponentName(),
14862 Utf8Str(pcszMsg, args),
14863 false /* aWarning */,
14864 true /* aLogIt */);
14865 va_end(args);
14866 return rc;
14867}
14868
14869
14870HRESULT Machine::updateState(MachineState_T aState)
14871{
14872 NOREF(aState);
14873 ReturnComNotImplemented();
14874}
14875
14876HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14877{
14878 NOREF(aProgress);
14879 ReturnComNotImplemented();
14880}
14881
14882HRESULT Machine::endPowerUp(LONG aResult)
14883{
14884 NOREF(aResult);
14885 ReturnComNotImplemented();
14886}
14887
14888HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14889{
14890 NOREF(aProgress);
14891 ReturnComNotImplemented();
14892}
14893
14894HRESULT Machine::endPoweringDown(LONG aResult,
14895 const com::Utf8Str &aErrMsg)
14896{
14897 NOREF(aResult);
14898 NOREF(aErrMsg);
14899 ReturnComNotImplemented();
14900}
14901
14902HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14903 BOOL *aMatched,
14904 ULONG *aMaskedInterfaces)
14905{
14906 NOREF(aDevice);
14907 NOREF(aMatched);
14908 NOREF(aMaskedInterfaces);
14909 ReturnComNotImplemented();
14910
14911}
14912
14913HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14914{
14915 NOREF(aId); NOREF(aCaptureFilename);
14916 ReturnComNotImplemented();
14917}
14918
14919HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14920 BOOL aDone)
14921{
14922 NOREF(aId);
14923 NOREF(aDone);
14924 ReturnComNotImplemented();
14925}
14926
14927HRESULT Machine::autoCaptureUSBDevices()
14928{
14929 ReturnComNotImplemented();
14930}
14931
14932HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14933{
14934 NOREF(aDone);
14935 ReturnComNotImplemented();
14936}
14937
14938HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14939 ComPtr<IProgress> &aProgress)
14940{
14941 NOREF(aSession);
14942 NOREF(aProgress);
14943 ReturnComNotImplemented();
14944}
14945
14946HRESULT Machine::finishOnlineMergeMedium()
14947{
14948 ReturnComNotImplemented();
14949}
14950
14951HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14952{
14953 RT_NOREF(aParms, aID);
14954 ReturnComNotImplemented();
14955}
14956
14957HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14958{
14959 RT_NOREF(aID);
14960 ReturnComNotImplemented();
14961}
14962
14963HRESULT Machine::clipboardAreaAttach(ULONG aID)
14964{
14965 RT_NOREF(aID);
14966 ReturnComNotImplemented();
14967}
14968HRESULT Machine::clipboardAreaDetach(ULONG aID)
14969{
14970 RT_NOREF(aID);
14971 ReturnComNotImplemented();
14972}
14973
14974HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
14975{
14976 RT_NOREF(aID);
14977 ReturnComNotImplemented();
14978}
14979
14980HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
14981{
14982 RT_NOREF(aID, aRefCount);
14983 ReturnComNotImplemented();
14984}
14985
14986HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14987 std::vector<com::Utf8Str> &aValues,
14988 std::vector<LONG64> &aTimestamps,
14989 std::vector<com::Utf8Str> &aFlags)
14990{
14991 NOREF(aNames);
14992 NOREF(aValues);
14993 NOREF(aTimestamps);
14994 NOREF(aFlags);
14995 ReturnComNotImplemented();
14996}
14997
14998HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14999 const com::Utf8Str &aValue,
15000 LONG64 aTimestamp,
15001 const com::Utf8Str &aFlags)
15002{
15003 NOREF(aName);
15004 NOREF(aValue);
15005 NOREF(aTimestamp);
15006 NOREF(aFlags);
15007 ReturnComNotImplemented();
15008}
15009
15010HRESULT Machine::lockMedia()
15011{
15012 ReturnComNotImplemented();
15013}
15014
15015HRESULT Machine::unlockMedia()
15016{
15017 ReturnComNotImplemented();
15018}
15019
15020HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15021 ComPtr<IMediumAttachment> &aNewAttachment)
15022{
15023 NOREF(aAttachment);
15024 NOREF(aNewAttachment);
15025 ReturnComNotImplemented();
15026}
15027
15028HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15029 ULONG aCpuUser,
15030 ULONG aCpuKernel,
15031 ULONG aCpuIdle,
15032 ULONG aMemTotal,
15033 ULONG aMemFree,
15034 ULONG aMemBalloon,
15035 ULONG aMemShared,
15036 ULONG aMemCache,
15037 ULONG aPagedTotal,
15038 ULONG aMemAllocTotal,
15039 ULONG aMemFreeTotal,
15040 ULONG aMemBalloonTotal,
15041 ULONG aMemSharedTotal,
15042 ULONG aVmNetRx,
15043 ULONG aVmNetTx)
15044{
15045 NOREF(aValidStats);
15046 NOREF(aCpuUser);
15047 NOREF(aCpuKernel);
15048 NOREF(aCpuIdle);
15049 NOREF(aMemTotal);
15050 NOREF(aMemFree);
15051 NOREF(aMemBalloon);
15052 NOREF(aMemShared);
15053 NOREF(aMemCache);
15054 NOREF(aPagedTotal);
15055 NOREF(aMemAllocTotal);
15056 NOREF(aMemFreeTotal);
15057 NOREF(aMemBalloonTotal);
15058 NOREF(aMemSharedTotal);
15059 NOREF(aVmNetRx);
15060 NOREF(aVmNetTx);
15061 ReturnComNotImplemented();
15062}
15063
15064HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15065 com::Utf8Str &aResult)
15066{
15067 NOREF(aAuthParams);
15068 NOREF(aResult);
15069 ReturnComNotImplemented();
15070}
15071
15072com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15073{
15074 com::Utf8Str strControllerName = "Unknown";
15075 switch (aBusType)
15076 {
15077 case StorageBus_IDE:
15078 {
15079 strControllerName = "IDE";
15080 break;
15081 }
15082 case StorageBus_SATA:
15083 {
15084 strControllerName = "SATA";
15085 break;
15086 }
15087 case StorageBus_SCSI:
15088 {
15089 strControllerName = "SCSI";
15090 break;
15091 }
15092 case StorageBus_Floppy:
15093 {
15094 strControllerName = "Floppy";
15095 break;
15096 }
15097 case StorageBus_SAS:
15098 {
15099 strControllerName = "SAS";
15100 break;
15101 }
15102 case StorageBus_USB:
15103 {
15104 strControllerName = "USB";
15105 break;
15106 }
15107 default:
15108 break;
15109 }
15110 return strControllerName;
15111}
15112
15113HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15114{
15115 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15116
15117 AutoCaller autoCaller(this);
15118 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15119
15120 HRESULT rc = S_OK;
15121
15122 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15123 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15124 rc = getUSBDeviceFilters(usbDeviceFilters);
15125 if (FAILED(rc)) return rc;
15126
15127 NOREF(aFlags);
15128 com::Utf8Str osTypeId;
15129 ComObjPtr<GuestOSType> osType = NULL;
15130
15131 /* Get the guest os type as a string from the VB. */
15132 rc = getOSTypeId(osTypeId);
15133 if (FAILED(rc)) return rc;
15134
15135 /* Get the os type obj that coresponds, can be used to get
15136 * the defaults for this guest OS. */
15137 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15138 if (FAILED(rc)) return rc;
15139
15140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15141
15142 /* Let the OS type select 64-bit ness. */
15143 mHWData->mLongMode = osType->i_is64Bit()
15144 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15145
15146 /* Let the OS type enable the X2APIC */
15147 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15148
15149 /* This one covers IOAPICEnabled. */
15150 mBIOSSettings->i_applyDefaults(osType);
15151
15152 /* Initialize default record settings. */
15153 mRecordingSettings->i_applyDefaults();
15154
15155 /* Initialize default BIOS settings here */
15156 /* Hardware virtualization must be ON by default */
15157 mHWData->mAPIC = true;
15158 mHWData->mHWVirtExEnabled = true;
15159
15160 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15161 if (FAILED(rc)) return rc;
15162
15163 /* Graphics stuff. */
15164 GraphicsControllerType_T graphicsController;
15165 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15166 if (FAILED(rc)) return rc;
15167
15168 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15169 if (FAILED(rc)) return rc;
15170
15171 ULONG vramSize;
15172 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15173 if (FAILED(rc)) return rc;
15174
15175 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15176 if (FAILED(rc)) return rc;
15177
15178 BOOL fAccelerate2DVideoEnabled;
15179 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15180 if (FAILED(rc)) return rc;
15181
15182 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15183 if (FAILED(rc)) return rc;
15184
15185 BOOL fAccelerate3DEnabled;
15186 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15187 if (FAILED(rc)) return rc;
15188
15189 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15190 if (FAILED(rc)) return rc;
15191
15192 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15193 if (FAILED(rc)) return rc;
15194
15195 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15196 if (FAILED(rc)) return rc;
15197
15198 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15199 if (FAILED(rc)) return rc;
15200
15201 BOOL mRTCUseUTC;
15202 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15203 if (FAILED(rc)) return rc;
15204
15205 setRTCUseUTC(mRTCUseUTC);
15206 if (FAILED(rc)) return rc;
15207
15208 /* the setter does more than just the assignment, so use it */
15209 ChipsetType_T enmChipsetType;
15210 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15211 if (FAILED(rc)) return rc;
15212
15213 rc = COMSETTER(ChipsetType)(enmChipsetType);
15214 if (FAILED(rc)) return rc;
15215
15216 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15217 if (FAILED(rc)) return rc;
15218
15219 /* Apply network adapters defaults */
15220 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15221 mNetworkAdapters[slot]->i_applyDefaults(osType);
15222
15223 /* Apply serial port defaults */
15224 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15225 mSerialPorts[slot]->i_applyDefaults(osType);
15226
15227 /* Apply parallel port defaults - not OS dependent*/
15228 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15229 mParallelPorts[slot]->i_applyDefaults();
15230
15231 /* Audio stuff. */
15232 AudioControllerType_T audioController;
15233 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15237 if (FAILED(rc)) return rc;
15238
15239 AudioCodecType_T audioCodec;
15240 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15241 if (FAILED(rc)) return rc;
15242
15243 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15244 if (FAILED(rc)) return rc;
15245
15246 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15247 if (FAILED(rc)) return rc;
15248
15249 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15250 if (FAILED(rc)) return rc;
15251
15252 /* Storage Controllers */
15253 StorageControllerType_T hdStorageControllerType;
15254 StorageBus_T hdStorageBusType;
15255 StorageControllerType_T dvdStorageControllerType;
15256 StorageBus_T dvdStorageBusType;
15257 BOOL recommendedFloppy;
15258 ComPtr<IStorageController> floppyController;
15259 ComPtr<IStorageController> hdController;
15260 ComPtr<IStorageController> dvdController;
15261 Utf8Str strFloppyName, strDVDName, strHDName;
15262
15263 /* GUI auto generates controller names using bus type. Do the same*/
15264 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15265
15266 /* Floppy recommended? add one. */
15267 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15268 if (FAILED(rc)) return rc;
15269 if (recommendedFloppy)
15270 {
15271 rc = addStorageController(strFloppyName,
15272 StorageBus_Floppy,
15273 floppyController);
15274 if (FAILED(rc)) return rc;
15275 }
15276
15277 /* Setup one DVD storage controller. */
15278 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15279 if (FAILED(rc)) return rc;
15280
15281 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15282 if (FAILED(rc)) return rc;
15283
15284 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15285
15286 rc = addStorageController(strDVDName,
15287 dvdStorageBusType,
15288 dvdController);
15289 if (FAILED(rc)) return rc;
15290
15291 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15292 if (FAILED(rc)) return rc;
15293
15294 /* Setup one HDD storage controller. */
15295 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15296 if (FAILED(rc)) return rc;
15297
15298 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15299 if (FAILED(rc)) return rc;
15300
15301 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15302
15303 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15304 {
15305 rc = addStorageController(strHDName,
15306 hdStorageBusType,
15307 hdController);
15308 if (FAILED(rc)) return rc;
15309
15310 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15311 if (FAILED(rc)) return rc;
15312 }
15313 else
15314 {
15315 /* The HD controller is the same as DVD: */
15316 hdController = dvdController;
15317 }
15318
15319 /* Limit the AHCI port count if it's used because windows has trouble with
15320 * too many ports and other guest (OS X in particular) may take extra long
15321 * boot: */
15322
15323 // pParent = static_cast<Medium*>(aP)
15324 IStorageController *temp = hdController;
15325 ComObjPtr<StorageController> storageController;
15326 storageController = static_cast<StorageController *>(temp);
15327
15328 // tempHDController = aHDController;
15329 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15330 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15331 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15332 storageController->COMSETTER(PortCount)(1);
15333
15334 /* USB stuff */
15335
15336 bool ohciEnabled = false;
15337
15338 ComPtr<IUSBController> usbController;
15339 BOOL recommendedUSB3;
15340 BOOL recommendedUSB;
15341 BOOL usbProxyAvailable;
15342
15343 getUSBProxyAvailable(&usbProxyAvailable);
15344 if (FAILED(rc)) return rc;
15345
15346 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15347 if (FAILED(rc)) return rc;
15348 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15349 if (FAILED(rc)) return rc;
15350
15351 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15352 {
15353#ifdef VBOX_WITH_EXTPACK
15354 /* USB 3.0 is only available if the proper ExtPack is installed. */
15355 ExtPackManager *aManager = mParent->i_getExtPackManager();
15356 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15357 {
15358 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15359 if (FAILED(rc)) return rc;
15360
15361 /* xHci includes OHCI */
15362 ohciEnabled = true;
15363 }
15364#endif
15365 }
15366 if ( !ohciEnabled
15367 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15368 {
15369 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15370 if (FAILED(rc)) return rc;
15371 ohciEnabled = true;
15372
15373#ifdef VBOX_WITH_EXTPACK
15374 /* USB 2.0 is only available if the proper ExtPack is installed.
15375 * Note. Configuring EHCI here and providing messages about
15376 * the missing extpack isn't exactly clean, but it is a
15377 * necessary evil to patch over legacy compatability issues
15378 * introduced by the new distribution model. */
15379 ExtPackManager *manager = mParent->i_getExtPackManager();
15380 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15381 {
15382 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15383 if (FAILED(rc)) return rc;
15384 }
15385#endif
15386 }
15387
15388 /* Set recommended human interface device types: */
15389 BOOL recommendedUSBHID;
15390 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15391 if (FAILED(rc)) return rc;
15392
15393 if (recommendedUSBHID)
15394 {
15395 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15396 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15397 if (!ohciEnabled && !usbDeviceFilters.isNull())
15398 {
15399 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15400 if (FAILED(rc)) return rc;
15401 }
15402 }
15403
15404 BOOL recommendedUSBTablet;
15405 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15406 if (FAILED(rc)) return rc;
15407
15408 if (recommendedUSBTablet)
15409 {
15410 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15411 if (!ohciEnabled && !usbDeviceFilters.isNull())
15412 {
15413 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15414 if (FAILED(rc)) return rc;
15415 }
15416 }
15417 return S_OK;
15418}
15419
15420/* This isn't handled entirely by the wrapper generator yet. */
15421#ifdef VBOX_WITH_XPCOM
15422NS_DECL_CLASSINFO(SessionMachine)
15423NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15424
15425NS_DECL_CLASSINFO(SnapshotMachine)
15426NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15427#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