VirtualBox

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

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

Main: Enabled large pages by default on linux too. bugref:5324

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 540.9 KB
Line 
1/* $Id: MachineImpl.cpp 92421 2021-11-15 08:48:16Z 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 mHWVirtExLargePagesEnabled = HC_ARCH_BITS == 64; /* Not supported on 32 bits hosts. */
176 mHWVirtExVPIDEnabled = true;
177 mHWVirtExUXEnabled = true;
178 mHWVirtExForceEnabled = false;
179 mHWVirtExUseNativeApi = false;
180 mHWVirtExVirtVmsaveVmload = true;
181#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
182 mPAEEnabled = true;
183#else
184 mPAEEnabled = false;
185#endif
186 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
187 mTripleFaultReset = false;
188 mAPIC = true;
189 mX2APIC = false;
190 mIBPBOnVMExit = false;
191 mIBPBOnVMEntry = false;
192 mSpecCtrl = false;
193 mSpecCtrlByHost = false;
194 mL1DFlushOnSched = true;
195 mL1DFlushOnVMEntry = false;
196 mMDSClearOnSched = true;
197 mMDSClearOnVMEntry = false;
198 mNestedHWVirt = false;
199 mHPETEnabled = false;
200 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
201 mCpuIdPortabilityLevel = 0;
202 mCpuProfile = "host";
203
204 /* default boot order: floppy - DVD - HDD */
205 mBootOrder[0] = DeviceType_Floppy;
206 mBootOrder[1] = DeviceType_DVD;
207 mBootOrder[2] = DeviceType_HardDisk;
208 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
209 mBootOrder[i] = DeviceType_Null;
210
211 mClipboardMode = ClipboardMode_Disabled;
212 mClipboardFileTransfersEnabled = FALSE;
213
214 mDnDMode = DnDMode_Disabled;
215
216 mFirmwareType = FirmwareType_BIOS;
217 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
218 mPointingHIDType = PointingHIDType_PS2Mouse;
219 mChipsetType = ChipsetType_PIIX3;
220 mIommuType = IommuType_None;
221 mParavirtProvider = ParavirtProvider_Default;
222 mEmulatedUSBCardReaderEnabled = FALSE;
223
224 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
225 mCPUAttached[i] = false;
226
227 mIOCacheEnabled = true;
228 mIOCacheSize = 5; /* 5MB */
229}
230
231Machine::HWData::~HWData()
232{
233}
234
235/////////////////////////////////////////////////////////////////////////////
236// Machine class
237/////////////////////////////////////////////////////////////////////////////
238
239// constructor / destructor
240/////////////////////////////////////////////////////////////////////////////
241
242Machine::Machine() :
243#ifdef VBOX_WITH_RESOURCE_USAGE_API
244 mCollectorGuest(NULL),
245#endif
246 mPeer(NULL),
247 mParent(NULL),
248 mSerialPorts(),
249 mParallelPorts(),
250 uRegistryNeedsSaving(0)
251{}
252
253Machine::~Machine()
254{}
255
256HRESULT Machine::FinalConstruct()
257{
258 LogFlowThisFunc(("\n"));
259 return BaseFinalConstruct();
260}
261
262void Machine::FinalRelease()
263{
264 LogFlowThisFunc(("\n"));
265 uninit();
266 BaseFinalRelease();
267}
268
269/**
270 * Initializes a new machine instance; this init() variant creates a new, empty machine.
271 * This gets called from VirtualBox::CreateMachine().
272 *
273 * @param aParent Associated parent object
274 * @param strConfigFile Local file system path to the VM settings file (can
275 * be relative to the VirtualBox config directory).
276 * @param strName name for the machine
277 * @param llGroups list of groups for the machine
278 * @param strOsType OS Type string (stored as is if aOsType is NULL).
279 * @param aOsType OS Type of this machine or NULL.
280 * @param aId UUID for the new machine.
281 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
282 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
283 * scheme (includes the UUID).
284 *
285 * @return Success indicator. if not S_OK, the machine object is invalid
286 */
287HRESULT Machine::init(VirtualBox *aParent,
288 const Utf8Str &strConfigFile,
289 const Utf8Str &strName,
290 const StringsList &llGroups,
291 const Utf8Str &strOsType,
292 GuestOSType *aOsType,
293 const Guid &aId,
294 bool fForceOverwrite,
295 bool fDirectoryIncludesUUID)
296{
297 LogFlowThisFuncEnter();
298 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
299
300 /* Enclose the state transition NotReady->InInit->Ready */
301 AutoInitSpan autoInitSpan(this);
302 AssertReturn(autoInitSpan.isOk(), E_FAIL);
303
304 HRESULT rc = initImpl(aParent, strConfigFile);
305 if (FAILED(rc)) return rc;
306
307 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
308 if (FAILED(rc)) return rc;
309
310 if (SUCCEEDED(rc))
311 {
312 // create an empty machine config
313 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
314
315 rc = initDataAndChildObjects();
316 }
317
318 if (SUCCEEDED(rc))
319 {
320 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
321 mData->mAccessible = TRUE;
322
323 unconst(mData->mUuid) = aId;
324
325 mUserData->s.strName = strName;
326
327 if (llGroups.size())
328 mUserData->s.llGroups = llGroups;
329
330 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
331 // the "name sync" flag determines whether the machine directory gets renamed along
332 // with the machine file; say so if the settings file name is the same as the
333 // settings file parent directory (machine directory)
334 mUserData->s.fNameSync = i_isInOwnDir();
335
336 // initialize the default snapshots folder
337 rc = COMSETTER(SnapshotFolder)(NULL);
338 AssertComRC(rc);
339
340 if (aOsType)
341 {
342 /* Store OS type */
343 mUserData->s.strOsType = aOsType->i_id();
344
345 /* Let the OS type select 64-bit ness. */
346 mHWData->mLongMode = aOsType->i_is64Bit()
347 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
348
349 /* Let the OS type enable the X2APIC */
350 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
351
352 rc = aOsType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
353 AssertComRC(rc);
354 }
355 else if (!strOsType.isEmpty())
356 {
357 /* Store OS type */
358 mUserData->s.strOsType = strOsType;
359
360 /* No guest OS type object. Pick some plausible defaults which the
361 * host can handle. There's no way to know or validate anything. */
362 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363 mHWData->mX2APIC = false;
364 }
365
366 /* Apply BIOS defaults. */
367 mBIOSSettings->i_applyDefaults(aOsType);
368
369 /* Apply TPM defaults. */
370 mTrustedPlatformModule->i_applyDefaults(aOsType);
371
372 /* Apply record defaults. */
373 mRecordingSettings->i_applyDefaults();
374
375 /* Apply network adapters defaults */
376 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
377 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
378
379 /* Apply serial port defaults */
380 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
381 mSerialPorts[slot]->i_applyDefaults(aOsType);
382
383 /* Apply parallel port defaults */
384 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
385 mParallelPorts[slot]->i_applyDefaults();
386
387 /* At this point the changing of the current state modification
388 * flag is allowed. */
389 i_allowStateModification();
390
391 /* commit all changes made during the initialization */
392 i_commit();
393 }
394
395 /* Confirm a successful initialization when it's the case */
396 if (SUCCEEDED(rc))
397 {
398 if (mData->mAccessible)
399 autoInitSpan.setSucceeded();
400 else
401 autoInitSpan.setLimited();
402 }
403
404 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
405 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
406 mData->mRegistered,
407 mData->mAccessible,
408 rc));
409
410 LogFlowThisFuncLeave();
411
412 return rc;
413}
414
415/**
416 * Initializes a new instance with data from machine XML (formerly Init_Registered).
417 * Gets called in two modes:
418 *
419 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
420 * UUID is specified and we mark the machine as "registered";
421 *
422 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
423 * and the machine remains unregistered until RegisterMachine() is called.
424 *
425 * @param aParent Associated parent object
426 * @param strConfigFile Local file system path to the VM settings file (can
427 * be relative to the VirtualBox config directory).
428 * @param aId UUID of the machine or NULL (see above).
429 *
430 * @return Success indicator. if not S_OK, the machine object is invalid
431 */
432HRESULT Machine::initFromSettings(VirtualBox *aParent,
433 const Utf8Str &strConfigFile,
434 const Guid *aId)
435{
436 LogFlowThisFuncEnter();
437 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
438
439 /* Enclose the state transition NotReady->InInit->Ready */
440 AutoInitSpan autoInitSpan(this);
441 AssertReturn(autoInitSpan.isOk(), E_FAIL);
442
443 HRESULT rc = initImpl(aParent, strConfigFile);
444 if (FAILED(rc)) return rc;
445
446 if (aId)
447 {
448 // loading a registered VM:
449 unconst(mData->mUuid) = *aId;
450 mData->mRegistered = TRUE;
451 // now load the settings from XML:
452 rc = i_registeredInit();
453 // this calls initDataAndChildObjects() and loadSettings()
454 }
455 else
456 {
457 // opening an unregistered VM (VirtualBox::OpenMachine()):
458 rc = initDataAndChildObjects();
459
460 if (SUCCEEDED(rc))
461 {
462 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
463 mData->mAccessible = TRUE;
464
465 try
466 {
467 // load and parse machine XML; this will throw on XML or logic errors
468 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
469
470 // reject VM UUID duplicates, they can happen if someone
471 // tries to register an already known VM config again
472 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
473 true /* fPermitInaccessible */,
474 false /* aDoSetError */,
475 NULL) != VBOX_E_OBJECT_NOT_FOUND)
476 {
477 throw setError(E_FAIL,
478 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
479 mData->m_strConfigFile.c_str());
480 }
481
482 // use UUID from machine config
483 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
484
485 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
486 NULL /* puuidRegistry */);
487 if (FAILED(rc)) throw rc;
488
489 /* At this point the changing of the current state modification
490 * flag is allowed. */
491 i_allowStateModification();
492
493 i_commit();
494 }
495 catch (HRESULT err)
496 {
497 /* we assume that error info is set by the thrower */
498 rc = err;
499 }
500 catch (...)
501 {
502 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
503 }
504 }
505 }
506
507 /* Confirm a successful initialization when it's the case */
508 if (SUCCEEDED(rc))
509 {
510 if (mData->mAccessible)
511 autoInitSpan.setSucceeded();
512 else
513 {
514 autoInitSpan.setLimited();
515
516 // uninit media from this machine's media registry, or else
517 // reloading the settings will fail
518 mParent->i_unregisterMachineMedia(i_getId());
519 }
520 }
521
522 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
523 "rc=%08X\n",
524 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
525 mData->mRegistered, mData->mAccessible, rc));
526
527 LogFlowThisFuncLeave();
528
529 return rc;
530}
531
532/**
533 * Initializes a new instance from a machine config that is already in memory
534 * (import OVF case). Since we are importing, the UUID in the machine
535 * config is ignored and we always generate a fresh one.
536 *
537 * @param aParent Associated parent object.
538 * @param strName Name for the new machine; this overrides what is specified in config.
539 * @param strSettingsFilename File name of .vbox file.
540 * @param config Machine configuration loaded and parsed from XML.
541 *
542 * @return Success indicator. if not S_OK, the machine object is invalid
543 */
544HRESULT Machine::init(VirtualBox *aParent,
545 const Utf8Str &strName,
546 const Utf8Str &strSettingsFilename,
547 const settings::MachineConfigFile &config)
548{
549 LogFlowThisFuncEnter();
550
551 /* Enclose the state transition NotReady->InInit->Ready */
552 AutoInitSpan autoInitSpan(this);
553 AssertReturn(autoInitSpan.isOk(), E_FAIL);
554
555 HRESULT rc = initImpl(aParent, strSettingsFilename);
556 if (FAILED(rc)) return rc;
557
558 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
559 if (FAILED(rc)) return rc;
560
561 rc = initDataAndChildObjects();
562
563 if (SUCCEEDED(rc))
564 {
565 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
566 mData->mAccessible = TRUE;
567
568 // create empty machine config for instance data
569 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
570
571 // generate fresh UUID, ignore machine config
572 unconst(mData->mUuid).create();
573
574 rc = i_loadMachineDataFromSettings(config,
575 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
576
577 // override VM name as well, it may be different
578 mUserData->s.strName = strName;
579
580 if (SUCCEEDED(rc))
581 {
582 /* At this point the changing of the current state modification
583 * flag is allowed. */
584 i_allowStateModification();
585
586 /* commit all changes made during the initialization */
587 i_commit();
588 }
589 }
590
591 /* Confirm a successful initialization when it's the case */
592 if (SUCCEEDED(rc))
593 {
594 if (mData->mAccessible)
595 autoInitSpan.setSucceeded();
596 else
597 {
598 /* Ignore all errors from unregistering, they would destroy
599- * the more interesting error information we already have,
600- * pinpointing the issue with the VM config. */
601 ErrorInfoKeeper eik;
602
603 autoInitSpan.setLimited();
604
605 // uninit media from this machine's media registry, or else
606 // reloading the settings will fail
607 mParent->i_unregisterMachineMedia(i_getId());
608 }
609 }
610
611 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
612 "rc=%08X\n",
613 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
614 mData->mRegistered, mData->mAccessible, rc));
615
616 LogFlowThisFuncLeave();
617
618 return rc;
619}
620
621/**
622 * Shared code between the various init() implementations.
623 * @param aParent The VirtualBox object.
624 * @param strConfigFile Settings file.
625 * @return
626 */
627HRESULT Machine::initImpl(VirtualBox *aParent,
628 const Utf8Str &strConfigFile)
629{
630 LogFlowThisFuncEnter();
631
632 AssertReturn(aParent, E_INVALIDARG);
633 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
634
635 HRESULT rc = S_OK;
636
637 /* share the parent weakly */
638 unconst(mParent) = aParent;
639
640 /* allocate the essential machine data structure (the rest will be
641 * allocated later by initDataAndChildObjects() */
642 mData.allocate();
643
644 /* memorize the config file name (as provided) */
645 mData->m_strConfigFile = strConfigFile;
646
647 /* get the full file name */
648 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
649 if (RT_FAILURE(vrc1))
650 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
651 tr("Invalid machine settings file name '%s' (%Rrc)"),
652 strConfigFile.c_str(),
653 vrc1);
654
655 LogFlowThisFuncLeave();
656
657 return rc;
658}
659
660/**
661 * Tries to create a machine settings file in the path stored in the machine
662 * instance data. Used when a new machine is created to fail gracefully if
663 * the settings file could not be written (e.g. because machine dir is read-only).
664 * @return
665 */
666HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
667{
668 HRESULT rc = S_OK;
669
670 // when we create a new machine, we must be able to create the settings file
671 RTFILE f = NIL_RTFILE;
672 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
673 if ( RT_SUCCESS(vrc)
674 || vrc == VERR_SHARING_VIOLATION
675 )
676 {
677 if (RT_SUCCESS(vrc))
678 RTFileClose(f);
679 if (!fForceOverwrite)
680 rc = setError(VBOX_E_FILE_ERROR,
681 tr("Machine settings file '%s' already exists"),
682 mData->m_strConfigFileFull.c_str());
683 else
684 {
685 /* try to delete the config file, as otherwise the creation
686 * of a new settings file will fail. */
687 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
688 if (RT_FAILURE(vrc2))
689 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
690 tr("Could not delete the existing settings file '%s' (%Rrc)"),
691 mData->m_strConfigFileFull.c_str(), vrc2);
692 }
693 }
694 else if ( vrc != VERR_FILE_NOT_FOUND
695 && vrc != VERR_PATH_NOT_FOUND
696 )
697 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
698 tr("Invalid machine settings file name '%s' (%Rrc)"),
699 mData->m_strConfigFileFull.c_str(),
700 vrc);
701 return rc;
702}
703
704/**
705 * Initializes the registered machine by loading the settings file.
706 * This method is separated from #init() in order to make it possible to
707 * retry the operation after VirtualBox startup instead of refusing to
708 * startup the whole VirtualBox server in case if the settings file of some
709 * registered VM is invalid or inaccessible.
710 *
711 * @note Must be always called from this object's write lock
712 * (unless called from #init() that doesn't need any locking).
713 * @note Locks the mUSBController method for writing.
714 * @note Subclasses must not call this method.
715 */
716HRESULT Machine::i_registeredInit()
717{
718 AssertReturn(!i_isSessionMachine(), E_FAIL);
719 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
720 AssertReturn(mData->mUuid.isValid(), E_FAIL);
721 AssertReturn(!mData->mAccessible, E_FAIL);
722
723 HRESULT rc = initDataAndChildObjects();
724
725 if (SUCCEEDED(rc))
726 {
727 /* Temporarily reset the registered flag in order to let setters
728 * potentially called from loadSettings() succeed (isMutable() used in
729 * all setters will return FALSE for a Machine instance if mRegistered
730 * is TRUE). */
731 mData->mRegistered = FALSE;
732
733 try
734 {
735 // load and parse machine XML; this will throw on XML or logic errors
736 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
737
738 if (mData->mUuid != mData->pMachineConfigFile->uuid)
739 throw setError(E_FAIL,
740 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
741 mData->pMachineConfigFile->uuid.raw(),
742 mData->m_strConfigFileFull.c_str(),
743 mData->mUuid.toString().c_str(),
744 mParent->i_settingsFilePath().c_str());
745
746 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
747 NULL /* const Guid *puuidRegistry */);
748 if (FAILED(rc)) throw rc;
749 }
750 catch (HRESULT err)
751 {
752 /* we assume that error info is set by the thrower */
753 rc = err;
754 }
755 catch (...)
756 {
757 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
758 }
759
760 /* Restore the registered flag (even on failure) */
761 mData->mRegistered = TRUE;
762 }
763
764 if (SUCCEEDED(rc))
765 {
766 /* Set mAccessible to TRUE only if we successfully locked and loaded
767 * the settings file */
768 mData->mAccessible = TRUE;
769
770 /* commit all changes made during loading the settings file */
771 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
772 /// @todo r=klaus for some reason the settings loading logic backs up
773 // the settings, and therefore a commit is needed. Should probably be changed.
774 }
775 else
776 {
777 /* If the machine is registered, then, instead of returning a
778 * failure, we mark it as inaccessible and set the result to
779 * success to give it a try later */
780
781 /* fetch the current error info */
782 mData->mAccessError = com::ErrorInfo();
783 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
784
785 /* rollback all changes */
786 i_rollback(false /* aNotify */);
787
788 // uninit media from this machine's media registry, or else
789 // reloading the settings will fail
790 mParent->i_unregisterMachineMedia(i_getId());
791
792 /* uninitialize the common part to make sure all data is reset to
793 * default (null) values */
794 uninitDataAndChildObjects();
795
796 rc = S_OK;
797 }
798
799 return rc;
800}
801
802/**
803 * Uninitializes the instance.
804 * Called either from FinalRelease() or by the parent when it gets destroyed.
805 *
806 * @note The caller of this method must make sure that this object
807 * a) doesn't have active callers on the current thread and b) is not locked
808 * by the current thread; otherwise uninit() will hang either a) due to
809 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
810 * a dead-lock caused by this thread waiting for all callers on the other
811 * threads are done but preventing them from doing so by holding a lock.
812 */
813void Machine::uninit()
814{
815 LogFlowThisFuncEnter();
816
817 Assert(!isWriteLockOnCurrentThread());
818
819 Assert(!uRegistryNeedsSaving);
820 if (uRegistryNeedsSaving)
821 {
822 AutoCaller autoCaller(this);
823 if (SUCCEEDED(autoCaller.rc()))
824 {
825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
826 i_saveSettings(NULL, alock, Machine::SaveS_Force);
827 }
828 }
829
830 /* Enclose the state transition Ready->InUninit->NotReady */
831 AutoUninitSpan autoUninitSpan(this);
832 if (autoUninitSpan.uninitDone())
833 return;
834
835 Assert(!i_isSnapshotMachine());
836 Assert(!i_isSessionMachine());
837 Assert(!!mData);
838
839 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
840 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
841
842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
843
844 if (!mData->mSession.mMachine.isNull())
845 {
846 /* Theoretically, this can only happen if the VirtualBox server has been
847 * terminated while there were clients running that owned open direct
848 * sessions. Since in this case we are definitely called by
849 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
850 * won't happen on the client watcher thread (because it has a
851 * VirtualBox caller for the duration of the
852 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
853 * cannot happen until the VirtualBox caller is released). This is
854 * important, because SessionMachine::uninit() cannot correctly operate
855 * after we return from this method (it expects the Machine instance is
856 * still valid). We'll call it ourselves below.
857 */
858 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
859 (SessionMachine*)mData->mSession.mMachine));
860
861 if (Global::IsOnlineOrTransient(mData->mMachineState))
862 {
863 Log1WarningThisFunc(("Setting state to Aborted!\n"));
864 /* set machine state using SessionMachine reimplementation */
865 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
866 }
867
868 /*
869 * Uninitialize SessionMachine using public uninit() to indicate
870 * an unexpected uninitialization.
871 */
872 mData->mSession.mMachine->uninit();
873 /* SessionMachine::uninit() must set mSession.mMachine to null */
874 Assert(mData->mSession.mMachine.isNull());
875 }
876
877 // uninit media from this machine's media registry, if they're still there
878 Guid uuidMachine(i_getId());
879
880 /* the lock is no more necessary (SessionMachine is uninitialized) */
881 alock.release();
882
883 /* XXX This will fail with
884 * "cannot be closed because it is still attached to 1 virtual machines"
885 * because at this point we did not call uninitDataAndChildObjects() yet
886 * and therefore also removeBackReference() for all these mediums was not called! */
887
888 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
889 mParent->i_unregisterMachineMedia(uuidMachine);
890
891 // has machine been modified?
892 if (mData->flModifications)
893 {
894 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
895 i_rollback(false /* aNotify */);
896 }
897
898 if (mData->mAccessible)
899 uninitDataAndChildObjects();
900
901 /* free the essential data structure last */
902 mData.free();
903
904 LogFlowThisFuncLeave();
905}
906
907// Wrapped IMachine properties
908/////////////////////////////////////////////////////////////////////////////
909HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
910{
911 /* mParent is constant during life time, no need to lock */
912 ComObjPtr<VirtualBox> pVirtualBox(mParent);
913 aParent = pVirtualBox;
914
915 return S_OK;
916}
917
918
919HRESULT Machine::getAccessible(BOOL *aAccessible)
920{
921 /* In some cases (medium registry related), it is necessary to be able to
922 * go through the list of all machines. Happens when an inaccessible VM
923 * has a sensible medium registry. */
924 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
926
927 HRESULT rc = S_OK;
928
929 if (!mData->mAccessible)
930 {
931 /* try to initialize the VM once more if not accessible */
932
933 AutoReinitSpan autoReinitSpan(this);
934 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
935
936#ifdef DEBUG
937 LogFlowThisFunc(("Dumping media backreferences\n"));
938 mParent->i_dumpAllBackRefs();
939#endif
940
941 if (mData->pMachineConfigFile)
942 {
943 // reset the XML file to force loadSettings() (called from i_registeredInit())
944 // to parse it again; the file might have changed
945 delete mData->pMachineConfigFile;
946 mData->pMachineConfigFile = NULL;
947 }
948
949 rc = i_registeredInit();
950
951 if (SUCCEEDED(rc) && mData->mAccessible)
952 {
953 autoReinitSpan.setSucceeded();
954
955 /* make sure interesting parties will notice the accessibility
956 * state change */
957 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
958 mParent->i_onMachineDataChanged(mData->mUuid);
959 }
960 }
961
962 if (SUCCEEDED(rc))
963 *aAccessible = mData->mAccessible;
964
965 LogFlowThisFuncLeave();
966
967 return rc;
968}
969
970HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
971{
972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
973
974 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
975 {
976 /* return shortly */
977 aAccessError = NULL;
978 return S_OK;
979 }
980
981 HRESULT rc = S_OK;
982
983 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
984 rc = errorInfo.createObject();
985 if (SUCCEEDED(rc))
986 {
987 errorInfo->init(mData->mAccessError.getResultCode(),
988 mData->mAccessError.getInterfaceID().ref(),
989 Utf8Str(mData->mAccessError.getComponent()).c_str(),
990 Utf8Str(mData->mAccessError.getText()));
991 aAccessError = errorInfo;
992 }
993
994 return rc;
995}
996
997HRESULT Machine::getName(com::Utf8Str &aName)
998{
999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 aName = mUserData->s.strName;
1002
1003 return S_OK;
1004}
1005
1006HRESULT Machine::setName(const com::Utf8Str &aName)
1007{
1008 // prohibit setting a UUID only as the machine name, or else it can
1009 // never be found by findMachine()
1010 Guid test(aName);
1011
1012 if (test.isValid())
1013 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1014
1015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1016
1017 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1018 if (FAILED(rc)) return rc;
1019
1020 i_setModified(IsModified_MachineData);
1021 mUserData.backup();
1022 mUserData->s.strName = aName;
1023
1024 return S_OK;
1025}
1026
1027HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1028{
1029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 aDescription = mUserData->s.strDescription;
1032
1033 return S_OK;
1034}
1035
1036HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1037{
1038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 // this can be done in principle in any state as it doesn't affect the VM
1041 // significantly, but play safe by not messing around while complex
1042 // activities are going on
1043 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1044 if (FAILED(rc)) return rc;
1045
1046 i_setModified(IsModified_MachineData);
1047 mUserData.backup();
1048 mUserData->s.strDescription = aDescription;
1049
1050 return S_OK;
1051}
1052
1053HRESULT Machine::getId(com::Guid &aId)
1054{
1055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1056
1057 aId = mData->mUuid;
1058
1059 return S_OK;
1060}
1061
1062HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1063{
1064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1065 aGroups.resize(mUserData->s.llGroups.size());
1066 size_t i = 0;
1067 for (StringsList::const_iterator
1068 it = mUserData->s.llGroups.begin();
1069 it != mUserData->s.llGroups.end();
1070 ++it, ++i)
1071 aGroups[i] = (*it);
1072
1073 return S_OK;
1074}
1075
1076HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1077{
1078 StringsList llGroups;
1079 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1080 if (FAILED(rc))
1081 return rc;
1082
1083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1084
1085 rc = i_checkStateDependency(MutableOrSavedStateDep);
1086 if (FAILED(rc)) return rc;
1087
1088 i_setModified(IsModified_MachineData);
1089 mUserData.backup();
1090 mUserData->s.llGroups = llGroups;
1091
1092 return S_OK;
1093}
1094
1095HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1096{
1097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 aOSTypeId = mUserData->s.strOsType;
1100
1101 return S_OK;
1102}
1103
1104HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1105{
1106 /* look up the object by Id to check it is valid */
1107 ComObjPtr<GuestOSType> pGuestOSType;
1108 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1109
1110 /* when setting, always use the "etalon" value for consistency -- lookup
1111 * by ID is case-insensitive and the input value may have different case */
1112 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1113
1114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1115
1116 HRESULT rc = i_checkStateDependency(MutableStateDep);
1117 if (FAILED(rc)) return rc;
1118
1119 i_setModified(IsModified_MachineData);
1120 mUserData.backup();
1121 mUserData->s.strOsType = osTypeId;
1122
1123 return S_OK;
1124}
1125
1126HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1127{
1128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1129
1130 *aFirmwareType = mHWData->mFirmwareType;
1131
1132 return S_OK;
1133}
1134
1135HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1136{
1137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1138
1139 HRESULT rc = i_checkStateDependency(MutableStateDep);
1140 if (FAILED(rc)) return rc;
1141
1142 i_setModified(IsModified_MachineData);
1143 mHWData.backup();
1144 mHWData->mFirmwareType = aFirmwareType;
1145 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1146 alock.release();
1147
1148 mNvramStore->i_updateNonVolatileStorageFile(strNVRAM);
1149
1150 return S_OK;
1151}
1152
1153HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1154{
1155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1156
1157 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1158
1159 return S_OK;
1160}
1161
1162HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1163{
1164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1165
1166 HRESULT rc = i_checkStateDependency(MutableStateDep);
1167 if (FAILED(rc)) return rc;
1168
1169 i_setModified(IsModified_MachineData);
1170 mHWData.backup();
1171 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1172
1173 return S_OK;
1174}
1175
1176HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1177{
1178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 *aPointingHIDType = mHWData->mPointingHIDType;
1181
1182 return S_OK;
1183}
1184
1185HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1186{
1187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1188
1189 HRESULT rc = i_checkStateDependency(MutableStateDep);
1190 if (FAILED(rc)) return rc;
1191
1192 i_setModified(IsModified_MachineData);
1193 mHWData.backup();
1194 mHWData->mPointingHIDType = aPointingHIDType;
1195
1196 return S_OK;
1197}
1198
1199HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1200{
1201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 *aChipsetType = mHWData->mChipsetType;
1204
1205 return S_OK;
1206}
1207
1208HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1209{
1210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1211
1212 HRESULT rc = i_checkStateDependency(MutableStateDep);
1213 if (FAILED(rc)) return rc;
1214
1215 if (aChipsetType != mHWData->mChipsetType)
1216 {
1217 i_setModified(IsModified_MachineData);
1218 mHWData.backup();
1219 mHWData->mChipsetType = aChipsetType;
1220
1221 // Resize network adapter array, to be finalized on commit/rollback.
1222 // We must not throw away entries yet, otherwise settings are lost
1223 // without a way to roll back.
1224 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1225 size_t oldCount = mNetworkAdapters.size();
1226 if (newCount > oldCount)
1227 {
1228 mNetworkAdapters.resize(newCount);
1229 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1230 {
1231 unconst(mNetworkAdapters[slot]).createObject();
1232 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1233 }
1234 }
1235 }
1236
1237 return S_OK;
1238}
1239
1240HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1241{
1242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1243
1244 *aIommuType = mHWData->mIommuType;
1245
1246 return S_OK;
1247}
1248
1249HRESULT Machine::setIommuType(IommuType_T aIommuType)
1250{
1251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1252
1253 HRESULT rc = i_checkStateDependency(MutableStateDep);
1254 if (FAILED(rc)) return rc;
1255
1256 if (aIommuType != mHWData->mIommuType)
1257 {
1258 if (aIommuType == IommuType_Intel)
1259 {
1260#ifndef VBOX_WITH_IOMMU_INTEL
1261 LogRelFunc(("Setting Intel IOMMU when Intel IOMMU support not available!\n"));
1262 return E_UNEXPECTED;
1263#endif
1264 }
1265
1266 i_setModified(IsModified_MachineData);
1267 mHWData.backup();
1268 mHWData->mIommuType = aIommuType;
1269 }
1270
1271 return S_OK;
1272}
1273
1274HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1275{
1276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1277
1278 aParavirtDebug = mHWData->mParavirtDebug;
1279 return S_OK;
1280}
1281
1282HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1283{
1284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1285
1286 HRESULT rc = i_checkStateDependency(MutableStateDep);
1287 if (FAILED(rc)) return rc;
1288
1289 /** @todo Parse/validate options? */
1290 if (aParavirtDebug != mHWData->mParavirtDebug)
1291 {
1292 i_setModified(IsModified_MachineData);
1293 mHWData.backup();
1294 mHWData->mParavirtDebug = aParavirtDebug;
1295 }
1296
1297 return S_OK;
1298}
1299
1300HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1301{
1302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1303
1304 *aParavirtProvider = mHWData->mParavirtProvider;
1305
1306 return S_OK;
1307}
1308
1309HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1310{
1311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1312
1313 HRESULT rc = i_checkStateDependency(MutableStateDep);
1314 if (FAILED(rc)) return rc;
1315
1316 if (aParavirtProvider != mHWData->mParavirtProvider)
1317 {
1318 i_setModified(IsModified_MachineData);
1319 mHWData.backup();
1320 mHWData->mParavirtProvider = aParavirtProvider;
1321 }
1322
1323 return S_OK;
1324}
1325
1326HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1327{
1328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1329
1330 *aParavirtProvider = mHWData->mParavirtProvider;
1331 switch (mHWData->mParavirtProvider)
1332 {
1333 case ParavirtProvider_None:
1334 case ParavirtProvider_HyperV:
1335 case ParavirtProvider_KVM:
1336 case ParavirtProvider_Minimal:
1337 break;
1338
1339 /* Resolve dynamic provider types to the effective types. */
1340 default:
1341 {
1342 ComObjPtr<GuestOSType> pGuestOSType;
1343 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1344 pGuestOSType);
1345 if (FAILED(hrc2) || pGuestOSType.isNull())
1346 {
1347 *aParavirtProvider = ParavirtProvider_None;
1348 break;
1349 }
1350
1351 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1352 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1353
1354 switch (mHWData->mParavirtProvider)
1355 {
1356 case ParavirtProvider_Legacy:
1357 {
1358 if (fOsXGuest)
1359 *aParavirtProvider = ParavirtProvider_Minimal;
1360 else
1361 *aParavirtProvider = ParavirtProvider_None;
1362 break;
1363 }
1364
1365 case ParavirtProvider_Default:
1366 {
1367 if (fOsXGuest)
1368 *aParavirtProvider = ParavirtProvider_Minimal;
1369 else if ( mUserData->s.strOsType == "Windows10"
1370 || mUserData->s.strOsType == "Windows10_64"
1371 || mUserData->s.strOsType == "Windows81"
1372 || mUserData->s.strOsType == "Windows81_64"
1373 || mUserData->s.strOsType == "Windows8"
1374 || mUserData->s.strOsType == "Windows8_64"
1375 || mUserData->s.strOsType == "Windows7"
1376 || mUserData->s.strOsType == "Windows7_64"
1377 || mUserData->s.strOsType == "WindowsVista"
1378 || mUserData->s.strOsType == "WindowsVista_64"
1379 || ( ( mUserData->s.strOsType.startsWith("Windows202")
1380 || mUserData->s.strOsType.startsWith("Windows201"))
1381 && mUserData->s.strOsType.endsWith("_64"))
1382 || mUserData->s.strOsType == "Windows2012"
1383 || mUserData->s.strOsType == "Windows2012_64"
1384 || mUserData->s.strOsType == "Windows2008"
1385 || mUserData->s.strOsType == "Windows2008_64")
1386 {
1387 *aParavirtProvider = ParavirtProvider_HyperV;
1388 }
1389 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1390 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1391 || mUserData->s.strOsType == "Linux"
1392 || mUserData->s.strOsType == "Linux_64"
1393 || mUserData->s.strOsType == "ArchLinux"
1394 || mUserData->s.strOsType == "ArchLinux_64"
1395 || mUserData->s.strOsType == "Debian"
1396 || mUserData->s.strOsType == "Debian_64"
1397 || mUserData->s.strOsType == "Fedora"
1398 || mUserData->s.strOsType == "Fedora_64"
1399 || mUserData->s.strOsType == "Gentoo"
1400 || mUserData->s.strOsType == "Gentoo_64"
1401 || mUserData->s.strOsType == "Mandriva"
1402 || mUserData->s.strOsType == "Mandriva_64"
1403 || mUserData->s.strOsType == "OpenSUSE"
1404 || mUserData->s.strOsType == "OpenSUSE_64"
1405 || mUserData->s.strOsType == "Oracle"
1406 || mUserData->s.strOsType == "Oracle_64"
1407 || mUserData->s.strOsType == "RedHat"
1408 || mUserData->s.strOsType == "RedHat_64"
1409 || mUserData->s.strOsType == "Turbolinux"
1410 || mUserData->s.strOsType == "Turbolinux_64"
1411 || mUserData->s.strOsType == "Ubuntu"
1412 || mUserData->s.strOsType == "Ubuntu_64"
1413 || mUserData->s.strOsType == "Xandros"
1414 || mUserData->s.strOsType == "Xandros_64")
1415 {
1416 *aParavirtProvider = ParavirtProvider_KVM;
1417 }
1418 else
1419 *aParavirtProvider = ParavirtProvider_None;
1420 break;
1421 }
1422
1423 default: AssertFailedBreak(); /* Shut up MSC. */
1424 }
1425 break;
1426 }
1427 }
1428
1429 Assert( *aParavirtProvider == ParavirtProvider_None
1430 || *aParavirtProvider == ParavirtProvider_Minimal
1431 || *aParavirtProvider == ParavirtProvider_HyperV
1432 || *aParavirtProvider == ParavirtProvider_KVM);
1433 return S_OK;
1434}
1435
1436HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1437{
1438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 aHardwareVersion = mHWData->mHWVersion;
1441
1442 return S_OK;
1443}
1444
1445HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1446{
1447 /* check known version */
1448 Utf8Str hwVersion = aHardwareVersion;
1449 if ( hwVersion.compare("1") != 0
1450 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1451 return setError(E_INVALIDARG,
1452 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1453
1454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 HRESULT rc = i_checkStateDependency(MutableStateDep);
1457 if (FAILED(rc)) return rc;
1458
1459 i_setModified(IsModified_MachineData);
1460 mHWData.backup();
1461 mHWData->mHWVersion = aHardwareVersion;
1462
1463 return S_OK;
1464}
1465
1466HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1467{
1468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1469
1470 if (!mHWData->mHardwareUUID.isZero())
1471 aHardwareUUID = mHWData->mHardwareUUID;
1472 else
1473 aHardwareUUID = mData->mUuid;
1474
1475 return S_OK;
1476}
1477
1478HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1479{
1480 if (!aHardwareUUID.isValid())
1481 return E_INVALIDARG;
1482
1483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 HRESULT rc = i_checkStateDependency(MutableStateDep);
1486 if (FAILED(rc)) return rc;
1487
1488 i_setModified(IsModified_MachineData);
1489 mHWData.backup();
1490 if (aHardwareUUID == mData->mUuid)
1491 mHWData->mHardwareUUID.clear();
1492 else
1493 mHWData->mHardwareUUID = aHardwareUUID;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1499{
1500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1501
1502 *aMemorySize = mHWData->mMemorySize;
1503
1504 return S_OK;
1505}
1506
1507HRESULT Machine::setMemorySize(ULONG aMemorySize)
1508{
1509 /* check RAM limits */
1510 if ( aMemorySize < MM_RAM_MIN_IN_MB
1511 || aMemorySize > MM_RAM_MAX_IN_MB
1512 )
1513 return setError(E_INVALIDARG,
1514 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1515 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1516
1517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1518
1519 HRESULT rc = i_checkStateDependency(MutableStateDep);
1520 if (FAILED(rc)) return rc;
1521
1522 i_setModified(IsModified_MachineData);
1523 mHWData.backup();
1524 mHWData->mMemorySize = aMemorySize;
1525
1526 return S_OK;
1527}
1528
1529HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1530{
1531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1532
1533 *aCPUCount = mHWData->mCPUCount;
1534
1535 return S_OK;
1536}
1537
1538HRESULT Machine::setCPUCount(ULONG aCPUCount)
1539{
1540 /* check CPU limits */
1541 if ( aCPUCount < SchemaDefs::MinCPUCount
1542 || aCPUCount > SchemaDefs::MaxCPUCount
1543 )
1544 return setError(E_INVALIDARG,
1545 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1546 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1547
1548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1549
1550 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1551 if (mHWData->mCPUHotPlugEnabled)
1552 {
1553 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1554 {
1555 if (mHWData->mCPUAttached[idx])
1556 return setError(E_INVALIDARG,
1557 tr("There is still a CPU attached to socket %lu."
1558 "Detach the CPU before removing the socket"),
1559 aCPUCount, idx+1);
1560 }
1561 }
1562
1563 HRESULT rc = i_checkStateDependency(MutableStateDep);
1564 if (FAILED(rc)) return rc;
1565
1566 i_setModified(IsModified_MachineData);
1567 mHWData.backup();
1568 mHWData->mCPUCount = aCPUCount;
1569
1570 return S_OK;
1571}
1572
1573HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1574{
1575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1576
1577 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1578
1579 return S_OK;
1580}
1581
1582HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1583{
1584 HRESULT rc = S_OK;
1585
1586 /* check throttle limits */
1587 if ( aCPUExecutionCap < 1
1588 || aCPUExecutionCap > 100
1589 )
1590 return setError(E_INVALIDARG,
1591 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1592 aCPUExecutionCap, 1, 100);
1593
1594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1595
1596 alock.release();
1597 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1598 alock.acquire();
1599 if (FAILED(rc)) return rc;
1600
1601 i_setModified(IsModified_MachineData);
1602 mHWData.backup();
1603 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1604
1605 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1606 if (Global::IsOnline(mData->mMachineState))
1607 i_saveSettings(NULL, alock);
1608
1609 return S_OK;
1610}
1611
1612HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1613{
1614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1615
1616 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1617
1618 return S_OK;
1619}
1620
1621HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1622{
1623 HRESULT rc = S_OK;
1624
1625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 rc = i_checkStateDependency(MutableStateDep);
1628 if (FAILED(rc)) return rc;
1629
1630 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1631 {
1632 if (aCPUHotPlugEnabled)
1633 {
1634 i_setModified(IsModified_MachineData);
1635 mHWData.backup();
1636
1637 /* Add the amount of CPUs currently attached */
1638 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1639 mHWData->mCPUAttached[i] = true;
1640 }
1641 else
1642 {
1643 /*
1644 * We can disable hotplug only if the amount of maximum CPUs is equal
1645 * to the amount of attached CPUs
1646 */
1647 unsigned cCpusAttached = 0;
1648 unsigned iHighestId = 0;
1649
1650 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1651 {
1652 if (mHWData->mCPUAttached[i])
1653 {
1654 cCpusAttached++;
1655 iHighestId = i;
1656 }
1657 }
1658
1659 if ( (cCpusAttached != mHWData->mCPUCount)
1660 || (iHighestId >= mHWData->mCPUCount))
1661 return setError(E_INVALIDARG,
1662 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1663
1664 i_setModified(IsModified_MachineData);
1665 mHWData.backup();
1666 }
1667 }
1668
1669 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1670
1671 return rc;
1672}
1673
1674HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1675{
1676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1679
1680 return S_OK;
1681}
1682
1683HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1684{
1685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1688 if (SUCCEEDED(hrc))
1689 {
1690 i_setModified(IsModified_MachineData);
1691 mHWData.backup();
1692 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1693 }
1694 return hrc;
1695}
1696
1697HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1698{
1699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1700 aCPUProfile = mHWData->mCpuProfile;
1701 return S_OK;
1702}
1703
1704HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1705{
1706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1707 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1708 if (SUCCEEDED(hrc))
1709 {
1710 i_setModified(IsModified_MachineData);
1711 mHWData.backup();
1712 /* Empty equals 'host'. */
1713 if (aCPUProfile.isNotEmpty())
1714 mHWData->mCpuProfile = aCPUProfile;
1715 else
1716 mHWData->mCpuProfile = "host";
1717 }
1718 return hrc;
1719}
1720
1721HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1722{
1723#ifdef VBOX_WITH_USB_CARDREADER
1724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1725
1726 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1727
1728 return S_OK;
1729#else
1730 NOREF(aEmulatedUSBCardReaderEnabled);
1731 return E_NOTIMPL;
1732#endif
1733}
1734
1735HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1736{
1737#ifdef VBOX_WITH_USB_CARDREADER
1738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1739
1740 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1741 if (FAILED(rc)) return rc;
1742
1743 i_setModified(IsModified_MachineData);
1744 mHWData.backup();
1745 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1746
1747 return S_OK;
1748#else
1749 NOREF(aEmulatedUSBCardReaderEnabled);
1750 return E_NOTIMPL;
1751#endif
1752}
1753
1754HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1755{
1756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1757
1758 *aHPETEnabled = mHWData->mHPETEnabled;
1759
1760 return S_OK;
1761}
1762
1763HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1764{
1765 HRESULT rc = S_OK;
1766
1767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1768
1769 rc = i_checkStateDependency(MutableStateDep);
1770 if (FAILED(rc)) return rc;
1771
1772 i_setModified(IsModified_MachineData);
1773 mHWData.backup();
1774
1775 mHWData->mHPETEnabled = aHPETEnabled;
1776
1777 return rc;
1778}
1779
1780/** @todo this method should not be public */
1781HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1782{
1783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1784
1785 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1786
1787 return S_OK;
1788}
1789
1790/**
1791 * Set the memory balloon size.
1792 *
1793 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1794 * we have to make sure that we never call IGuest from here.
1795 */
1796HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1797{
1798 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1799#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1800 /* check limits */
1801 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1802 return setError(E_INVALIDARG,
1803 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1804 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1805
1806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1807
1808 i_setModified(IsModified_MachineData);
1809 mHWData.backup();
1810 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1811
1812 return S_OK;
1813#else
1814 NOREF(aMemoryBalloonSize);
1815 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1816#endif
1817}
1818
1819HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1820{
1821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1822
1823 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1824 return S_OK;
1825}
1826
1827HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1828{
1829#ifdef VBOX_WITH_PAGE_SHARING
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831
1832 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1833 i_setModified(IsModified_MachineData);
1834 mHWData.backup();
1835 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1836 return S_OK;
1837#else
1838 NOREF(aPageFusionEnabled);
1839 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1840#endif
1841}
1842
1843HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1844{
1845 /* mBIOSSettings is constant during life time, no need to lock */
1846 aBIOSSettings = mBIOSSettings;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getTrustedPlatformModule(ComPtr<ITrustedPlatformModule> &aTrustedPlatformModule)
1852{
1853 /* mTrustedPlatformModule is constant during life time, no need to lock */
1854 aTrustedPlatformModule = mTrustedPlatformModule;
1855
1856 return S_OK;
1857}
1858
1859HRESULT Machine::getNonVolatileStore(ComPtr<INvramStore> &aNvramStore)
1860{
1861 /* mNvramStore is constant during life time, no need to lock */
1862 aNvramStore = mNvramStore;
1863
1864 return S_OK;
1865}
1866
1867HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1868{
1869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 aRecordingSettings = mRecordingSettings;
1872
1873 return S_OK;
1874}
1875
1876HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1877{
1878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1879
1880 aGraphicsAdapter = mGraphicsAdapter;
1881
1882 return S_OK;
1883}
1884
1885HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1886{
1887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 switch (aProperty)
1890 {
1891 case CPUPropertyType_PAE:
1892 *aValue = mHWData->mPAEEnabled;
1893 break;
1894
1895 case CPUPropertyType_LongMode:
1896 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1897 *aValue = TRUE;
1898 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1899 *aValue = FALSE;
1900#if HC_ARCH_BITS == 64
1901 else
1902 *aValue = TRUE;
1903#else
1904 else
1905 {
1906 *aValue = FALSE;
1907
1908 ComObjPtr<GuestOSType> pGuestOSType;
1909 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1910 pGuestOSType);
1911 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1912 {
1913 if (pGuestOSType->i_is64Bit())
1914 {
1915 ComObjPtr<Host> pHost = mParent->i_host();
1916 alock.release();
1917
1918 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1919 if (FAILED(hrc2))
1920 *aValue = FALSE;
1921 }
1922 }
1923 }
1924#endif
1925 break;
1926
1927 case CPUPropertyType_TripleFaultReset:
1928 *aValue = mHWData->mTripleFaultReset;
1929 break;
1930
1931 case CPUPropertyType_APIC:
1932 *aValue = mHWData->mAPIC;
1933 break;
1934
1935 case CPUPropertyType_X2APIC:
1936 *aValue = mHWData->mX2APIC;
1937 break;
1938
1939 case CPUPropertyType_IBPBOnVMExit:
1940 *aValue = mHWData->mIBPBOnVMExit;
1941 break;
1942
1943 case CPUPropertyType_IBPBOnVMEntry:
1944 *aValue = mHWData->mIBPBOnVMEntry;
1945 break;
1946
1947 case CPUPropertyType_SpecCtrl:
1948 *aValue = mHWData->mSpecCtrl;
1949 break;
1950
1951 case CPUPropertyType_SpecCtrlByHost:
1952 *aValue = mHWData->mSpecCtrlByHost;
1953 break;
1954
1955 case CPUPropertyType_HWVirt:
1956 *aValue = mHWData->mNestedHWVirt;
1957 break;
1958
1959 case CPUPropertyType_L1DFlushOnEMTScheduling:
1960 *aValue = mHWData->mL1DFlushOnSched;
1961 break;
1962
1963 case CPUPropertyType_L1DFlushOnVMEntry:
1964 *aValue = mHWData->mL1DFlushOnVMEntry;
1965 break;
1966
1967 case CPUPropertyType_MDSClearOnEMTScheduling:
1968 *aValue = mHWData->mMDSClearOnSched;
1969 break;
1970
1971 case CPUPropertyType_MDSClearOnVMEntry:
1972 *aValue = mHWData->mMDSClearOnVMEntry;
1973 break;
1974
1975 default:
1976 return E_INVALIDARG;
1977 }
1978 return S_OK;
1979}
1980
1981HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1982{
1983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1984
1985 HRESULT rc = i_checkStateDependency(MutableStateDep);
1986 if (FAILED(rc)) return rc;
1987
1988 switch (aProperty)
1989 {
1990 case CPUPropertyType_PAE:
1991 i_setModified(IsModified_MachineData);
1992 mHWData.backup();
1993 mHWData->mPAEEnabled = !!aValue;
1994 break;
1995
1996 case CPUPropertyType_LongMode:
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2000 break;
2001
2002 case CPUPropertyType_TripleFaultReset:
2003 i_setModified(IsModified_MachineData);
2004 mHWData.backup();
2005 mHWData->mTripleFaultReset = !!aValue;
2006 break;
2007
2008 case CPUPropertyType_APIC:
2009 if (mHWData->mX2APIC)
2010 aValue = TRUE;
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mAPIC = !!aValue;
2014 break;
2015
2016 case CPUPropertyType_X2APIC:
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mX2APIC = !!aValue;
2020 if (aValue)
2021 mHWData->mAPIC = !!aValue;
2022 break;
2023
2024 case CPUPropertyType_IBPBOnVMExit:
2025 i_setModified(IsModified_MachineData);
2026 mHWData.backup();
2027 mHWData->mIBPBOnVMExit = !!aValue;
2028 break;
2029
2030 case CPUPropertyType_IBPBOnVMEntry:
2031 i_setModified(IsModified_MachineData);
2032 mHWData.backup();
2033 mHWData->mIBPBOnVMEntry = !!aValue;
2034 break;
2035
2036 case CPUPropertyType_SpecCtrl:
2037 i_setModified(IsModified_MachineData);
2038 mHWData.backup();
2039 mHWData->mSpecCtrl = !!aValue;
2040 break;
2041
2042 case CPUPropertyType_SpecCtrlByHost:
2043 i_setModified(IsModified_MachineData);
2044 mHWData.backup();
2045 mHWData->mSpecCtrlByHost = !!aValue;
2046 break;
2047
2048 case CPUPropertyType_HWVirt:
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mNestedHWVirt = !!aValue;
2052 break;
2053
2054 case CPUPropertyType_L1DFlushOnEMTScheduling:
2055 i_setModified(IsModified_MachineData);
2056 mHWData.backup();
2057 mHWData->mL1DFlushOnSched = !!aValue;
2058 break;
2059
2060 case CPUPropertyType_L1DFlushOnVMEntry:
2061 i_setModified(IsModified_MachineData);
2062 mHWData.backup();
2063 mHWData->mL1DFlushOnVMEntry = !!aValue;
2064 break;
2065
2066 case CPUPropertyType_MDSClearOnEMTScheduling:
2067 i_setModified(IsModified_MachineData);
2068 mHWData.backup();
2069 mHWData->mMDSClearOnSched = !!aValue;
2070 break;
2071
2072 case CPUPropertyType_MDSClearOnVMEntry:
2073 i_setModified(IsModified_MachineData);
2074 mHWData.backup();
2075 mHWData->mMDSClearOnVMEntry = !!aValue;
2076 break;
2077
2078 default:
2079 return E_INVALIDARG;
2080 }
2081 return S_OK;
2082}
2083
2084HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2085 ULONG *aValEcx, ULONG *aValEdx)
2086{
2087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2088 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2089 {
2090 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2091 it != mHWData->mCpuIdLeafList.end();
2092 ++it)
2093 {
2094 if (aOrdinal == 0)
2095 {
2096 const settings::CpuIdLeaf &rLeaf= *it;
2097 *aIdx = rLeaf.idx;
2098 *aSubIdx = rLeaf.idxSub;
2099 *aValEax = rLeaf.uEax;
2100 *aValEbx = rLeaf.uEbx;
2101 *aValEcx = rLeaf.uEcx;
2102 *aValEdx = rLeaf.uEdx;
2103 return S_OK;
2104 }
2105 aOrdinal--;
2106 }
2107 }
2108 return E_INVALIDARG;
2109}
2110
2111HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2112{
2113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 /*
2116 * Search the list.
2117 */
2118 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2119 {
2120 const settings::CpuIdLeaf &rLeaf= *it;
2121 if ( rLeaf.idx == aIdx
2122 && ( aSubIdx == UINT32_MAX
2123 || rLeaf.idxSub == aSubIdx) )
2124 {
2125 *aValEax = rLeaf.uEax;
2126 *aValEbx = rLeaf.uEbx;
2127 *aValEcx = rLeaf.uEcx;
2128 *aValEdx = rLeaf.uEdx;
2129 return S_OK;
2130 }
2131 }
2132
2133 return E_INVALIDARG;
2134}
2135
2136
2137HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2138{
2139 /*
2140 * Validate input before taking locks and checking state.
2141 */
2142 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2143 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2144 if ( aIdx >= UINT32_C(0x20)
2145 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2146 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2147 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2148
2149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2150 HRESULT rc = i_checkStateDependency(MutableStateDep);
2151 if (FAILED(rc)) return rc;
2152
2153 /*
2154 * Impose a maximum number of leaves.
2155 */
2156 if (mHWData->mCpuIdLeafList.size() > 256)
2157 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2158
2159 /*
2160 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2161 */
2162 i_setModified(IsModified_MachineData);
2163 mHWData.backup();
2164
2165 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2166 {
2167 settings::CpuIdLeaf &rLeaf= *it;
2168 if ( rLeaf.idx == aIdx
2169 && ( aSubIdx == UINT32_MAX
2170 || rLeaf.idxSub == aSubIdx) )
2171 it = mHWData->mCpuIdLeafList.erase(it);
2172 else
2173 ++it;
2174 }
2175
2176 settings::CpuIdLeaf NewLeaf;
2177 NewLeaf.idx = aIdx;
2178 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2179 NewLeaf.uEax = aValEax;
2180 NewLeaf.uEbx = aValEbx;
2181 NewLeaf.uEcx = aValEcx;
2182 NewLeaf.uEdx = aValEdx;
2183 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2184 return S_OK;
2185}
2186
2187HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2188{
2189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2190
2191 HRESULT rc = i_checkStateDependency(MutableStateDep);
2192 if (FAILED(rc)) return rc;
2193
2194 /*
2195 * Do the removal.
2196 */
2197 bool fModified = mHWData.isBackedUp();
2198 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2199 {
2200 settings::CpuIdLeaf &rLeaf= *it;
2201 if ( rLeaf.idx == aIdx
2202 && ( aSubIdx == UINT32_MAX
2203 || rLeaf.idxSub == aSubIdx) )
2204 {
2205 if (!fModified)
2206 {
2207 fModified = true;
2208 i_setModified(IsModified_MachineData);
2209 mHWData.backup();
2210 // Start from the beginning, since mHWData.backup() creates
2211 // a new list, causing iterator mixup. This makes sure that
2212 // the settings are not unnecessarily marked as modified,
2213 // at the price of extra list walking.
2214 it = mHWData->mCpuIdLeafList.begin();
2215 }
2216 else
2217 it = mHWData->mCpuIdLeafList.erase(it);
2218 }
2219 else
2220 ++it;
2221 }
2222
2223 return S_OK;
2224}
2225
2226HRESULT Machine::removeAllCPUIDLeaves()
2227{
2228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2229
2230 HRESULT rc = i_checkStateDependency(MutableStateDep);
2231 if (FAILED(rc)) return rc;
2232
2233 if (mHWData->mCpuIdLeafList.size() > 0)
2234 {
2235 i_setModified(IsModified_MachineData);
2236 mHWData.backup();
2237
2238 mHWData->mCpuIdLeafList.clear();
2239 }
2240
2241 return S_OK;
2242}
2243HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2244{
2245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2246
2247 switch(aProperty)
2248 {
2249 case HWVirtExPropertyType_Enabled:
2250 *aValue = mHWData->mHWVirtExEnabled;
2251 break;
2252
2253 case HWVirtExPropertyType_VPID:
2254 *aValue = mHWData->mHWVirtExVPIDEnabled;
2255 break;
2256
2257 case HWVirtExPropertyType_NestedPaging:
2258 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2259 break;
2260
2261 case HWVirtExPropertyType_UnrestrictedExecution:
2262 *aValue = mHWData->mHWVirtExUXEnabled;
2263 break;
2264
2265 case HWVirtExPropertyType_LargePages:
2266 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2267 break;
2268
2269 case HWVirtExPropertyType_Force:
2270 *aValue = mHWData->mHWVirtExForceEnabled;
2271 break;
2272
2273 case HWVirtExPropertyType_UseNativeApi:
2274 *aValue = mHWData->mHWVirtExUseNativeApi;
2275 break;
2276
2277 case HWVirtExPropertyType_VirtVmsaveVmload:
2278 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2279 break;
2280
2281 default:
2282 return E_INVALIDARG;
2283 }
2284 return S_OK;
2285}
2286
2287HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2288{
2289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2290
2291 HRESULT rc = i_checkStateDependency(MutableStateDep);
2292 if (FAILED(rc)) return rc;
2293
2294 switch (aProperty)
2295 {
2296 case HWVirtExPropertyType_Enabled:
2297 i_setModified(IsModified_MachineData);
2298 mHWData.backup();
2299 mHWData->mHWVirtExEnabled = !!aValue;
2300 break;
2301
2302 case HWVirtExPropertyType_VPID:
2303 i_setModified(IsModified_MachineData);
2304 mHWData.backup();
2305 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2306 break;
2307
2308 case HWVirtExPropertyType_NestedPaging:
2309 i_setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2312 break;
2313
2314 case HWVirtExPropertyType_UnrestrictedExecution:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mHWVirtExUXEnabled = !!aValue;
2318 break;
2319
2320 case HWVirtExPropertyType_LargePages:
2321 i_setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2324 break;
2325
2326 case HWVirtExPropertyType_Force:
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 mHWData->mHWVirtExForceEnabled = !!aValue;
2330 break;
2331
2332 case HWVirtExPropertyType_UseNativeApi:
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mHWVirtExUseNativeApi = !!aValue;
2336 break;
2337
2338 case HWVirtExPropertyType_VirtVmsaveVmload:
2339 i_setModified(IsModified_MachineData);
2340 mHWData.backup();
2341 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2342 break;
2343
2344 default:
2345 return E_INVALIDARG;
2346 }
2347
2348 return S_OK;
2349}
2350
2351HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2352{
2353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
2355 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2356
2357 return S_OK;
2358}
2359
2360HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2361{
2362 /** @todo (r=dmik):
2363 * 1. Allow to change the name of the snapshot folder containing snapshots
2364 * 2. Rename the folder on disk instead of just changing the property
2365 * value (to be smart and not to leave garbage). Note that it cannot be
2366 * done here because the change may be rolled back. Thus, the right
2367 * place is #saveSettings().
2368 */
2369
2370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2371
2372 HRESULT rc = i_checkStateDependency(MutableStateDep);
2373 if (FAILED(rc)) return rc;
2374
2375 if (!mData->mCurrentSnapshot.isNull())
2376 return setError(E_FAIL,
2377 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2378
2379 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2380
2381 if (strSnapshotFolder.isEmpty())
2382 strSnapshotFolder = "Snapshots";
2383 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2384 if (RT_FAILURE(vrc))
2385 return setErrorBoth(E_FAIL, vrc,
2386 tr("Invalid snapshot folder '%s' (%Rrc)"),
2387 strSnapshotFolder.c_str(), vrc);
2388
2389 i_setModified(IsModified_MachineData);
2390 mUserData.backup();
2391
2392 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2393
2394 return S_OK;
2395}
2396
2397HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2398{
2399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2400
2401 aMediumAttachments.resize(mMediumAttachments->size());
2402 size_t i = 0;
2403 for (MediumAttachmentList::const_iterator
2404 it = mMediumAttachments->begin();
2405 it != mMediumAttachments->end();
2406 ++it, ++i)
2407 aMediumAttachments[i] = *it;
2408
2409 return S_OK;
2410}
2411
2412HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2413{
2414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2415
2416 Assert(!!mVRDEServer);
2417
2418 aVRDEServer = mVRDEServer;
2419
2420 return S_OK;
2421}
2422
2423HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2424{
2425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2426
2427 aAudioAdapter = mAudioAdapter;
2428
2429 return S_OK;
2430}
2431
2432HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2433{
2434#ifdef VBOX_WITH_VUSB
2435 clearError();
2436 MultiResult rc(S_OK);
2437
2438# ifdef VBOX_WITH_USB
2439 rc = mParent->i_host()->i_checkUSBProxyService();
2440 if (FAILED(rc)) return rc;
2441# endif
2442
2443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2444
2445 aUSBControllers.resize(mUSBControllers->size());
2446 size_t i = 0;
2447 for (USBControllerList::const_iterator
2448 it = mUSBControllers->begin();
2449 it != mUSBControllers->end();
2450 ++it, ++i)
2451 aUSBControllers[i] = *it;
2452
2453 return S_OK;
2454#else
2455 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2456 * extended error info to indicate that USB is simply not available
2457 * (w/o treating it as a failure), for example, as in OSE */
2458 NOREF(aUSBControllers);
2459 ReturnComNotImplemented();
2460#endif /* VBOX_WITH_VUSB */
2461}
2462
2463HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2464{
2465#ifdef VBOX_WITH_VUSB
2466 clearError();
2467 MultiResult rc(S_OK);
2468
2469# ifdef VBOX_WITH_USB
2470 rc = mParent->i_host()->i_checkUSBProxyService();
2471 if (FAILED(rc)) return rc;
2472# endif
2473
2474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2475
2476 aUSBDeviceFilters = mUSBDeviceFilters;
2477 return rc;
2478#else
2479 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2480 * extended error info to indicate that USB is simply not available
2481 * (w/o treating it as a failure), for example, as in OSE */
2482 NOREF(aUSBDeviceFilters);
2483 ReturnComNotImplemented();
2484#endif /* VBOX_WITH_VUSB */
2485}
2486
2487HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2488{
2489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2490
2491 aSettingsFilePath = mData->m_strConfigFileFull;
2492
2493 return S_OK;
2494}
2495
2496HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2497{
2498 RT_NOREF(aSettingsFilePath);
2499 ReturnComNotImplemented();
2500}
2501
2502HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2503{
2504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2505
2506 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2507 if (FAILED(rc)) return rc;
2508
2509 if (!mData->pMachineConfigFile->fileExists())
2510 // this is a new machine, and no config file exists yet:
2511 *aSettingsModified = TRUE;
2512 else
2513 *aSettingsModified = (mData->flModifications != 0);
2514
2515 return S_OK;
2516}
2517
2518HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2519{
2520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2521
2522 *aSessionState = mData->mSession.mState;
2523
2524 return S_OK;
2525}
2526
2527HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2528{
2529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2530
2531 aSessionName = mData->mSession.mName;
2532
2533 return S_OK;
2534}
2535
2536HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2537{
2538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2539
2540 *aSessionPID = mData->mSession.mPID;
2541
2542 return S_OK;
2543}
2544
2545HRESULT Machine::getState(MachineState_T *aState)
2546{
2547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 *aState = mData->mMachineState;
2550 Assert(mData->mMachineState != MachineState_Null);
2551
2552 return S_OK;
2553}
2554
2555HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2556{
2557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2558
2559 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2560
2561 return S_OK;
2562}
2563
2564HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2565{
2566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568 aStateFilePath = mSSData->strStateFilePath;
2569
2570 return S_OK;
2571}
2572
2573HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2574{
2575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 i_getLogFolder(aLogFolder);
2578
2579 return S_OK;
2580}
2581
2582HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2583{
2584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2585
2586 aCurrentSnapshot = mData->mCurrentSnapshot;
2587
2588 return S_OK;
2589}
2590
2591HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2592{
2593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2594
2595 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2596 ? 0
2597 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2598
2599 return S_OK;
2600}
2601
2602HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2603{
2604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2605
2606 /* Note: for machines with no snapshots, we always return FALSE
2607 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2608 * reasons :) */
2609
2610 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2611 ? FALSE
2612 : mData->mCurrentStateModified;
2613
2614 return S_OK;
2615}
2616
2617HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2618{
2619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2620
2621 aSharedFolders.resize(mHWData->mSharedFolders.size());
2622 size_t i = 0;
2623 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2624 it = mHWData->mSharedFolders.begin();
2625 it != mHWData->mSharedFolders.end();
2626 ++it, ++i)
2627 aSharedFolders[i] = *it;
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 *aClipboardMode = mHWData->mClipboardMode;
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2642{
2643 HRESULT rc = S_OK;
2644
2645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2646
2647 alock.release();
2648 rc = i_onClipboardModeChange(aClipboardMode);
2649 alock.acquire();
2650 if (FAILED(rc)) return rc;
2651
2652 i_setModified(IsModified_MachineData);
2653 mHWData.backup();
2654 mHWData->mClipboardMode = aClipboardMode;
2655
2656 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2657 if (Global::IsOnline(mData->mMachineState))
2658 i_saveSettings(NULL, alock);
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2664{
2665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2666
2667 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2668
2669 return S_OK;
2670}
2671
2672HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2673{
2674 HRESULT rc = S_OK;
2675
2676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 alock.release();
2679 rc = i_onClipboardFileTransferModeChange(aEnabled);
2680 alock.acquire();
2681 if (FAILED(rc)) return rc;
2682
2683 i_setModified(IsModified_MachineData);
2684 mHWData.backup();
2685 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2686
2687 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2688 if (Global::IsOnline(mData->mMachineState))
2689 i_saveSettings(NULL, alock);
2690
2691 return S_OK;
2692}
2693
2694HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2695{
2696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2697
2698 *aDnDMode = mHWData->mDnDMode;
2699
2700 return S_OK;
2701}
2702
2703HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2704{
2705 HRESULT rc = S_OK;
2706
2707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 alock.release();
2710 rc = i_onDnDModeChange(aDnDMode);
2711
2712 alock.acquire();
2713 if (FAILED(rc)) return rc;
2714
2715 i_setModified(IsModified_MachineData);
2716 mHWData.backup();
2717 mHWData->mDnDMode = aDnDMode;
2718
2719 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2720 if (Global::IsOnline(mData->mMachineState))
2721 i_saveSettings(NULL, alock);
2722
2723 return S_OK;
2724}
2725
2726HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2727{
2728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2729
2730 aStorageControllers.resize(mStorageControllers->size());
2731 size_t i = 0;
2732 for (StorageControllerList::const_iterator
2733 it = mStorageControllers->begin();
2734 it != mStorageControllers->end();
2735 ++it, ++i)
2736 aStorageControllers[i] = *it;
2737
2738 return S_OK;
2739}
2740
2741HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2742{
2743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 *aEnabled = mUserData->s.fTeleporterEnabled;
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2751{
2752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 /* Only allow it to be set to true when PoweredOff or Aborted.
2755 (Clearing it is always permitted.) */
2756 if ( aTeleporterEnabled
2757 && mData->mRegistered
2758 && ( !i_isSessionMachine()
2759 || ( mData->mMachineState != MachineState_PoweredOff
2760 && mData->mMachineState != MachineState_Teleported
2761 && mData->mMachineState != MachineState_Aborted
2762 )
2763 )
2764 )
2765 return setError(VBOX_E_INVALID_VM_STATE,
2766 tr("The machine is not powered off (state is %s)"),
2767 Global::stringifyMachineState(mData->mMachineState));
2768
2769 i_setModified(IsModified_MachineData);
2770 mUserData.backup();
2771 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2772
2773 return S_OK;
2774}
2775
2776HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2777{
2778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2779
2780 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2781
2782 return S_OK;
2783}
2784
2785HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2786{
2787 if (aTeleporterPort >= _64K)
2788 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2789
2790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2793 if (FAILED(rc)) return rc;
2794
2795 i_setModified(IsModified_MachineData);
2796 mUserData.backup();
2797 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2798
2799 return S_OK;
2800}
2801
2802HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2803{
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2807
2808 return S_OK;
2809}
2810
2811HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2812{
2813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2816 if (FAILED(rc)) return rc;
2817
2818 i_setModified(IsModified_MachineData);
2819 mUserData.backup();
2820 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2821
2822 return S_OK;
2823}
2824
2825HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2826{
2827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2828 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2829
2830 return S_OK;
2831}
2832
2833HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2834{
2835 /*
2836 * Hash the password first.
2837 */
2838 com::Utf8Str aT = aTeleporterPassword;
2839
2840 if (!aT.isEmpty())
2841 {
2842 if (VBoxIsPasswordHashed(&aT))
2843 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2844 VBoxHashPassword(&aT);
2845 }
2846
2847 /*
2848 * Do the update.
2849 */
2850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2851 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2852 if (SUCCEEDED(hrc))
2853 {
2854 i_setModified(IsModified_MachineData);
2855 mUserData.backup();
2856 mUserData->s.strTeleporterPassword = aT;
2857 }
2858
2859 return hrc;
2860}
2861
2862HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2863{
2864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2865
2866 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2867
2868 return S_OK;
2869}
2870
2871HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2872{
2873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 /* Only allow it to be set to true when PoweredOff or Aborted.
2876 (Clearing it is always permitted.) */
2877 if ( aRTCUseUTC
2878 && mData->mRegistered
2879 && ( !i_isSessionMachine()
2880 || ( mData->mMachineState != MachineState_PoweredOff
2881 && mData->mMachineState != MachineState_Teleported
2882 && mData->mMachineState != MachineState_Aborted
2883 )
2884 )
2885 )
2886 return setError(VBOX_E_INVALID_VM_STATE,
2887 tr("The machine is not powered off (state is %s)"),
2888 Global::stringifyMachineState(mData->mMachineState));
2889
2890 i_setModified(IsModified_MachineData);
2891 mUserData.backup();
2892 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2893
2894 return S_OK;
2895}
2896
2897HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2898{
2899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2900
2901 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2902
2903 return S_OK;
2904}
2905
2906HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2907{
2908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2909
2910 HRESULT rc = i_checkStateDependency(MutableStateDep);
2911 if (FAILED(rc)) return rc;
2912
2913 i_setModified(IsModified_MachineData);
2914 mHWData.backup();
2915 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2916
2917 return S_OK;
2918}
2919
2920HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2921{
2922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2923
2924 *aIOCacheSize = mHWData->mIOCacheSize;
2925
2926 return S_OK;
2927}
2928
2929HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2930{
2931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2932
2933 HRESULT rc = i_checkStateDependency(MutableStateDep);
2934 if (FAILED(rc)) return rc;
2935
2936 i_setModified(IsModified_MachineData);
2937 mHWData.backup();
2938 mHWData->mIOCacheSize = aIOCacheSize;
2939
2940 return S_OK;
2941}
2942
2943
2944/**
2945 * @note Locks objects!
2946 */
2947HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2948 LockType_T aLockType)
2949{
2950 /* check the session state */
2951 SessionState_T state;
2952 HRESULT rc = aSession->COMGETTER(State)(&state);
2953 if (FAILED(rc)) return rc;
2954
2955 if (state != SessionState_Unlocked)
2956 return setError(VBOX_E_INVALID_OBJECT_STATE,
2957 tr("The given session is busy"));
2958
2959 // get the client's IInternalSessionControl interface
2960 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2961 ComAssertMsgRet(!!pSessionControl, (tr("No IInternalSessionControl interface")),
2962 E_INVALIDARG);
2963
2964 // session name (only used in some code paths)
2965 Utf8Str strSessionName;
2966
2967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 if (!mData->mRegistered)
2970 return setError(E_UNEXPECTED,
2971 tr("The machine '%s' is not registered"),
2972 mUserData->s.strName.c_str());
2973
2974 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2975
2976 SessionState_T oldState = mData->mSession.mState;
2977 /* Hack: in case the session is closing and there is a progress object
2978 * which allows waiting for the session to be closed, take the opportunity
2979 * and do a limited wait (max. 1 second). This helps a lot when the system
2980 * is busy and thus session closing can take a little while. */
2981 if ( mData->mSession.mState == SessionState_Unlocking
2982 && mData->mSession.mProgress)
2983 {
2984 alock.release();
2985 mData->mSession.mProgress->WaitForCompletion(1000);
2986 alock.acquire();
2987 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2988 }
2989
2990 // try again now
2991 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2992 // (i.e. session machine exists)
2993 && (aLockType == LockType_Shared) // caller wants a shared link to the
2994 // existing session that holds the write lock:
2995 )
2996 {
2997 // OK, share the session... we are now dealing with three processes:
2998 // 1) VBoxSVC (where this code runs);
2999 // 2) process C: the caller's client process (who wants a shared session);
3000 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3001
3002 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3003 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3004 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3005 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3006 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3007
3008 /*
3009 * Release the lock before calling the client process. It's safe here
3010 * since the only thing to do after we get the lock again is to add
3011 * the remote control to the list (which doesn't directly influence
3012 * anything).
3013 */
3014 alock.release();
3015
3016 // get the console of the session holding the write lock (this is a remote call)
3017 ComPtr<IConsole> pConsoleW;
3018 if (mData->mSession.mLockType == LockType_VM)
3019 {
3020 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3021 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3022 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3023 if (FAILED(rc))
3024 // the failure may occur w/o any error info (from RPC), so provide one
3025 return setError(VBOX_E_VM_ERROR,
3026 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3027 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3028 }
3029
3030 // share the session machine and W's console with the caller's session
3031 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3032 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3033 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3034
3035 if (FAILED(rc))
3036 // the failure may occur w/o any error info (from RPC), so provide one
3037 return setError(VBOX_E_VM_ERROR,
3038 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3039 alock.acquire();
3040
3041 // need to revalidate the state after acquiring the lock again
3042 if (mData->mSession.mState != SessionState_Locked)
3043 {
3044 pSessionControl->Uninitialize();
3045 return setError(VBOX_E_INVALID_SESSION_STATE,
3046 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3047 mUserData->s.strName.c_str());
3048 }
3049
3050 // add the caller's session to the list
3051 mData->mSession.mRemoteControls.push_back(pSessionControl);
3052 }
3053 else if ( mData->mSession.mState == SessionState_Locked
3054 || mData->mSession.mState == SessionState_Unlocking
3055 )
3056 {
3057 // sharing not permitted, or machine still unlocking:
3058 return setError(VBOX_E_INVALID_OBJECT_STATE,
3059 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3060 mUserData->s.strName.c_str());
3061 }
3062 else
3063 {
3064 // machine is not locked: then write-lock the machine (create the session machine)
3065
3066 // must not be busy
3067 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3068
3069 // get the caller's session PID
3070 RTPROCESS pid = NIL_RTPROCESS;
3071 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3072 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3073 Assert(pid != NIL_RTPROCESS);
3074
3075 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3076
3077 if (fLaunchingVMProcess)
3078 {
3079 if (mData->mSession.mPID == NIL_RTPROCESS)
3080 {
3081 // two or more clients racing for a lock, the one which set the
3082 // session state to Spawning will win, the others will get an
3083 // error as we can't decide here if waiting a little would help
3084 // (only for shared locks this would avoid an error)
3085 return setError(VBOX_E_INVALID_OBJECT_STATE,
3086 tr("The machine '%s' already has a lock request pending"),
3087 mUserData->s.strName.c_str());
3088 }
3089
3090 // this machine is awaiting for a spawning session to be opened:
3091 // then the calling process must be the one that got started by
3092 // LaunchVMProcess()
3093
3094 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3095 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3096
3097#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3098 /* Hardened windows builds spawns three processes when a VM is
3099 launched, the 3rd one is the one that will end up here. */
3100 RTPROCESS pidParent;
3101 int vrc = RTProcQueryParent(pid, &pidParent);
3102 if (RT_SUCCESS(vrc))
3103 vrc = RTProcQueryParent(pidParent, &pidParent);
3104 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3105 || vrc == VERR_ACCESS_DENIED)
3106 {
3107 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3108 mData->mSession.mPID = pid;
3109 }
3110#endif
3111
3112 if (mData->mSession.mPID != pid)
3113 return setError(E_ACCESSDENIED,
3114 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3115 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3116 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3117 }
3118
3119 // create the mutable SessionMachine from the current machine
3120 ComObjPtr<SessionMachine> sessionMachine;
3121 sessionMachine.createObject();
3122 rc = sessionMachine->init(this);
3123 AssertComRC(rc);
3124
3125 /* NOTE: doing return from this function after this point but
3126 * before the end is forbidden since it may call SessionMachine::uninit()
3127 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3128 * lock while still holding the Machine lock in alock so that a deadlock
3129 * is possible due to the wrong lock order. */
3130
3131 if (SUCCEEDED(rc))
3132 {
3133 /*
3134 * Set the session state to Spawning to protect against subsequent
3135 * attempts to open a session and to unregister the machine after
3136 * we release the lock.
3137 */
3138 SessionState_T origState = mData->mSession.mState;
3139 mData->mSession.mState = SessionState_Spawning;
3140
3141#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3142 /* Get the client token ID to be passed to the client process */
3143 Utf8Str strTokenId;
3144 sessionMachine->i_getTokenId(strTokenId);
3145 Assert(!strTokenId.isEmpty());
3146#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3147 /* Get the client token to be passed to the client process */
3148 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3149 /* The token is now "owned" by pToken, fix refcount */
3150 if (!pToken.isNull())
3151 pToken->Release();
3152#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3153
3154 /*
3155 * Release the lock before calling the client process -- it will call
3156 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3157 * because the state is Spawning, so that LaunchVMProcess() and
3158 * LockMachine() calls will fail. This method, called before we
3159 * acquire the lock again, will fail because of the wrong PID.
3160 *
3161 * Note that mData->mSession.mRemoteControls accessed outside
3162 * the lock may not be modified when state is Spawning, so it's safe.
3163 */
3164 alock.release();
3165
3166 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3167#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3168 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3169#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3170 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3171 /* Now the token is owned by the client process. */
3172 pToken.setNull();
3173#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3174 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3175
3176 /* The failure may occur w/o any error info (from RPC), so provide one */
3177 if (FAILED(rc))
3178 setError(VBOX_E_VM_ERROR,
3179 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3180
3181 // get session name, either to remember or to compare against
3182 // the already known session name.
3183 {
3184 Bstr bstrSessionName;
3185 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3186 if (SUCCEEDED(rc2))
3187 strSessionName = bstrSessionName;
3188 }
3189
3190 if ( SUCCEEDED(rc)
3191 && fLaunchingVMProcess
3192 )
3193 {
3194 /* complete the remote session initialization */
3195
3196 /* get the console from the direct session */
3197 ComPtr<IConsole> console;
3198 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3199 ComAssertComRC(rc);
3200
3201 if (SUCCEEDED(rc) && !console)
3202 {
3203 ComAssert(!!console);
3204 rc = E_FAIL;
3205 }
3206
3207 /* assign machine & console to the remote session */
3208 if (SUCCEEDED(rc))
3209 {
3210 /*
3211 * after LaunchVMProcess(), the first and the only
3212 * entry in remoteControls is that remote session
3213 */
3214 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3215 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3216 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3217
3218 /* The failure may occur w/o any error info (from RPC), so provide one */
3219 if (FAILED(rc))
3220 setError(VBOX_E_VM_ERROR,
3221 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3222 }
3223
3224 if (FAILED(rc))
3225 pSessionControl->Uninitialize();
3226 }
3227
3228 /* acquire the lock again */
3229 alock.acquire();
3230
3231 /* Restore the session state */
3232 mData->mSession.mState = origState;
3233 }
3234
3235 // finalize spawning anyway (this is why we don't return on errors above)
3236 if (fLaunchingVMProcess)
3237 {
3238 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3239 /* Note that the progress object is finalized later */
3240 /** @todo Consider checking mData->mSession.mProgress for cancellation
3241 * around here. */
3242
3243 /* We don't reset mSession.mPID here because it is necessary for
3244 * SessionMachine::uninit() to reap the child process later. */
3245
3246 if (FAILED(rc))
3247 {
3248 /* Close the remote session, remove the remote control from the list
3249 * and reset session state to Closed (@note keep the code in sync
3250 * with the relevant part in checkForSpawnFailure()). */
3251
3252 Assert(mData->mSession.mRemoteControls.size() == 1);
3253 if (mData->mSession.mRemoteControls.size() == 1)
3254 {
3255 ErrorInfoKeeper eik;
3256 mData->mSession.mRemoteControls.front()->Uninitialize();
3257 }
3258
3259 mData->mSession.mRemoteControls.clear();
3260 mData->mSession.mState = SessionState_Unlocked;
3261 }
3262 }
3263 else
3264 {
3265 /* memorize PID of the directly opened session */
3266 if (SUCCEEDED(rc))
3267 mData->mSession.mPID = pid;
3268 }
3269
3270 if (SUCCEEDED(rc))
3271 {
3272 mData->mSession.mLockType = aLockType;
3273 /* memorize the direct session control and cache IUnknown for it */
3274 mData->mSession.mDirectControl = pSessionControl;
3275 mData->mSession.mState = SessionState_Locked;
3276 if (!fLaunchingVMProcess)
3277 mData->mSession.mName = strSessionName;
3278 /* associate the SessionMachine with this Machine */
3279 mData->mSession.mMachine = sessionMachine;
3280
3281 /* request an IUnknown pointer early from the remote party for later
3282 * identity checks (it will be internally cached within mDirectControl
3283 * at least on XPCOM) */
3284 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3285 NOREF(unk);
3286 }
3287
3288 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3289 * would break the lock order */
3290 alock.release();
3291
3292 /* uninitialize the created session machine on failure */
3293 if (FAILED(rc))
3294 sessionMachine->uninit();
3295 }
3296
3297 if (SUCCEEDED(rc))
3298 {
3299 /*
3300 * tell the client watcher thread to update the set of
3301 * machines that have open sessions
3302 */
3303 mParent->i_updateClientWatcher();
3304
3305 if (oldState != SessionState_Locked)
3306 /* fire an event */
3307 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3308 }
3309
3310 return rc;
3311}
3312
3313/**
3314 * @note Locks objects!
3315 */
3316HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3317 const com::Utf8Str &aName,
3318 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3319 ComPtr<IProgress> &aProgress)
3320{
3321 Utf8Str strFrontend(aName);
3322 /* "emergencystop" doesn't need the session, so skip the checks/interface
3323 * retrieval. This code doesn't quite fit in here, but introducing a
3324 * special API method would be even more effort, and would require explicit
3325 * support by every API client. It's better to hide the feature a bit. */
3326 if (strFrontend != "emergencystop")
3327 CheckComArgNotNull(aSession);
3328
3329 HRESULT rc = S_OK;
3330 if (strFrontend.isEmpty())
3331 {
3332 Bstr bstrFrontend;
3333 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3334 if (FAILED(rc))
3335 return rc;
3336 strFrontend = bstrFrontend;
3337 if (strFrontend.isEmpty())
3338 {
3339 ComPtr<ISystemProperties> systemProperties;
3340 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3341 if (FAILED(rc))
3342 return rc;
3343 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3344 if (FAILED(rc))
3345 return rc;
3346 strFrontend = bstrFrontend;
3347 }
3348 /* paranoia - emergencystop is not a valid default */
3349 if (strFrontend == "emergencystop")
3350 strFrontend = Utf8Str::Empty;
3351 }
3352 /* default frontend: Qt GUI */
3353 if (strFrontend.isEmpty())
3354 strFrontend = "GUI/Qt";
3355
3356 if (strFrontend != "emergencystop")
3357 {
3358 /* check the session state */
3359 SessionState_T state;
3360 rc = aSession->COMGETTER(State)(&state);
3361 if (FAILED(rc))
3362 return rc;
3363
3364 if (state != SessionState_Unlocked)
3365 return setError(VBOX_E_INVALID_OBJECT_STATE,
3366 tr("The given session is busy"));
3367
3368 /* get the IInternalSessionControl interface */
3369 ComPtr<IInternalSessionControl> control(aSession);
3370 ComAssertMsgRet(!control.isNull(),
3371 ("No IInternalSessionControl interface"),
3372 E_INVALIDARG);
3373
3374 /* get the teleporter enable state for the progress object init. */
3375 BOOL fTeleporterEnabled;
3376 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3377 if (FAILED(rc))
3378 return rc;
3379
3380 /* create a progress object */
3381 ComObjPtr<ProgressProxy> progress;
3382 progress.createObject();
3383 rc = progress->init(mParent,
3384 static_cast<IMachine*>(this),
3385 Bstr(tr("Starting VM")).raw(),
3386 TRUE /* aCancelable */,
3387 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3388 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3389 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3390 2 /* uFirstOperationWeight */,
3391 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3392
3393 if (SUCCEEDED(rc))
3394 {
3395 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3396 if (SUCCEEDED(rc))
3397 {
3398 aProgress = progress;
3399
3400 /* signal the client watcher thread */
3401 mParent->i_updateClientWatcher();
3402
3403 /* fire an event */
3404 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3405 }
3406 }
3407 }
3408 else
3409 {
3410 /* no progress object - either instant success or failure */
3411 aProgress = NULL;
3412
3413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3414
3415 if (mData->mSession.mState != SessionState_Locked)
3416 return setError(VBOX_E_INVALID_OBJECT_STATE,
3417 tr("The machine '%s' is not locked by a session"),
3418 mUserData->s.strName.c_str());
3419
3420 /* must have a VM process associated - do not kill normal API clients
3421 * with an open session */
3422 if (!Global::IsOnline(mData->mMachineState))
3423 return setError(VBOX_E_INVALID_OBJECT_STATE,
3424 tr("The machine '%s' does not have a VM process"),
3425 mUserData->s.strName.c_str());
3426
3427 /* forcibly terminate the VM process */
3428 if (mData->mSession.mPID != NIL_RTPROCESS)
3429 RTProcTerminate(mData->mSession.mPID);
3430
3431 /* signal the client watcher thread, as most likely the client has
3432 * been terminated */
3433 mParent->i_updateClientWatcher();
3434 }
3435
3436 return rc;
3437}
3438
3439HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3440{
3441 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3442 return setError(E_INVALIDARG,
3443 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3444 aPosition, SchemaDefs::MaxBootPosition);
3445
3446 if (aDevice == DeviceType_USB)
3447 return setError(E_NOTIMPL,
3448 tr("Booting from USB device is currently not supported"));
3449
3450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3451
3452 HRESULT rc = i_checkStateDependency(MutableStateDep);
3453 if (FAILED(rc)) return rc;
3454
3455 i_setModified(IsModified_MachineData);
3456 mHWData.backup();
3457 mHWData->mBootOrder[aPosition - 1] = aDevice;
3458
3459 return S_OK;
3460}
3461
3462HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3463{
3464 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3465 return setError(E_INVALIDARG,
3466 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3467 aPosition, SchemaDefs::MaxBootPosition);
3468
3469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3470
3471 *aDevice = mHWData->mBootOrder[aPosition - 1];
3472
3473 return S_OK;
3474}
3475
3476HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3477 LONG aControllerPort,
3478 LONG aDevice,
3479 DeviceType_T aType,
3480 const ComPtr<IMedium> &aMedium)
3481{
3482 IMedium *aM = aMedium;
3483 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3484 aName.c_str(), aControllerPort, aDevice, aType, aM));
3485
3486 // request the host lock first, since might be calling Host methods for getting host drives;
3487 // next, protect the media tree all the while we're in here, as well as our member variables
3488 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3489 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3490
3491 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3492 if (FAILED(rc)) return rc;
3493
3494 /// @todo NEWMEDIA implicit machine registration
3495 if (!mData->mRegistered)
3496 return setError(VBOX_E_INVALID_OBJECT_STATE,
3497 tr("Cannot attach storage devices to an unregistered machine"));
3498
3499 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3500
3501 /* Check for an existing controller. */
3502 ComObjPtr<StorageController> ctl;
3503 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3504 if (FAILED(rc)) return rc;
3505
3506 StorageControllerType_T ctrlType;
3507 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3508 if (FAILED(rc))
3509 return setError(E_FAIL,
3510 tr("Could not get type of controller '%s'"),
3511 aName.c_str());
3512
3513 bool fSilent = false;
3514 Utf8Str strReconfig;
3515
3516 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3517 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3518 if ( mData->mMachineState == MachineState_Paused
3519 && strReconfig == "1")
3520 fSilent = true;
3521
3522 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3523 bool fHotplug = false;
3524 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3525 fHotplug = true;
3526
3527 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3528 return setError(VBOX_E_INVALID_VM_STATE,
3529 tr("Controller '%s' does not support hotplugging"),
3530 aName.c_str());
3531
3532 // check that the port and device are not out of range
3533 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3534 if (FAILED(rc)) return rc;
3535
3536 /* check if the device slot is already busy */
3537 MediumAttachment *pAttachTemp;
3538 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3539 aName,
3540 aControllerPort,
3541 aDevice)))
3542 {
3543 Medium *pMedium = pAttachTemp->i_getMedium();
3544 if (pMedium)
3545 {
3546 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3547 return setError(VBOX_E_OBJECT_IN_USE,
3548 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3549 pMedium->i_getLocationFull().c_str(),
3550 aControllerPort,
3551 aDevice,
3552 aName.c_str());
3553 }
3554 else
3555 return setError(VBOX_E_OBJECT_IN_USE,
3556 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3557 aControllerPort, aDevice, aName.c_str());
3558 }
3559
3560 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3561 if (aMedium && medium.isNull())
3562 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
3563
3564 AutoCaller mediumCaller(medium);
3565 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3566
3567 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3568
3569 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3570 && !medium.isNull()
3571 && ( medium->i_getType() != MediumType_Readonly
3572 || medium->i_getDeviceType() != DeviceType_DVD)
3573 )
3574 return setError(VBOX_E_OBJECT_IN_USE,
3575 tr("Medium '%s' is already attached to this virtual machine"),
3576 medium->i_getLocationFull().c_str());
3577
3578 if (!medium.isNull())
3579 {
3580 MediumType_T mtype = medium->i_getType();
3581 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3582 // For DVDs it's not written to the config file, so needs no global config
3583 // version bump. For floppies it's a new attribute "type", which is ignored
3584 // by older VirtualBox version, so needs no global config version bump either.
3585 // For hard disks this type is not accepted.
3586 if (mtype == MediumType_MultiAttach)
3587 {
3588 // This type is new with VirtualBox 4.0 and therefore requires settings
3589 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3590 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3591 // two reasons: The medium type is a property of the media registry tree, which
3592 // can reside in the global config file (for pre-4.0 media); we would therefore
3593 // possibly need to bump the global config version. We don't want to do that though
3594 // because that might make downgrading to pre-4.0 impossible.
3595 // As a result, we can only use these two new types if the medium is NOT in the
3596 // global registry:
3597 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3598 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3599 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3600 )
3601 return setError(VBOX_E_INVALID_OBJECT_STATE,
3602 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3603 "to machines that were created with VirtualBox 4.0 or later"),
3604 medium->i_getLocationFull().c_str());
3605 }
3606 }
3607
3608 bool fIndirect = false;
3609 if (!medium.isNull())
3610 fIndirect = medium->i_isReadOnly();
3611 bool associate = true;
3612
3613 do
3614 {
3615 if ( aType == DeviceType_HardDisk
3616 && mMediumAttachments.isBackedUp())
3617 {
3618 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3619
3620 /* check if the medium was attached to the VM before we started
3621 * changing attachments in which case the attachment just needs to
3622 * be restored */
3623 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3624 {
3625 AssertReturn(!fIndirect, E_FAIL);
3626
3627 /* see if it's the same bus/channel/device */
3628 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3629 {
3630 /* the simplest case: restore the whole attachment
3631 * and return, nothing else to do */
3632 mMediumAttachments->push_back(pAttachTemp);
3633
3634 /* Reattach the medium to the VM. */
3635 if (fHotplug || fSilent)
3636 {
3637 mediumLock.release();
3638 treeLock.release();
3639 alock.release();
3640
3641 MediumLockList *pMediumLockList(new MediumLockList());
3642
3643 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3644 medium /* pToLockWrite */,
3645 false /* fMediumLockWriteAll */,
3646 NULL,
3647 *pMediumLockList);
3648 alock.acquire();
3649 if (FAILED(rc))
3650 delete pMediumLockList;
3651 else
3652 {
3653 mData->mSession.mLockedMedia.Unlock();
3654 alock.release();
3655 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3656 mData->mSession.mLockedMedia.Lock();
3657 alock.acquire();
3658 }
3659 alock.release();
3660
3661 if (SUCCEEDED(rc))
3662 {
3663 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3664 /* Remove lock list in case of error. */
3665 if (FAILED(rc))
3666 {
3667 mData->mSession.mLockedMedia.Unlock();
3668 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3669 mData->mSession.mLockedMedia.Lock();
3670 }
3671 }
3672 }
3673
3674 return S_OK;
3675 }
3676
3677 /* bus/channel/device differ; we need a new attachment object,
3678 * but don't try to associate it again */
3679 associate = false;
3680 break;
3681 }
3682 }
3683
3684 /* go further only if the attachment is to be indirect */
3685 if (!fIndirect)
3686 break;
3687
3688 /* perform the so called smart attachment logic for indirect
3689 * attachments. Note that smart attachment is only applicable to base
3690 * hard disks. */
3691
3692 if (medium->i_getParent().isNull())
3693 {
3694 /* first, investigate the backup copy of the current hard disk
3695 * attachments to make it possible to re-attach existing diffs to
3696 * another device slot w/o losing their contents */
3697 if (mMediumAttachments.isBackedUp())
3698 {
3699 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3700
3701 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3702 uint32_t foundLevel = 0;
3703
3704 for (MediumAttachmentList::const_iterator
3705 it = oldAtts.begin();
3706 it != oldAtts.end();
3707 ++it)
3708 {
3709 uint32_t level = 0;
3710 MediumAttachment *pAttach = *it;
3711 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3712 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3713 if (pMedium.isNull())
3714 continue;
3715
3716 if (pMedium->i_getBase(&level) == medium)
3717 {
3718 /* skip the hard disk if its currently attached (we
3719 * cannot attach the same hard disk twice) */
3720 if (i_findAttachment(*mMediumAttachments.data(),
3721 pMedium))
3722 continue;
3723
3724 /* matched device, channel and bus (i.e. attached to the
3725 * same place) will win and immediately stop the search;
3726 * otherwise the attachment that has the youngest
3727 * descendant of medium will be used
3728 */
3729 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3730 {
3731 /* the simplest case: restore the whole attachment
3732 * and return, nothing else to do */
3733 mMediumAttachments->push_back(*it);
3734
3735 /* Reattach the medium to the VM. */
3736 if (fHotplug || fSilent)
3737 {
3738 mediumLock.release();
3739 treeLock.release();
3740 alock.release();
3741
3742 MediumLockList *pMediumLockList(new MediumLockList());
3743
3744 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3745 medium /* pToLockWrite */,
3746 false /* fMediumLockWriteAll */,
3747 NULL,
3748 *pMediumLockList);
3749 alock.acquire();
3750 if (FAILED(rc))
3751 delete pMediumLockList;
3752 else
3753 {
3754 mData->mSession.mLockedMedia.Unlock();
3755 alock.release();
3756 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3757 mData->mSession.mLockedMedia.Lock();
3758 alock.acquire();
3759 }
3760 alock.release();
3761
3762 if (SUCCEEDED(rc))
3763 {
3764 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3765 /* Remove lock list in case of error. */
3766 if (FAILED(rc))
3767 {
3768 mData->mSession.mLockedMedia.Unlock();
3769 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3770 mData->mSession.mLockedMedia.Lock();
3771 }
3772 }
3773 }
3774
3775 return S_OK;
3776 }
3777 else if ( foundIt == oldAtts.end()
3778 || level > foundLevel /* prefer younger */
3779 )
3780 {
3781 foundIt = it;
3782 foundLevel = level;
3783 }
3784 }
3785 }
3786
3787 if (foundIt != oldAtts.end())
3788 {
3789 /* use the previously attached hard disk */
3790 medium = (*foundIt)->i_getMedium();
3791 mediumCaller.attach(medium);
3792 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3793 mediumLock.attach(medium);
3794 /* not implicit, doesn't require association with this VM */
3795 fIndirect = false;
3796 associate = false;
3797 /* go right to the MediumAttachment creation */
3798 break;
3799 }
3800 }
3801
3802 /* must give up the medium lock and medium tree lock as below we
3803 * go over snapshots, which needs a lock with higher lock order. */
3804 mediumLock.release();
3805 treeLock.release();
3806
3807 /* then, search through snapshots for the best diff in the given
3808 * hard disk's chain to base the new diff on */
3809
3810 ComObjPtr<Medium> base;
3811 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3812 while (snap)
3813 {
3814 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3815
3816 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3817
3818 MediumAttachment *pAttachFound = NULL;
3819 uint32_t foundLevel = 0;
3820
3821 for (MediumAttachmentList::const_iterator
3822 it = snapAtts.begin();
3823 it != snapAtts.end();
3824 ++it)
3825 {
3826 MediumAttachment *pAttach = *it;
3827 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3828 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3829 if (pMedium.isNull())
3830 continue;
3831
3832 uint32_t level = 0;
3833 if (pMedium->i_getBase(&level) == medium)
3834 {
3835 /* matched device, channel and bus (i.e. attached to the
3836 * same place) will win and immediately stop the search;
3837 * otherwise the attachment that has the youngest
3838 * descendant of medium will be used
3839 */
3840 if ( pAttach->i_getDevice() == aDevice
3841 && pAttach->i_getPort() == aControllerPort
3842 && pAttach->i_getControllerName() == aName
3843 )
3844 {
3845 pAttachFound = pAttach;
3846 break;
3847 }
3848 else if ( !pAttachFound
3849 || level > foundLevel /* prefer younger */
3850 )
3851 {
3852 pAttachFound = pAttach;
3853 foundLevel = level;
3854 }
3855 }
3856 }
3857
3858 if (pAttachFound)
3859 {
3860 base = pAttachFound->i_getMedium();
3861 break;
3862 }
3863
3864 snap = snap->i_getParent();
3865 }
3866
3867 /* re-lock medium tree and the medium, as we need it below */
3868 treeLock.acquire();
3869 mediumLock.acquire();
3870
3871 /* found a suitable diff, use it as a base */
3872 if (!base.isNull())
3873 {
3874 medium = base;
3875 mediumCaller.attach(medium);
3876 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3877 mediumLock.attach(medium);
3878 }
3879 }
3880
3881 Utf8Str strFullSnapshotFolder;
3882 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3883
3884 ComObjPtr<Medium> diff;
3885 diff.createObject();
3886 // store this diff in the same registry as the parent
3887 Guid uuidRegistryParent;
3888 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3889 {
3890 // parent image has no registry: this can happen if we're attaching a new immutable
3891 // image that has not yet been attached (medium then points to the base and we're
3892 // creating the diff image for the immutable, and the parent is not yet registered);
3893 // put the parent in the machine registry then
3894 mediumLock.release();
3895 treeLock.release();
3896 alock.release();
3897 i_addMediumToRegistry(medium);
3898 alock.acquire();
3899 treeLock.acquire();
3900 mediumLock.acquire();
3901 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3902 }
3903 rc = diff->init(mParent,
3904 medium->i_getPreferredDiffFormat(),
3905 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3906 uuidRegistryParent,
3907 DeviceType_HardDisk);
3908 if (FAILED(rc)) return rc;
3909
3910 /* Apply the normal locking logic to the entire chain. */
3911 MediumLockList *pMediumLockList(new MediumLockList());
3912 mediumLock.release();
3913 treeLock.release();
3914 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3915 diff /* pToLockWrite */,
3916 false /* fMediumLockWriteAll */,
3917 medium,
3918 *pMediumLockList);
3919 treeLock.acquire();
3920 mediumLock.acquire();
3921 if (SUCCEEDED(rc))
3922 {
3923 mediumLock.release();
3924 treeLock.release();
3925 rc = pMediumLockList->Lock();
3926 treeLock.acquire();
3927 mediumLock.acquire();
3928 if (FAILED(rc))
3929 setError(rc,
3930 tr("Could not lock medium when creating diff '%s'"),
3931 diff->i_getLocationFull().c_str());
3932 else
3933 {
3934 /* will release the lock before the potentially lengthy
3935 * operation, so protect with the special state */
3936 MachineState_T oldState = mData->mMachineState;
3937 i_setMachineState(MachineState_SettingUp);
3938
3939 mediumLock.release();
3940 treeLock.release();
3941 alock.release();
3942
3943 rc = medium->i_createDiffStorage(diff,
3944 medium->i_getPreferredDiffVariant(),
3945 pMediumLockList,
3946 NULL /* aProgress */,
3947 true /* aWait */,
3948 false /* aNotify */);
3949
3950 alock.acquire();
3951 treeLock.acquire();
3952 mediumLock.acquire();
3953
3954 i_setMachineState(oldState);
3955 }
3956 }
3957
3958 /* Unlock the media and free the associated memory. */
3959 delete pMediumLockList;
3960
3961 if (FAILED(rc)) return rc;
3962
3963 /* use the created diff for the actual attachment */
3964 medium = diff;
3965 mediumCaller.attach(medium);
3966 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3967 mediumLock.attach(medium);
3968 }
3969 while (0);
3970
3971 ComObjPtr<MediumAttachment> attachment;
3972 attachment.createObject();
3973 rc = attachment->init(this,
3974 medium,
3975 aName,
3976 aControllerPort,
3977 aDevice,
3978 aType,
3979 fIndirect,
3980 false /* fPassthrough */,
3981 false /* fTempEject */,
3982 false /* fNonRotational */,
3983 false /* fDiscard */,
3984 fHotplug /* fHotPluggable */,
3985 Utf8Str::Empty);
3986 if (FAILED(rc)) return rc;
3987
3988 if (associate && !medium.isNull())
3989 {
3990 // as the last step, associate the medium to the VM
3991 rc = medium->i_addBackReference(mData->mUuid);
3992 // here we can fail because of Deleting, or being in process of creating a Diff
3993 if (FAILED(rc)) return rc;
3994
3995 mediumLock.release();
3996 treeLock.release();
3997 alock.release();
3998 i_addMediumToRegistry(medium);
3999 alock.acquire();
4000 treeLock.acquire();
4001 mediumLock.acquire();
4002 }
4003
4004 /* success: finally remember the attachment */
4005 i_setModified(IsModified_Storage);
4006 mMediumAttachments.backup();
4007 mMediumAttachments->push_back(attachment);
4008
4009 mediumLock.release();
4010 treeLock.release();
4011 alock.release();
4012
4013 if (fHotplug || fSilent)
4014 {
4015 if (!medium.isNull())
4016 {
4017 MediumLockList *pMediumLockList(new MediumLockList());
4018
4019 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4020 medium /* pToLockWrite */,
4021 false /* fMediumLockWriteAll */,
4022 NULL,
4023 *pMediumLockList);
4024 alock.acquire();
4025 if (FAILED(rc))
4026 delete pMediumLockList;
4027 else
4028 {
4029 mData->mSession.mLockedMedia.Unlock();
4030 alock.release();
4031 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4032 mData->mSession.mLockedMedia.Lock();
4033 alock.acquire();
4034 }
4035 alock.release();
4036 }
4037
4038 if (SUCCEEDED(rc))
4039 {
4040 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4041 /* Remove lock list in case of error. */
4042 if (FAILED(rc))
4043 {
4044 mData->mSession.mLockedMedia.Unlock();
4045 mData->mSession.mLockedMedia.Remove(attachment);
4046 mData->mSession.mLockedMedia.Lock();
4047 }
4048 }
4049 }
4050
4051 /* Save modified registries, but skip this machine as it's the caller's
4052 * job to save its settings like all other settings changes. */
4053 mParent->i_unmarkRegistryModified(i_getId());
4054 mParent->i_saveModifiedRegistries();
4055
4056 if (SUCCEEDED(rc))
4057 {
4058 if (fIndirect && medium != aM)
4059 mParent->i_onMediumConfigChanged(medium);
4060 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4061 }
4062
4063 return rc;
4064}
4065
4066HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4067 LONG aDevice)
4068{
4069 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4070 aName.c_str(), aControllerPort, aDevice));
4071
4072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4073
4074 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4075 if (FAILED(rc)) return rc;
4076
4077 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4078
4079 /* Check for an existing controller. */
4080 ComObjPtr<StorageController> ctl;
4081 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4082 if (FAILED(rc)) return rc;
4083
4084 StorageControllerType_T ctrlType;
4085 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4086 if (FAILED(rc))
4087 return setError(E_FAIL,
4088 tr("Could not get type of controller '%s'"),
4089 aName.c_str());
4090
4091 bool fSilent = false;
4092 Utf8Str strReconfig;
4093
4094 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4095 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4096 if ( mData->mMachineState == MachineState_Paused
4097 && strReconfig == "1")
4098 fSilent = true;
4099
4100 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4101 bool fHotplug = false;
4102 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4103 fHotplug = true;
4104
4105 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4106 return setError(VBOX_E_INVALID_VM_STATE,
4107 tr("Controller '%s' does not support hotplugging"),
4108 aName.c_str());
4109
4110 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4111 aName,
4112 aControllerPort,
4113 aDevice);
4114 if (!pAttach)
4115 return setError(VBOX_E_OBJECT_NOT_FOUND,
4116 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4117 aDevice, aControllerPort, aName.c_str());
4118
4119 if (fHotplug && !pAttach->i_getHotPluggable())
4120 return setError(VBOX_E_NOT_SUPPORTED,
4121 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4122 aDevice, aControllerPort, aName.c_str());
4123
4124 /*
4125 * The VM has to detach the device before we delete any implicit diffs.
4126 * If this fails we can roll back without loosing data.
4127 */
4128 if (fHotplug || fSilent)
4129 {
4130 alock.release();
4131 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4132 alock.acquire();
4133 }
4134 if (FAILED(rc)) return rc;
4135
4136 /* If we are here everything went well and we can delete the implicit now. */
4137 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4138
4139 alock.release();
4140
4141 /* Save modified registries, but skip this machine as it's the caller's
4142 * job to save its settings like all other settings changes. */
4143 mParent->i_unmarkRegistryModified(i_getId());
4144 mParent->i_saveModifiedRegistries();
4145
4146 if (SUCCEEDED(rc))
4147 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4148
4149 return rc;
4150}
4151
4152HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4153 LONG aDevice, BOOL aPassthrough)
4154{
4155 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4156 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4157
4158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4159
4160 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4161 if (FAILED(rc)) return rc;
4162
4163 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4164
4165 /* Check for an existing controller. */
4166 ComObjPtr<StorageController> ctl;
4167 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4168 if (FAILED(rc)) return rc;
4169
4170 StorageControllerType_T ctrlType;
4171 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4172 if (FAILED(rc))
4173 return setError(E_FAIL,
4174 tr("Could not get type of controller '%s'"),
4175 aName.c_str());
4176
4177 bool fSilent = false;
4178 Utf8Str strReconfig;
4179
4180 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4181 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4182 if ( mData->mMachineState == MachineState_Paused
4183 && strReconfig == "1")
4184 fSilent = true;
4185
4186 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4187 bool fHotplug = false;
4188 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4189 fHotplug = true;
4190
4191 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4192 return setError(VBOX_E_INVALID_VM_STATE,
4193 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4194 aName.c_str());
4195
4196 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4197 aName,
4198 aControllerPort,
4199 aDevice);
4200 if (!pAttach)
4201 return setError(VBOX_E_OBJECT_NOT_FOUND,
4202 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4203 aDevice, aControllerPort, aName.c_str());
4204
4205
4206 i_setModified(IsModified_Storage);
4207 mMediumAttachments.backup();
4208
4209 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4210
4211 if (pAttach->i_getType() != DeviceType_DVD)
4212 return setError(E_INVALIDARG,
4213 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4214 aDevice, aControllerPort, aName.c_str());
4215
4216 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4217
4218 pAttach->i_updatePassthrough(!!aPassthrough);
4219
4220 attLock.release();
4221 alock.release();
4222 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4223 if (SUCCEEDED(rc) && fValueChanged)
4224 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4225
4226 return rc;
4227}
4228
4229HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4230 LONG aDevice, BOOL aTemporaryEject)
4231{
4232
4233 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4234 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4235
4236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4237
4238 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4239 if (FAILED(rc)) return rc;
4240
4241 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4242 aName,
4243 aControllerPort,
4244 aDevice);
4245 if (!pAttach)
4246 return setError(VBOX_E_OBJECT_NOT_FOUND,
4247 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4248 aDevice, aControllerPort, aName.c_str());
4249
4250
4251 i_setModified(IsModified_Storage);
4252 mMediumAttachments.backup();
4253
4254 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4255
4256 if (pAttach->i_getType() != DeviceType_DVD)
4257 return setError(E_INVALIDARG,
4258 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4259 aDevice, aControllerPort, aName.c_str());
4260 pAttach->i_updateTempEject(!!aTemporaryEject);
4261
4262 return S_OK;
4263}
4264
4265HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4266 LONG aDevice, BOOL aNonRotational)
4267{
4268
4269 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4270 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4271
4272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4273
4274 HRESULT rc = i_checkStateDependency(MutableStateDep);
4275 if (FAILED(rc)) return rc;
4276
4277 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4278
4279 if (Global::IsOnlineOrTransient(mData->mMachineState))
4280 return setError(VBOX_E_INVALID_VM_STATE,
4281 tr("Invalid machine state: %s"),
4282 Global::stringifyMachineState(mData->mMachineState));
4283
4284 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4285 aName,
4286 aControllerPort,
4287 aDevice);
4288 if (!pAttach)
4289 return setError(VBOX_E_OBJECT_NOT_FOUND,
4290 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4291 aDevice, aControllerPort, aName.c_str());
4292
4293
4294 i_setModified(IsModified_Storage);
4295 mMediumAttachments.backup();
4296
4297 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4298
4299 if (pAttach->i_getType() != DeviceType_HardDisk)
4300 return setError(E_INVALIDARG,
4301 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"),
4302 aDevice, aControllerPort, aName.c_str());
4303 pAttach->i_updateNonRotational(!!aNonRotational);
4304
4305 return S_OK;
4306}
4307
4308HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4309 LONG aDevice, BOOL aDiscard)
4310{
4311
4312 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4313 aName.c_str(), aControllerPort, aDevice, aDiscard));
4314
4315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4316
4317 HRESULT rc = i_checkStateDependency(MutableStateDep);
4318 if (FAILED(rc)) return rc;
4319
4320 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4321
4322 if (Global::IsOnlineOrTransient(mData->mMachineState))
4323 return setError(VBOX_E_INVALID_VM_STATE,
4324 tr("Invalid machine state: %s"),
4325 Global::stringifyMachineState(mData->mMachineState));
4326
4327 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4328 aName,
4329 aControllerPort,
4330 aDevice);
4331 if (!pAttach)
4332 return setError(VBOX_E_OBJECT_NOT_FOUND,
4333 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4334 aDevice, aControllerPort, aName.c_str());
4335
4336
4337 i_setModified(IsModified_Storage);
4338 mMediumAttachments.backup();
4339
4340 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4341
4342 if (pAttach->i_getType() != DeviceType_HardDisk)
4343 return setError(E_INVALIDARG,
4344 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"),
4345 aDevice, aControllerPort, aName.c_str());
4346 pAttach->i_updateDiscard(!!aDiscard);
4347
4348 return S_OK;
4349}
4350
4351HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4352 LONG aDevice, BOOL aHotPluggable)
4353{
4354 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4355 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4356
4357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4358
4359 HRESULT rc = i_checkStateDependency(MutableStateDep);
4360 if (FAILED(rc)) return rc;
4361
4362 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4363
4364 if (Global::IsOnlineOrTransient(mData->mMachineState))
4365 return setError(VBOX_E_INVALID_VM_STATE,
4366 tr("Invalid machine state: %s"),
4367 Global::stringifyMachineState(mData->mMachineState));
4368
4369 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4370 aName,
4371 aControllerPort,
4372 aDevice);
4373 if (!pAttach)
4374 return setError(VBOX_E_OBJECT_NOT_FOUND,
4375 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4376 aDevice, aControllerPort, aName.c_str());
4377
4378 /* Check for an existing controller. */
4379 ComObjPtr<StorageController> ctl;
4380 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4381 if (FAILED(rc)) return rc;
4382
4383 StorageControllerType_T ctrlType;
4384 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4385 if (FAILED(rc))
4386 return setError(E_FAIL,
4387 tr("Could not get type of controller '%s'"),
4388 aName.c_str());
4389
4390 if (!i_isControllerHotplugCapable(ctrlType))
4391 return setError(VBOX_E_NOT_SUPPORTED,
4392 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4393 aName.c_str());
4394
4395 i_setModified(IsModified_Storage);
4396 mMediumAttachments.backup();
4397
4398 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4399
4400 if (pAttach->i_getType() == DeviceType_Floppy)
4401 return setError(E_INVALIDARG,
4402 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"),
4403 aDevice, aControllerPort, aName.c_str());
4404 pAttach->i_updateHotPluggable(!!aHotPluggable);
4405
4406 return S_OK;
4407}
4408
4409HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4410 LONG aDevice)
4411{
4412 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4413 aName.c_str(), aControllerPort, aDevice));
4414
4415 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4416}
4417
4418HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4419 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4420{
4421 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4422 aName.c_str(), aControllerPort, aDevice));
4423
4424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4425
4426 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4427 if (FAILED(rc)) return rc;
4428
4429 if (Global::IsOnlineOrTransient(mData->mMachineState))
4430 return setError(VBOX_E_INVALID_VM_STATE,
4431 tr("Invalid machine state: %s"),
4432 Global::stringifyMachineState(mData->mMachineState));
4433
4434 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4435 aName,
4436 aControllerPort,
4437 aDevice);
4438 if (!pAttach)
4439 return setError(VBOX_E_OBJECT_NOT_FOUND,
4440 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4441 aDevice, aControllerPort, aName.c_str());
4442
4443
4444 i_setModified(IsModified_Storage);
4445 mMediumAttachments.backup();
4446
4447 IBandwidthGroup *iB = aBandwidthGroup;
4448 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4449 if (aBandwidthGroup && group.isNull())
4450 return setError(E_INVALIDARG, tr("The given bandwidth group pointer is invalid"));
4451
4452 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4453
4454 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4455 if (strBandwidthGroupOld.isNotEmpty())
4456 {
4457 /* Get the bandwidth group object and release it - this must not fail. */
4458 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4459 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4460 Assert(SUCCEEDED(rc));
4461
4462 pBandwidthGroupOld->i_release();
4463 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4464 }
4465
4466 if (!group.isNull())
4467 {
4468 group->i_reference();
4469 pAttach->i_updateBandwidthGroup(group->i_getName());
4470 }
4471
4472 return S_OK;
4473}
4474
4475HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4476 LONG aControllerPort,
4477 LONG aDevice,
4478 DeviceType_T aType)
4479{
4480 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4481 aName.c_str(), aControllerPort, aDevice, aType));
4482
4483 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4484}
4485
4486
4487HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4488 LONG aControllerPort,
4489 LONG aDevice,
4490 BOOL aForce)
4491{
4492 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4493 aName.c_str(), aControllerPort, aForce));
4494
4495 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4496}
4497
4498HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4499 LONG aControllerPort,
4500 LONG aDevice,
4501 const ComPtr<IMedium> &aMedium,
4502 BOOL aForce)
4503{
4504 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4505 aName.c_str(), aControllerPort, aDevice, aForce));
4506
4507 // request the host lock first, since might be calling Host methods for getting host drives;
4508 // next, protect the media tree all the while we're in here, as well as our member variables
4509 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4510 this->lockHandle(),
4511 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4512
4513 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4514 aName,
4515 aControllerPort,
4516 aDevice);
4517 if (pAttach.isNull())
4518 return setError(VBOX_E_OBJECT_NOT_FOUND,
4519 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4520 aDevice, aControllerPort, aName.c_str());
4521
4522 /* Remember previously mounted medium. The medium before taking the
4523 * backup is not necessarily the same thing. */
4524 ComObjPtr<Medium> oldmedium;
4525 oldmedium = pAttach->i_getMedium();
4526
4527 IMedium *iM = aMedium;
4528 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4529 if (aMedium && pMedium.isNull())
4530 return setError(E_INVALIDARG, tr("The given medium pointer is invalid"));
4531
4532 AutoCaller mediumCaller(pMedium);
4533 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4534
4535 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4536 if (pMedium)
4537 {
4538 DeviceType_T mediumType = pAttach->i_getType();
4539 switch (mediumType)
4540 {
4541 case DeviceType_DVD:
4542 case DeviceType_Floppy:
4543 break;
4544
4545 default:
4546 return setError(VBOX_E_INVALID_OBJECT_STATE,
4547 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4548 aControllerPort,
4549 aDevice,
4550 aName.c_str());
4551 }
4552 }
4553
4554 i_setModified(IsModified_Storage);
4555 mMediumAttachments.backup();
4556
4557 {
4558 // The backup operation makes the pAttach reference point to the
4559 // old settings. Re-get the correct reference.
4560 pAttach = i_findAttachment(*mMediumAttachments.data(),
4561 aName,
4562 aControllerPort,
4563 aDevice);
4564 if (!oldmedium.isNull())
4565 oldmedium->i_removeBackReference(mData->mUuid);
4566 if (!pMedium.isNull())
4567 {
4568 pMedium->i_addBackReference(mData->mUuid);
4569
4570 mediumLock.release();
4571 multiLock.release();
4572 i_addMediumToRegistry(pMedium);
4573 multiLock.acquire();
4574 mediumLock.acquire();
4575 }
4576
4577 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4578 pAttach->i_updateMedium(pMedium);
4579 }
4580
4581 i_setModified(IsModified_Storage);
4582
4583 mediumLock.release();
4584 multiLock.release();
4585 HRESULT rc = i_onMediumChange(pAttach, aForce);
4586 multiLock.acquire();
4587 mediumLock.acquire();
4588
4589 /* On error roll back this change only. */
4590 if (FAILED(rc))
4591 {
4592 if (!pMedium.isNull())
4593 pMedium->i_removeBackReference(mData->mUuid);
4594 pAttach = i_findAttachment(*mMediumAttachments.data(),
4595 aName,
4596 aControllerPort,
4597 aDevice);
4598 /* If the attachment is gone in the meantime, bail out. */
4599 if (pAttach.isNull())
4600 return rc;
4601 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4602 if (!oldmedium.isNull())
4603 oldmedium->i_addBackReference(mData->mUuid);
4604 pAttach->i_updateMedium(oldmedium);
4605 }
4606
4607 mediumLock.release();
4608 multiLock.release();
4609
4610 /* Save modified registries, but skip this machine as it's the caller's
4611 * job to save its settings like all other settings changes. */
4612 mParent->i_unmarkRegistryModified(i_getId());
4613 mParent->i_saveModifiedRegistries();
4614
4615 return rc;
4616}
4617HRESULT Machine::getMedium(const com::Utf8Str &aName,
4618 LONG aControllerPort,
4619 LONG aDevice,
4620 ComPtr<IMedium> &aMedium)
4621{
4622 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4623 aName.c_str(), aControllerPort, aDevice));
4624
4625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4626
4627 aMedium = NULL;
4628
4629 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4630 aName,
4631 aControllerPort,
4632 aDevice);
4633 if (pAttach.isNull())
4634 return setError(VBOX_E_OBJECT_NOT_FOUND,
4635 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4636 aDevice, aControllerPort, aName.c_str());
4637
4638 aMedium = pAttach->i_getMedium();
4639
4640 return S_OK;
4641}
4642
4643HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4644{
4645 if (aSlot < RT_ELEMENTS(mSerialPorts))
4646 {
4647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4648 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4649 return S_OK;
4650 }
4651 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4652}
4653
4654HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4655{
4656 if (aSlot < RT_ELEMENTS(mParallelPorts))
4657 {
4658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4659 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4660 return S_OK;
4661 }
4662 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4663}
4664
4665
4666HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4667{
4668 /* Do not assert if slot is out of range, just return the advertised
4669 status. testdriver/vbox.py triggers this in logVmInfo. */
4670 if (aSlot >= mNetworkAdapters.size())
4671 return setError(E_INVALIDARG,
4672 tr("No network adapter in slot %RU32 (total %RU32 adapters)", "", mNetworkAdapters.size()),
4673 aSlot, mNetworkAdapters.size());
4674
4675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4676
4677 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4678
4679 return S_OK;
4680}
4681
4682HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4683{
4684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4685
4686 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4687 size_t i = 0;
4688 for (settings::StringsMap::const_iterator
4689 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4690 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4691 ++it, ++i)
4692 aKeys[i] = it->first;
4693
4694 return S_OK;
4695}
4696
4697 /**
4698 * @note Locks this object for reading.
4699 */
4700HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4701 com::Utf8Str &aValue)
4702{
4703 /* start with nothing found */
4704 aValue = "";
4705
4706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4707
4708 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4709 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4710 // found:
4711 aValue = it->second; // source is a Utf8Str
4712
4713 /* return the result to caller (may be empty) */
4714 return S_OK;
4715}
4716
4717 /**
4718 * @note Locks mParent for writing + this object for writing.
4719 */
4720HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4721{
4722 /* Because control characters in aKey have caused problems in the settings
4723 * they are rejected unless the key should be deleted. */
4724 if (!aValue.isEmpty())
4725 {
4726 for (size_t i = 0; i < aKey.length(); ++i)
4727 {
4728 char ch = aKey[i];
4729 if (RTLocCIsCntrl(ch))
4730 return E_INVALIDARG;
4731 }
4732 }
4733
4734 Utf8Str strOldValue; // empty
4735
4736 // locking note: we only hold the read lock briefly to look up the old value,
4737 // then release it and call the onExtraCanChange callbacks. There is a small
4738 // chance of a race insofar as the callback might be called twice if two callers
4739 // change the same key at the same time, but that's a much better solution
4740 // than the deadlock we had here before. The actual changing of the extradata
4741 // is then performed under the write lock and race-free.
4742
4743 // look up the old value first; if nothing has changed then we need not do anything
4744 {
4745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4746
4747 // For snapshots don't even think about allowing changes, extradata
4748 // is global for a machine, so there is nothing snapshot specific.
4749 if (i_isSnapshotMachine())
4750 return setError(VBOX_E_INVALID_VM_STATE,
4751 tr("Cannot set extradata for a snapshot"));
4752
4753 // check if the right IMachine instance is used
4754 if (mData->mRegistered && !i_isSessionMachine())
4755 return setError(VBOX_E_INVALID_VM_STATE,
4756 tr("Cannot set extradata for an immutable machine"));
4757
4758 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4759 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4760 strOldValue = it->second;
4761 }
4762
4763 bool fChanged;
4764 if ((fChanged = (strOldValue != aValue)))
4765 {
4766 // ask for permission from all listeners outside the locks;
4767 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4768 // lock to copy the list of callbacks to invoke
4769 Bstr bstrError;
4770 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4771 {
4772 const char *sep = bstrError.isEmpty() ? "" : ": ";
4773 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4774 return setError(E_ACCESSDENIED,
4775 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4776 aKey.c_str(),
4777 aValue.c_str(),
4778 sep,
4779 bstrError.raw());
4780 }
4781
4782 // data is changing and change not vetoed: then write it out under the lock
4783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4784
4785 if (aValue.isEmpty())
4786 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4787 else
4788 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4789 // creates a new key if needed
4790
4791 bool fNeedsGlobalSaveSettings = false;
4792 // This saving of settings is tricky: there is no "old state" for the
4793 // extradata items at all (unlike all other settings), so the old/new
4794 // settings comparison would give a wrong result!
4795 i_saveSettings(&fNeedsGlobalSaveSettings, alock, SaveS_Force);
4796
4797 if (fNeedsGlobalSaveSettings)
4798 {
4799 // save the global settings; for that we should hold only the VirtualBox lock
4800 alock.release();
4801 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4802 mParent->i_saveSettings();
4803 }
4804 }
4805
4806 // fire notification outside the lock
4807 if (fChanged)
4808 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4809
4810 return S_OK;
4811}
4812
4813HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4814{
4815 aProgress = NULL;
4816 NOREF(aSettingsFilePath);
4817 ReturnComNotImplemented();
4818}
4819
4820HRESULT Machine::saveSettings()
4821{
4822 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4823
4824 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4825 if (FAILED(rc)) return rc;
4826
4827 /* the settings file path may never be null */
4828 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4829
4830 /* save all VM data excluding snapshots */
4831 bool fNeedsGlobalSaveSettings = false;
4832 rc = i_saveSettings(&fNeedsGlobalSaveSettings, mlock);
4833 mlock.release();
4834
4835 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4836 {
4837 // save the global settings; for that we should hold only the VirtualBox lock
4838 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4839 rc = mParent->i_saveSettings();
4840 }
4841
4842 return rc;
4843}
4844
4845
4846HRESULT Machine::discardSettings()
4847{
4848 /*
4849 * We need to take the machine list lock here as well as the machine one
4850 * or we'll get into trouble should any media stuff require rolling back.
4851 *
4852 * Details:
4853 *
4854 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4855 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4856 * 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]
4857 * 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
4858 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4859 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4860 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4861 * 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
4862 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4863 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4864 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4865 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4866 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4867 * 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]
4868 * 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] (*)
4869 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4870 * 0:005> k
4871 * # Child-SP RetAddr Call Site
4872 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4873 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4874 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4875 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4876 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4877 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4878 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4879 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4880 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4881 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4882 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4883 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4884 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4885 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4886 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4887 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4888 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4889 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4890 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4891 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4892 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4893 *
4894 */
4895 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4897
4898 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4899 if (FAILED(rc)) return rc;
4900
4901 /*
4902 * during this rollback, the session will be notified if data has
4903 * been actually changed
4904 */
4905 i_rollback(true /* aNotify */);
4906
4907 return S_OK;
4908}
4909
4910/** @note Locks objects! */
4911HRESULT Machine::unregister(AutoCaller &autoCaller,
4912 CleanupMode_T aCleanupMode,
4913 std::vector<ComPtr<IMedium> > &aMedia)
4914{
4915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4916
4917 Guid id(i_getId());
4918
4919 if (mData->mSession.mState != SessionState_Unlocked)
4920 return setError(VBOX_E_INVALID_OBJECT_STATE,
4921 tr("Cannot unregister the machine '%s' while it is locked"),
4922 mUserData->s.strName.c_str());
4923
4924 // wait for state dependents to drop to zero
4925 i_ensureNoStateDependencies(alock);
4926
4927 if (!mData->mAccessible)
4928 {
4929 // inaccessible machines can only be unregistered; uninitialize ourselves
4930 // here because currently there may be no unregistered that are inaccessible
4931 // (this state combination is not supported). Note releasing the caller and
4932 // leaving the lock before calling uninit()
4933 alock.release();
4934 autoCaller.release();
4935
4936 uninit();
4937
4938 mParent->i_unregisterMachine(this, id);
4939 // calls VirtualBox::i_saveSettings()
4940
4941 return S_OK;
4942 }
4943
4944 HRESULT rc = S_OK;
4945 mData->llFilesToDelete.clear();
4946
4947 if (!mSSData->strStateFilePath.isEmpty())
4948 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4949
4950 Utf8Str strNVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
4951 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4952 mData->llFilesToDelete.push_back(strNVRAMFile);
4953
4954 // This list collects the medium objects from all medium attachments
4955 // which we will detach from the machine and its snapshots, in a specific
4956 // order which allows for closing all media without getting "media in use"
4957 // errors, simply by going through the list from the front to the back:
4958 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4959 // and must be closed before the parent media from the snapshots, or closing the parents
4960 // will fail because they still have children);
4961 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4962 // the root ("first") snapshot of the machine.
4963 MediaList llMedia;
4964
4965 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4966 && mMediumAttachments->size()
4967 )
4968 {
4969 // we have media attachments: detach them all and add the Medium objects to our list
4970 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4971 }
4972
4973 if (mData->mFirstSnapshot)
4974 {
4975 // add the media from the medium attachments of the snapshots to llMedia
4976 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4977 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4978 // into the children first
4979
4980 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4981 MachineState_T oldState = mData->mMachineState;
4982 mData->mMachineState = MachineState_DeletingSnapshot;
4983
4984 // make a copy of the first snapshot reference so the refcount does not
4985 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4986 // (would hang due to the AutoCaller voodoo)
4987 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4988
4989 // GO!
4990 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4991
4992 mData->mMachineState = oldState;
4993 }
4994
4995 if (FAILED(rc))
4996 {
4997 i_rollbackMedia();
4998 return rc;
4999 }
5000
5001 // commit all the media changes made above
5002 i_commitMedia();
5003
5004 mData->mRegistered = false;
5005
5006 // machine lock no longer needed
5007 alock.release();
5008
5009 /* Make sure that the settings of the current VM are not saved, because
5010 * they are rather crippled at this point to meet the cleanup expectations
5011 * and there's no point destroying the VM config on disk just because. */
5012 mParent->i_unmarkRegistryModified(id);
5013
5014 // return media to caller
5015 aMedia.resize(llMedia.size());
5016 size_t i = 0;
5017 for (MediaList::const_iterator
5018 it = llMedia.begin();
5019 it != llMedia.end();
5020 ++it, ++i)
5021 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5022
5023 mParent->i_unregisterMachine(this, id);
5024 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5025
5026 return S_OK;
5027}
5028
5029/**
5030 * Task record for deleting a machine config.
5031 */
5032class Machine::DeleteConfigTask
5033 : public Machine::Task
5034{
5035public:
5036 DeleteConfigTask(Machine *m,
5037 Progress *p,
5038 const Utf8Str &t,
5039 const RTCList<ComPtr<IMedium> > &llMediums,
5040 const StringsList &llFilesToDelete)
5041 : Task(m, p, t),
5042 m_llMediums(llMediums),
5043 m_llFilesToDelete(llFilesToDelete)
5044 {}
5045
5046private:
5047 void handler()
5048 {
5049 try
5050 {
5051 m_pMachine->i_deleteConfigHandler(*this);
5052 }
5053 catch (...)
5054 {
5055 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5056 }
5057 }
5058
5059 RTCList<ComPtr<IMedium> > m_llMediums;
5060 StringsList m_llFilesToDelete;
5061
5062 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5063};
5064
5065/**
5066 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5067 * SessionMachine::taskHandler().
5068 *
5069 * @note Locks this object for writing.
5070 *
5071 * @param task
5072 * @return
5073 */
5074void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5075{
5076 LogFlowThisFuncEnter();
5077
5078 AutoCaller autoCaller(this);
5079 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5080 if (FAILED(autoCaller.rc()))
5081 {
5082 /* we might have been uninitialized because the session was accidentally
5083 * closed by the client, so don't assert */
5084 HRESULT rc = setError(E_FAIL,
5085 tr("The session has been accidentally closed"));
5086 task.m_pProgress->i_notifyComplete(rc);
5087 LogFlowThisFuncLeave();
5088 return;
5089 }
5090
5091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5092
5093 HRESULT rc = S_OK;
5094
5095 try
5096 {
5097 ULONG uLogHistoryCount = 3;
5098 ComPtr<ISystemProperties> systemProperties;
5099 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5100 if (FAILED(rc)) throw rc;
5101
5102 if (!systemProperties.isNull())
5103 {
5104 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5105 if (FAILED(rc)) throw rc;
5106 }
5107
5108 MachineState_T oldState = mData->mMachineState;
5109 i_setMachineState(MachineState_SettingUp);
5110 alock.release();
5111 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5112 {
5113 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5114 {
5115 AutoCaller mac(pMedium);
5116 if (FAILED(mac.rc())) throw mac.rc();
5117 Utf8Str strLocation = pMedium->i_getLocationFull();
5118 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5119 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5120 if (FAILED(rc)) throw rc;
5121 }
5122 if (pMedium->i_isMediumFormatFile())
5123 {
5124 ComPtr<IProgress> pProgress2;
5125 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5126 if (FAILED(rc)) throw rc;
5127 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5128 if (FAILED(rc)) throw rc;
5129 }
5130
5131 /* Close the medium, deliberately without checking the return
5132 * code, and without leaving any trace in the error info, as
5133 * a failure here is a very minor issue, which shouldn't happen
5134 * as above we even managed to delete the medium. */
5135 {
5136 ErrorInfoKeeper eik;
5137 pMedium->Close();
5138 }
5139 }
5140 i_setMachineState(oldState);
5141 alock.acquire();
5142
5143 // delete the files pushed on the task list by Machine::Delete()
5144 // (this includes saved states of the machine and snapshots and
5145 // medium storage files from the IMedium list passed in, and the
5146 // machine XML file)
5147 for (StringsList::const_iterator
5148 it = task.m_llFilesToDelete.begin();
5149 it != task.m_llFilesToDelete.end();
5150 ++it)
5151 {
5152 const Utf8Str &strFile = *it;
5153 LogFunc(("Deleting file %s\n", strFile.c_str()));
5154 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5155 if (FAILED(rc)) throw rc;
5156
5157 int vrc = RTFileDelete(strFile.c_str());
5158 if (RT_FAILURE(vrc))
5159 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5160 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5161 }
5162
5163 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5164 if (FAILED(rc)) throw rc;
5165
5166 /* delete the settings only when the file actually exists */
5167 if (mData->pMachineConfigFile->fileExists())
5168 {
5169 /* Delete any backup or uncommitted XML files. Ignore failures.
5170 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5171 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5172 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5173 RTFileDelete(otherXml.c_str());
5174 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5175 RTFileDelete(otherXml.c_str());
5176
5177 /* delete the Logs folder, nothing important should be left
5178 * there (we don't check for errors because the user might have
5179 * some private files there that we don't want to delete) */
5180 Utf8Str logFolder;
5181 getLogFolder(logFolder);
5182 Assert(logFolder.length());
5183 if (RTDirExists(logFolder.c_str()))
5184 {
5185 /* Delete all VBox.log[.N] files from the Logs folder
5186 * (this must be in sync with the rotation logic in
5187 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5188 * files that may have been created by the GUI. */
5189 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5190 RTFileDelete(log.c_str());
5191 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5192 RTFileDelete(log.c_str());
5193 for (ULONG i = uLogHistoryCount; i > 0; i--)
5194 {
5195 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5196 RTFileDelete(log.c_str());
5197 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5198 RTFileDelete(log.c_str());
5199 }
5200 log.printf("%s%cVBoxUI.log", logFolder.c_str(), RTPATH_DELIMITER);
5201 RTFileDelete(log.c_str());
5202#if defined(RT_OS_WINDOWS)
5203 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5204 RTFileDelete(log.c_str());
5205 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5206 RTFileDelete(log.c_str());
5207#endif
5208
5209 RTDirRemove(logFolder.c_str());
5210 }
5211
5212 /* delete the Snapshots folder, nothing important should be left
5213 * there (we don't check for errors because the user might have
5214 * some private files there that we don't want to delete) */
5215 Utf8Str strFullSnapshotFolder;
5216 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5217 Assert(!strFullSnapshotFolder.isEmpty());
5218 if (RTDirExists(strFullSnapshotFolder.c_str()))
5219 RTDirRemove(strFullSnapshotFolder.c_str());
5220
5221 // delete the directory that contains the settings file, but only
5222 // if it matches the VM name
5223 Utf8Str settingsDir;
5224 if (i_isInOwnDir(&settingsDir))
5225 RTDirRemove(settingsDir.c_str());
5226 }
5227
5228 alock.release();
5229
5230 mParent->i_saveModifiedRegistries();
5231 }
5232 catch (HRESULT aRC) { rc = aRC; }
5233
5234 task.m_pProgress->i_notifyComplete(rc);
5235
5236 LogFlowThisFuncLeave();
5237}
5238
5239HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5240{
5241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5242
5243 HRESULT rc = i_checkStateDependency(MutableStateDep);
5244 if (FAILED(rc)) return rc;
5245
5246 if (mData->mRegistered)
5247 return setError(VBOX_E_INVALID_VM_STATE,
5248 tr("Cannot delete settings of a registered machine"));
5249
5250 // collect files to delete
5251 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5252 // machine config file
5253 if (mData->pMachineConfigFile->fileExists())
5254 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5255 // backup of machine config file
5256 Utf8Str strTmp(mData->m_strConfigFileFull);
5257 strTmp.append("-prev");
5258 if (RTFileExists(strTmp.c_str()))
5259 llFilesToDelete.push_back(strTmp);
5260
5261 RTCList<ComPtr<IMedium> > llMediums;
5262 for (size_t i = 0; i < aMedia.size(); ++i)
5263 {
5264 IMedium *pIMedium(aMedia[i]);
5265 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5266 if (pMedium.isNull())
5267 return setError(E_INVALIDARG, tr("The given medium pointer with index %d is invalid"), i);
5268 SafeArray<BSTR> ids;
5269 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5270 if (FAILED(rc)) return rc;
5271 /* At this point the medium should not have any back references
5272 * anymore. If it has it is attached to another VM and *must* not
5273 * deleted. */
5274 if (ids.size() < 1)
5275 llMediums.append(pMedium);
5276 }
5277
5278 ComObjPtr<Progress> pProgress;
5279 pProgress.createObject();
5280 rc = pProgress->init(i_getVirtualBox(),
5281 static_cast<IMachine*>(this) /* aInitiator */,
5282 tr("Deleting files"),
5283 true /* fCancellable */,
5284 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5285 tr("Collecting file inventory"));
5286 if (FAILED(rc))
5287 return rc;
5288
5289 /* create and start the task on a separate thread (note that it will not
5290 * start working until we release alock) */
5291 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5292 rc = pTask->createThread();
5293 pTask = NULL;
5294 if (FAILED(rc))
5295 return rc;
5296
5297 pProgress.queryInterfaceTo(aProgress.asOutParam());
5298
5299 LogFlowFuncLeave();
5300
5301 return S_OK;
5302}
5303
5304HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5305{
5306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5307
5308 ComObjPtr<Snapshot> pSnapshot;
5309 HRESULT rc;
5310
5311 if (aNameOrId.isEmpty())
5312 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5313 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5314 else
5315 {
5316 Guid uuid(aNameOrId);
5317 if (uuid.isValid())
5318 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5319 else
5320 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5321 }
5322 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5323
5324 return rc;
5325}
5326
5327HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5328 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5329{
5330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5331
5332 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5333 if (FAILED(rc)) return rc;
5334
5335 ComObjPtr<SharedFolder> sharedFolder;
5336 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5337 if (SUCCEEDED(rc))
5338 return setError(VBOX_E_OBJECT_IN_USE,
5339 tr("Shared folder named '%s' already exists"),
5340 aName.c_str());
5341
5342 sharedFolder.createObject();
5343 rc = sharedFolder->init(i_getMachine(),
5344 aName,
5345 aHostPath,
5346 !!aWritable,
5347 !!aAutomount,
5348 aAutoMountPoint,
5349 true /* fFailOnError */);
5350 if (FAILED(rc)) return rc;
5351
5352 i_setModified(IsModified_SharedFolders);
5353 mHWData.backup();
5354 mHWData->mSharedFolders.push_back(sharedFolder);
5355
5356 /* inform the direct session if any */
5357 alock.release();
5358 i_onSharedFolderChange();
5359
5360 return S_OK;
5361}
5362
5363HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5364{
5365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5366
5367 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5368 if (FAILED(rc)) return rc;
5369
5370 ComObjPtr<SharedFolder> sharedFolder;
5371 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5372 if (FAILED(rc)) return rc;
5373
5374 i_setModified(IsModified_SharedFolders);
5375 mHWData.backup();
5376 mHWData->mSharedFolders.remove(sharedFolder);
5377
5378 /* inform the direct session if any */
5379 alock.release();
5380 i_onSharedFolderChange();
5381
5382 return S_OK;
5383}
5384
5385HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5386{
5387 /* start with No */
5388 *aCanShow = FALSE;
5389
5390 ComPtr<IInternalSessionControl> directControl;
5391 {
5392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5393
5394 if (mData->mSession.mState != SessionState_Locked)
5395 return setError(VBOX_E_INVALID_VM_STATE,
5396 tr("Machine is not locked for session (session state: %s)"),
5397 Global::stringifySessionState(mData->mSession.mState));
5398
5399 if (mData->mSession.mLockType == LockType_VM)
5400 directControl = mData->mSession.mDirectControl;
5401 }
5402
5403 /* ignore calls made after #OnSessionEnd() is called */
5404 if (!directControl)
5405 return S_OK;
5406
5407 LONG64 dummy;
5408 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5409}
5410
5411HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5412{
5413 ComPtr<IInternalSessionControl> directControl;
5414 {
5415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5416
5417 if (mData->mSession.mState != SessionState_Locked)
5418 return setError(E_FAIL,
5419 tr("Machine is not locked for session (session state: %s)"),
5420 Global::stringifySessionState(mData->mSession.mState));
5421
5422 if (mData->mSession.mLockType == LockType_VM)
5423 directControl = mData->mSession.mDirectControl;
5424 }
5425
5426 /* ignore calls made after #OnSessionEnd() is called */
5427 if (!directControl)
5428 return S_OK;
5429
5430 BOOL dummy;
5431 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5432}
5433
5434#ifdef VBOX_WITH_GUEST_PROPS
5435/**
5436 * Look up a guest property in VBoxSVC's internal structures.
5437 */
5438HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5439 com::Utf8Str &aValue,
5440 LONG64 *aTimestamp,
5441 com::Utf8Str &aFlags) const
5442{
5443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5444
5445 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5446 if (it != mHWData->mGuestProperties.end())
5447 {
5448 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5449 aValue = it->second.strValue;
5450 *aTimestamp = it->second.mTimestamp;
5451 GuestPropWriteFlags(it->second.mFlags, szFlags);
5452 aFlags = Utf8Str(szFlags);
5453 }
5454
5455 return S_OK;
5456}
5457
5458/**
5459 * Query the VM that a guest property belongs to for the property.
5460 * @returns E_ACCESSDENIED if the VM process is not available or not
5461 * currently handling queries and the lookup should then be done in
5462 * VBoxSVC.
5463 */
5464HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5465 com::Utf8Str &aValue,
5466 LONG64 *aTimestamp,
5467 com::Utf8Str &aFlags) const
5468{
5469 HRESULT rc = S_OK;
5470 Bstr bstrValue;
5471 Bstr bstrFlags;
5472
5473 ComPtr<IInternalSessionControl> directControl;
5474 {
5475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5476 if (mData->mSession.mLockType == LockType_VM)
5477 directControl = mData->mSession.mDirectControl;
5478 }
5479
5480 /* ignore calls made after #OnSessionEnd() is called */
5481 if (!directControl)
5482 rc = E_ACCESSDENIED;
5483 else
5484 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5485 0 /* accessMode */,
5486 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5487
5488 aValue = bstrValue;
5489 aFlags = bstrFlags;
5490
5491 return rc;
5492}
5493#endif // VBOX_WITH_GUEST_PROPS
5494
5495HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5496 com::Utf8Str &aValue,
5497 LONG64 *aTimestamp,
5498 com::Utf8Str &aFlags)
5499{
5500#ifndef VBOX_WITH_GUEST_PROPS
5501 ReturnComNotImplemented();
5502#else // VBOX_WITH_GUEST_PROPS
5503
5504 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5505
5506 if (rc == E_ACCESSDENIED)
5507 /* The VM is not running or the service is not (yet) accessible */
5508 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5509 return rc;
5510#endif // VBOX_WITH_GUEST_PROPS
5511}
5512
5513HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5514{
5515 LONG64 dummyTimestamp;
5516 com::Utf8Str dummyFlags;
5517 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5518 return rc;
5519
5520}
5521HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5522{
5523 com::Utf8Str dummyFlags;
5524 com::Utf8Str dummyValue;
5525 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5526 return rc;
5527}
5528
5529#ifdef VBOX_WITH_GUEST_PROPS
5530/**
5531 * Set a guest property in VBoxSVC's internal structures.
5532 */
5533HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5534 const com::Utf8Str &aFlags, bool fDelete)
5535{
5536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5537 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5538 if (FAILED(rc)) return rc;
5539
5540 try
5541 {
5542 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5543 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5544 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5545
5546 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5547 if (it == mHWData->mGuestProperties.end())
5548 {
5549 if (!fDelete)
5550 {
5551 i_setModified(IsModified_MachineData);
5552 mHWData.backupEx();
5553
5554 RTTIMESPEC time;
5555 HWData::GuestProperty prop;
5556 prop.strValue = Bstr(aValue).raw();
5557 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5558 prop.mFlags = fFlags;
5559 mHWData->mGuestProperties[aName] = prop;
5560 }
5561 }
5562 else
5563 {
5564 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5565 {
5566 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5567 }
5568 else
5569 {
5570 i_setModified(IsModified_MachineData);
5571 mHWData.backupEx();
5572
5573 /* The backupEx() operation invalidates our iterator,
5574 * so get a new one. */
5575 it = mHWData->mGuestProperties.find(aName);
5576 Assert(it != mHWData->mGuestProperties.end());
5577
5578 if (!fDelete)
5579 {
5580 RTTIMESPEC time;
5581 it->second.strValue = aValue;
5582 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5583 it->second.mFlags = fFlags;
5584 }
5585 else
5586 mHWData->mGuestProperties.erase(it);
5587 }
5588 }
5589
5590 if (SUCCEEDED(rc))
5591 {
5592 alock.release();
5593
5594 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5595 }
5596 }
5597 catch (std::bad_alloc &)
5598 {
5599 rc = E_OUTOFMEMORY;
5600 }
5601
5602 return rc;
5603}
5604
5605/**
5606 * Set a property on the VM that that property belongs to.
5607 * @returns E_ACCESSDENIED if the VM process is not available or not
5608 * currently handling queries and the setting should then be done in
5609 * VBoxSVC.
5610 */
5611HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5612 const com::Utf8Str &aFlags, bool fDelete)
5613{
5614 HRESULT rc;
5615
5616 try
5617 {
5618 ComPtr<IInternalSessionControl> directControl;
5619 {
5620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5621 if (mData->mSession.mLockType == LockType_VM)
5622 directControl = mData->mSession.mDirectControl;
5623 }
5624
5625 Bstr dummy1; /* will not be changed (setter) */
5626 Bstr dummy2; /* will not be changed (setter) */
5627 LONG64 dummy64;
5628 if (!directControl)
5629 rc = E_ACCESSDENIED;
5630 else
5631 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5632 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5633 fDelete ? 2 : 1 /* accessMode */,
5634 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5635 }
5636 catch (std::bad_alloc &)
5637 {
5638 rc = E_OUTOFMEMORY;
5639 }
5640
5641 return rc;
5642}
5643#endif // VBOX_WITH_GUEST_PROPS
5644
5645HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5646 const com::Utf8Str &aFlags)
5647{
5648#ifndef VBOX_WITH_GUEST_PROPS
5649 ReturnComNotImplemented();
5650#else // VBOX_WITH_GUEST_PROPS
5651 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5652 if (rc == E_ACCESSDENIED)
5653 /* The VM is not running or the service is not (yet) accessible */
5654 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5655 return rc;
5656#endif // VBOX_WITH_GUEST_PROPS
5657}
5658
5659HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5660{
5661 return setGuestProperty(aProperty, aValue, "");
5662}
5663
5664HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5665{
5666#ifndef VBOX_WITH_GUEST_PROPS
5667 ReturnComNotImplemented();
5668#else // VBOX_WITH_GUEST_PROPS
5669 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5670 if (rc == E_ACCESSDENIED)
5671 /* The VM is not running or the service is not (yet) accessible */
5672 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5673 return rc;
5674#endif // VBOX_WITH_GUEST_PROPS
5675}
5676
5677#ifdef VBOX_WITH_GUEST_PROPS
5678/**
5679 * Enumerate the guest properties in VBoxSVC's internal structures.
5680 */
5681HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5682 std::vector<com::Utf8Str> &aNames,
5683 std::vector<com::Utf8Str> &aValues,
5684 std::vector<LONG64> &aTimestamps,
5685 std::vector<com::Utf8Str> &aFlags)
5686{
5687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5688 Utf8Str strPatterns(aPatterns);
5689
5690 /*
5691 * Look for matching patterns and build up a list.
5692 */
5693 HWData::GuestPropertyMap propMap;
5694 for (HWData::GuestPropertyMap::const_iterator
5695 it = mHWData->mGuestProperties.begin();
5696 it != mHWData->mGuestProperties.end();
5697 ++it)
5698 {
5699 if ( strPatterns.isEmpty()
5700 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5701 RTSTR_MAX,
5702 it->first.c_str(),
5703 RTSTR_MAX,
5704 NULL)
5705 )
5706 propMap.insert(*it);
5707 }
5708
5709 alock.release();
5710
5711 /*
5712 * And build up the arrays for returning the property information.
5713 */
5714 size_t cEntries = propMap.size();
5715
5716 aNames.resize(cEntries);
5717 aValues.resize(cEntries);
5718 aTimestamps.resize(cEntries);
5719 aFlags.resize(cEntries);
5720
5721 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5722 size_t i = 0;
5723 for (HWData::GuestPropertyMap::const_iterator
5724 it = propMap.begin();
5725 it != propMap.end();
5726 ++it, ++i)
5727 {
5728 aNames[i] = it->first;
5729 aValues[i] = it->second.strValue;
5730 aTimestamps[i] = it->second.mTimestamp;
5731 GuestPropWriteFlags(it->second.mFlags, szFlags);
5732 aFlags[i] = Utf8Str(szFlags);
5733 }
5734
5735 return S_OK;
5736}
5737
5738/**
5739 * Enumerate the properties managed by a VM.
5740 * @returns E_ACCESSDENIED if the VM process is not available or not
5741 * currently handling queries and the setting should then be done in
5742 * VBoxSVC.
5743 */
5744HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5745 std::vector<com::Utf8Str> &aNames,
5746 std::vector<com::Utf8Str> &aValues,
5747 std::vector<LONG64> &aTimestamps,
5748 std::vector<com::Utf8Str> &aFlags)
5749{
5750 HRESULT rc;
5751 ComPtr<IInternalSessionControl> directControl;
5752 {
5753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5754 if (mData->mSession.mLockType == LockType_VM)
5755 directControl = mData->mSession.mDirectControl;
5756 }
5757
5758 com::SafeArray<BSTR> bNames;
5759 com::SafeArray<BSTR> bValues;
5760 com::SafeArray<LONG64> bTimestamps;
5761 com::SafeArray<BSTR> bFlags;
5762
5763 if (!directControl)
5764 rc = E_ACCESSDENIED;
5765 else
5766 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5767 ComSafeArrayAsOutParam(bNames),
5768 ComSafeArrayAsOutParam(bValues),
5769 ComSafeArrayAsOutParam(bTimestamps),
5770 ComSafeArrayAsOutParam(bFlags));
5771 size_t i;
5772 aNames.resize(bNames.size());
5773 for (i = 0; i < bNames.size(); ++i)
5774 aNames[i] = Utf8Str(bNames[i]);
5775 aValues.resize(bValues.size());
5776 for (i = 0; i < bValues.size(); ++i)
5777 aValues[i] = Utf8Str(bValues[i]);
5778 aTimestamps.resize(bTimestamps.size());
5779 for (i = 0; i < bTimestamps.size(); ++i)
5780 aTimestamps[i] = bTimestamps[i];
5781 aFlags.resize(bFlags.size());
5782 for (i = 0; i < bFlags.size(); ++i)
5783 aFlags[i] = Utf8Str(bFlags[i]);
5784
5785 return rc;
5786}
5787#endif // VBOX_WITH_GUEST_PROPS
5788HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5789 std::vector<com::Utf8Str> &aNames,
5790 std::vector<com::Utf8Str> &aValues,
5791 std::vector<LONG64> &aTimestamps,
5792 std::vector<com::Utf8Str> &aFlags)
5793{
5794#ifndef VBOX_WITH_GUEST_PROPS
5795 ReturnComNotImplemented();
5796#else // VBOX_WITH_GUEST_PROPS
5797
5798 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5799
5800 if (rc == E_ACCESSDENIED)
5801 /* The VM is not running or the service is not (yet) accessible */
5802 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5803 return rc;
5804#endif // VBOX_WITH_GUEST_PROPS
5805}
5806
5807HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5808 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5809{
5810 MediumAttachmentList atts;
5811
5812 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5813 if (FAILED(rc)) return rc;
5814
5815 aMediumAttachments.resize(atts.size());
5816 size_t i = 0;
5817 for (MediumAttachmentList::const_iterator
5818 it = atts.begin();
5819 it != atts.end();
5820 ++it, ++i)
5821 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5822
5823 return S_OK;
5824}
5825
5826HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5827 LONG aControllerPort,
5828 LONG aDevice,
5829 ComPtr<IMediumAttachment> &aAttachment)
5830{
5831 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5832 aName.c_str(), aControllerPort, aDevice));
5833
5834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5835
5836 aAttachment = NULL;
5837
5838 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5839 aName,
5840 aControllerPort,
5841 aDevice);
5842 if (pAttach.isNull())
5843 return setError(VBOX_E_OBJECT_NOT_FOUND,
5844 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5845 aDevice, aControllerPort, aName.c_str());
5846
5847 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5848
5849 return S_OK;
5850}
5851
5852
5853HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5854 StorageBus_T aConnectionType,
5855 ComPtr<IStorageController> &aController)
5856{
5857 if ( (aConnectionType <= StorageBus_Null)
5858 || (aConnectionType > StorageBus_VirtioSCSI))
5859 return setError(E_INVALIDARG,
5860 tr("Invalid connection type: %d"),
5861 aConnectionType);
5862
5863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5864
5865 HRESULT rc = i_checkStateDependency(MutableStateDep);
5866 if (FAILED(rc)) return rc;
5867
5868 /* try to find one with the name first. */
5869 ComObjPtr<StorageController> ctrl;
5870
5871 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5872 if (SUCCEEDED(rc))
5873 return setError(VBOX_E_OBJECT_IN_USE,
5874 tr("Storage controller named '%s' already exists"),
5875 aName.c_str());
5876
5877 ctrl.createObject();
5878
5879 /* get a new instance number for the storage controller */
5880 ULONG ulInstance = 0;
5881 bool fBootable = true;
5882 for (StorageControllerList::const_iterator
5883 it = mStorageControllers->begin();
5884 it != mStorageControllers->end();
5885 ++it)
5886 {
5887 if ((*it)->i_getStorageBus() == aConnectionType)
5888 {
5889 ULONG ulCurInst = (*it)->i_getInstance();
5890
5891 if (ulCurInst >= ulInstance)
5892 ulInstance = ulCurInst + 1;
5893
5894 /* Only one controller of each type can be marked as bootable. */
5895 if ((*it)->i_getBootable())
5896 fBootable = false;
5897 }
5898 }
5899
5900 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5901 if (FAILED(rc)) return rc;
5902
5903 i_setModified(IsModified_Storage);
5904 mStorageControllers.backup();
5905 mStorageControllers->push_back(ctrl);
5906
5907 ctrl.queryInterfaceTo(aController.asOutParam());
5908
5909 /* inform the direct session if any */
5910 alock.release();
5911 i_onStorageControllerChange(i_getId(), aName);
5912
5913 return S_OK;
5914}
5915
5916HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5917 ComPtr<IStorageController> &aStorageController)
5918{
5919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5920
5921 ComObjPtr<StorageController> ctrl;
5922
5923 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5924 if (SUCCEEDED(rc))
5925 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5926
5927 return rc;
5928}
5929
5930HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5931 ULONG aInstance,
5932 ComPtr<IStorageController> &aStorageController)
5933{
5934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5935
5936 for (StorageControllerList::const_iterator
5937 it = mStorageControllers->begin();
5938 it != mStorageControllers->end();
5939 ++it)
5940 {
5941 if ( (*it)->i_getStorageBus() == aConnectionType
5942 && (*it)->i_getInstance() == aInstance)
5943 {
5944 (*it).queryInterfaceTo(aStorageController.asOutParam());
5945 return S_OK;
5946 }
5947 }
5948
5949 return setError(VBOX_E_OBJECT_NOT_FOUND,
5950 tr("Could not find a storage controller with instance number '%lu'"),
5951 aInstance);
5952}
5953
5954HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5955{
5956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5957
5958 HRESULT rc = i_checkStateDependency(MutableStateDep);
5959 if (FAILED(rc)) return rc;
5960
5961 ComObjPtr<StorageController> ctrl;
5962
5963 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5964 if (SUCCEEDED(rc))
5965 {
5966 /* Ensure that only one controller of each type is marked as bootable. */
5967 if (aBootable == TRUE)
5968 {
5969 for (StorageControllerList::const_iterator
5970 it = mStorageControllers->begin();
5971 it != mStorageControllers->end();
5972 ++it)
5973 {
5974 ComObjPtr<StorageController> aCtrl = (*it);
5975
5976 if ( (aCtrl->i_getName() != aName)
5977 && aCtrl->i_getBootable() == TRUE
5978 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5979 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5980 {
5981 aCtrl->i_setBootable(FALSE);
5982 break;
5983 }
5984 }
5985 }
5986
5987 if (SUCCEEDED(rc))
5988 {
5989 ctrl->i_setBootable(aBootable);
5990 i_setModified(IsModified_Storage);
5991 }
5992 }
5993
5994 if (SUCCEEDED(rc))
5995 {
5996 /* inform the direct session if any */
5997 alock.release();
5998 i_onStorageControllerChange(i_getId(), aName);
5999 }
6000
6001 return rc;
6002}
6003
6004HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6005{
6006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6007
6008 HRESULT rc = i_checkStateDependency(MutableStateDep);
6009 if (FAILED(rc)) return rc;
6010
6011 ComObjPtr<StorageController> ctrl;
6012 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6013 if (FAILED(rc)) return rc;
6014
6015 MediumAttachmentList llDetachedAttachments;
6016 {
6017 /* find all attached devices to the appropriate storage controller and detach them all */
6018 // make a temporary list because detachDevice invalidates iterators into
6019 // mMediumAttachments
6020 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6021
6022 for (MediumAttachmentList::const_iterator
6023 it = llAttachments2.begin();
6024 it != llAttachments2.end();
6025 ++it)
6026 {
6027 MediumAttachment *pAttachTemp = *it;
6028
6029 AutoCaller localAutoCaller(pAttachTemp);
6030 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6031
6032 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6033
6034 if (pAttachTemp->i_getControllerName() == aName)
6035 {
6036 llDetachedAttachments.push_back(pAttachTemp);
6037 rc = i_detachDevice(pAttachTemp, alock, NULL);
6038 if (FAILED(rc)) return rc;
6039 }
6040 }
6041 }
6042
6043 /* send event about detached devices before removing parent controller */
6044 for (MediumAttachmentList::const_iterator
6045 it = llDetachedAttachments.begin();
6046 it != llDetachedAttachments.end();
6047 ++it)
6048 {
6049 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6050 }
6051
6052 /* We can remove it now. */
6053 i_setModified(IsModified_Storage);
6054 mStorageControllers.backup();
6055
6056 ctrl->i_unshare();
6057
6058 mStorageControllers->remove(ctrl);
6059
6060 /* inform the direct session if any */
6061 alock.release();
6062 i_onStorageControllerChange(i_getId(), aName);
6063
6064 return S_OK;
6065}
6066
6067HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6068 ComPtr<IUSBController> &aController)
6069{
6070 if ( (aType <= USBControllerType_Null)
6071 || (aType >= USBControllerType_Last))
6072 return setError(E_INVALIDARG,
6073 tr("Invalid USB controller type: %d"),
6074 aType);
6075
6076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6077
6078 HRESULT rc = i_checkStateDependency(MutableStateDep);
6079 if (FAILED(rc)) return rc;
6080
6081 /* try to find one with the same type first. */
6082 ComObjPtr<USBController> ctrl;
6083
6084 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6085 if (SUCCEEDED(rc))
6086 return setError(VBOX_E_OBJECT_IN_USE,
6087 tr("USB controller named '%s' already exists"),
6088 aName.c_str());
6089
6090 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6091 ULONG maxInstances;
6092 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6093 if (FAILED(rc))
6094 return rc;
6095
6096 ULONG cInstances = i_getUSBControllerCountByType(aType);
6097 if (cInstances >= maxInstances)
6098 return setError(E_INVALIDARG,
6099 tr("Too many USB controllers of this type"));
6100
6101 ctrl.createObject();
6102
6103 rc = ctrl->init(this, aName, aType);
6104 if (FAILED(rc)) return rc;
6105
6106 i_setModified(IsModified_USB);
6107 mUSBControllers.backup();
6108 mUSBControllers->push_back(ctrl);
6109
6110 ctrl.queryInterfaceTo(aController.asOutParam());
6111
6112 /* inform the direct session if any */
6113 alock.release();
6114 i_onUSBControllerChange();
6115
6116 return S_OK;
6117}
6118
6119HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6120{
6121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6122
6123 ComObjPtr<USBController> ctrl;
6124
6125 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6126 if (SUCCEEDED(rc))
6127 ctrl.queryInterfaceTo(aController.asOutParam());
6128
6129 return rc;
6130}
6131
6132HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6133 ULONG *aControllers)
6134{
6135 if ( (aType <= USBControllerType_Null)
6136 || (aType >= USBControllerType_Last))
6137 return setError(E_INVALIDARG,
6138 tr("Invalid USB controller type: %d"),
6139 aType);
6140
6141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6142
6143 ComObjPtr<USBController> ctrl;
6144
6145 *aControllers = i_getUSBControllerCountByType(aType);
6146
6147 return S_OK;
6148}
6149
6150HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6151{
6152
6153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6154
6155 HRESULT rc = i_checkStateDependency(MutableStateDep);
6156 if (FAILED(rc)) return rc;
6157
6158 ComObjPtr<USBController> ctrl;
6159 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6160 if (FAILED(rc)) return rc;
6161
6162 i_setModified(IsModified_USB);
6163 mUSBControllers.backup();
6164
6165 ctrl->i_unshare();
6166
6167 mUSBControllers->remove(ctrl);
6168
6169 /* inform the direct session if any */
6170 alock.release();
6171 i_onUSBControllerChange();
6172
6173 return S_OK;
6174}
6175
6176HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6177 ULONG *aOriginX,
6178 ULONG *aOriginY,
6179 ULONG *aWidth,
6180 ULONG *aHeight,
6181 BOOL *aEnabled)
6182{
6183 uint32_t u32OriginX= 0;
6184 uint32_t u32OriginY= 0;
6185 uint32_t u32Width = 0;
6186 uint32_t u32Height = 0;
6187 uint16_t u16Flags = 0;
6188
6189 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6190 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6191 if (RT_FAILURE(vrc))
6192 {
6193#ifdef RT_OS_WINDOWS
6194 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6195 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6196 * So just assign fEnable to TRUE again.
6197 * The right fix would be to change GUI API wrappers to make sure that parameters
6198 * are changed only if API succeeds.
6199 */
6200 *aEnabled = TRUE;
6201#endif
6202 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6203 tr("Saved guest size is not available (%Rrc)"),
6204 vrc);
6205 }
6206
6207 *aOriginX = u32OriginX;
6208 *aOriginY = u32OriginY;
6209 *aWidth = u32Width;
6210 *aHeight = u32Height;
6211 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6212
6213 return S_OK;
6214}
6215
6216HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6217 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6218{
6219 if (aScreenId != 0)
6220 return E_NOTIMPL;
6221
6222 if ( aBitmapFormat != BitmapFormat_BGR0
6223 && aBitmapFormat != BitmapFormat_BGRA
6224 && aBitmapFormat != BitmapFormat_RGBA
6225 && aBitmapFormat != BitmapFormat_PNG)
6226 return setError(E_NOTIMPL,
6227 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6228
6229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6230
6231 uint8_t *pu8Data = NULL;
6232 uint32_t cbData = 0;
6233 uint32_t u32Width = 0;
6234 uint32_t u32Height = 0;
6235
6236 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6237
6238 if (RT_FAILURE(vrc))
6239 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6240 tr("Saved thumbnail data is not available (%Rrc)"),
6241 vrc);
6242
6243 HRESULT hr = S_OK;
6244
6245 *aWidth = u32Width;
6246 *aHeight = u32Height;
6247
6248 if (cbData > 0)
6249 {
6250 /* Convert pixels to the format expected by the API caller. */
6251 if (aBitmapFormat == BitmapFormat_BGR0)
6252 {
6253 /* [0] B, [1] G, [2] R, [3] 0. */
6254 aData.resize(cbData);
6255 memcpy(&aData.front(), pu8Data, cbData);
6256 }
6257 else if (aBitmapFormat == BitmapFormat_BGRA)
6258 {
6259 /* [0] B, [1] G, [2] R, [3] A. */
6260 aData.resize(cbData);
6261 for (uint32_t i = 0; i < cbData; i += 4)
6262 {
6263 aData[i] = pu8Data[i];
6264 aData[i + 1] = pu8Data[i + 1];
6265 aData[i + 2] = pu8Data[i + 2];
6266 aData[i + 3] = 0xff;
6267 }
6268 }
6269 else if (aBitmapFormat == BitmapFormat_RGBA)
6270 {
6271 /* [0] R, [1] G, [2] B, [3] A. */
6272 aData.resize(cbData);
6273 for (uint32_t i = 0; i < cbData; i += 4)
6274 {
6275 aData[i] = pu8Data[i + 2];
6276 aData[i + 1] = pu8Data[i + 1];
6277 aData[i + 2] = pu8Data[i];
6278 aData[i + 3] = 0xff;
6279 }
6280 }
6281 else if (aBitmapFormat == BitmapFormat_PNG)
6282 {
6283 uint8_t *pu8PNG = NULL;
6284 uint32_t cbPNG = 0;
6285 uint32_t cxPNG = 0;
6286 uint32_t cyPNG = 0;
6287
6288 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6289
6290 if (RT_SUCCESS(vrc))
6291 {
6292 aData.resize(cbPNG);
6293 if (cbPNG)
6294 memcpy(&aData.front(), pu8PNG, cbPNG);
6295 }
6296 else
6297 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6298 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6299 vrc);
6300
6301 RTMemFree(pu8PNG);
6302 }
6303 }
6304
6305 freeSavedDisplayScreenshot(pu8Data);
6306
6307 return hr;
6308}
6309
6310HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6311 ULONG *aWidth,
6312 ULONG *aHeight,
6313 std::vector<BitmapFormat_T> &aBitmapFormats)
6314{
6315 if (aScreenId != 0)
6316 return E_NOTIMPL;
6317
6318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6319
6320 uint8_t *pu8Data = NULL;
6321 uint32_t cbData = 0;
6322 uint32_t u32Width = 0;
6323 uint32_t u32Height = 0;
6324
6325 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6326
6327 if (RT_FAILURE(vrc))
6328 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6329 tr("Saved screenshot data is not available (%Rrc)"),
6330 vrc);
6331
6332 *aWidth = u32Width;
6333 *aHeight = u32Height;
6334 aBitmapFormats.resize(1);
6335 aBitmapFormats[0] = BitmapFormat_PNG;
6336
6337 freeSavedDisplayScreenshot(pu8Data);
6338
6339 return S_OK;
6340}
6341
6342HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6343 BitmapFormat_T aBitmapFormat,
6344 ULONG *aWidth,
6345 ULONG *aHeight,
6346 std::vector<BYTE> &aData)
6347{
6348 if (aScreenId != 0)
6349 return E_NOTIMPL;
6350
6351 if (aBitmapFormat != BitmapFormat_PNG)
6352 return E_NOTIMPL;
6353
6354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6355
6356 uint8_t *pu8Data = NULL;
6357 uint32_t cbData = 0;
6358 uint32_t u32Width = 0;
6359 uint32_t u32Height = 0;
6360
6361 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6362
6363 if (RT_FAILURE(vrc))
6364 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6365 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6366 vrc);
6367
6368 *aWidth = u32Width;
6369 *aHeight = u32Height;
6370
6371 aData.resize(cbData);
6372 if (cbData)
6373 memcpy(&aData.front(), pu8Data, cbData);
6374
6375 freeSavedDisplayScreenshot(pu8Data);
6376
6377 return S_OK;
6378}
6379
6380HRESULT Machine::hotPlugCPU(ULONG aCpu)
6381{
6382 HRESULT rc = S_OK;
6383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6384
6385 if (!mHWData->mCPUHotPlugEnabled)
6386 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6387
6388 if (aCpu >= mHWData->mCPUCount)
6389 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6390
6391 if (mHWData->mCPUAttached[aCpu])
6392 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6393
6394 alock.release();
6395 rc = i_onCPUChange(aCpu, false);
6396 alock.acquire();
6397 if (FAILED(rc)) return rc;
6398
6399 i_setModified(IsModified_MachineData);
6400 mHWData.backup();
6401 mHWData->mCPUAttached[aCpu] = true;
6402
6403 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6404 if (Global::IsOnline(mData->mMachineState))
6405 i_saveSettings(NULL, alock);
6406
6407 return S_OK;
6408}
6409
6410HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6411{
6412 HRESULT rc = S_OK;
6413
6414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6415
6416 if (!mHWData->mCPUHotPlugEnabled)
6417 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6418
6419 if (aCpu >= SchemaDefs::MaxCPUCount)
6420 return setError(E_INVALIDARG,
6421 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6422 SchemaDefs::MaxCPUCount);
6423
6424 if (!mHWData->mCPUAttached[aCpu])
6425 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6426
6427 /* CPU 0 can't be detached */
6428 if (aCpu == 0)
6429 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6430
6431 alock.release();
6432 rc = i_onCPUChange(aCpu, true);
6433 alock.acquire();
6434 if (FAILED(rc)) return rc;
6435
6436 i_setModified(IsModified_MachineData);
6437 mHWData.backup();
6438 mHWData->mCPUAttached[aCpu] = false;
6439
6440 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6441 if (Global::IsOnline(mData->mMachineState))
6442 i_saveSettings(NULL, alock);
6443
6444 return S_OK;
6445}
6446
6447HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6448{
6449 *aAttached = false;
6450
6451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6452
6453 /* If hotplug is enabled the CPU is always enabled. */
6454 if (!mHWData->mCPUHotPlugEnabled)
6455 {
6456 if (aCpu < mHWData->mCPUCount)
6457 *aAttached = true;
6458 }
6459 else
6460 {
6461 if (aCpu < SchemaDefs::MaxCPUCount)
6462 *aAttached = mHWData->mCPUAttached[aCpu];
6463 }
6464
6465 return S_OK;
6466}
6467
6468HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6469{
6470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6471
6472 Utf8Str log = i_getLogFilename(aIdx);
6473 if (!RTFileExists(log.c_str()))
6474 log.setNull();
6475 aFilename = log;
6476
6477 return S_OK;
6478}
6479
6480HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6481{
6482 if (aSize < 0)
6483 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6484
6485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6486
6487 HRESULT rc = S_OK;
6488 Utf8Str log = i_getLogFilename(aIdx);
6489
6490 /* do not unnecessarily hold the lock while doing something which does
6491 * not need the lock and potentially takes a long time. */
6492 alock.release();
6493
6494 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6495 * keeps the SOAP reply size under 1M for the webservice (we're using
6496 * base64 encoded strings for binary data for years now, avoiding the
6497 * expansion of each byte array element to approx. 25 bytes of XML. */
6498 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6499 aData.resize(cbData);
6500
6501 RTFILE LogFile;
6502 int vrc = RTFileOpen(&LogFile, log.c_str(),
6503 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6504 if (RT_SUCCESS(vrc))
6505 {
6506 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6507 if (RT_SUCCESS(vrc))
6508 aData.resize(cbData);
6509 else
6510 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6511 tr("Could not read log file '%s' (%Rrc)"),
6512 log.c_str(), vrc);
6513 RTFileClose(LogFile);
6514 }
6515 else
6516 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6517 tr("Could not open log file '%s' (%Rrc)"),
6518 log.c_str(), vrc);
6519
6520 if (FAILED(rc))
6521 aData.resize(0);
6522
6523 return rc;
6524}
6525
6526
6527/**
6528 * Currently this method doesn't attach device to the running VM,
6529 * just makes sure it's plugged on next VM start.
6530 */
6531HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6532{
6533 // lock scope
6534 {
6535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6536
6537 HRESULT rc = i_checkStateDependency(MutableStateDep);
6538 if (FAILED(rc)) return rc;
6539
6540 ChipsetType_T aChipset = ChipsetType_PIIX3;
6541 COMGETTER(ChipsetType)(&aChipset);
6542
6543 if (aChipset != ChipsetType_ICH9)
6544 {
6545 return setError(E_INVALIDARG,
6546 tr("Host PCI attachment only supported with ICH9 chipset"));
6547 }
6548
6549 // check if device with this host PCI address already attached
6550 for (HWData::PCIDeviceAssignmentList::const_iterator
6551 it = mHWData->mPCIDeviceAssignments.begin();
6552 it != mHWData->mPCIDeviceAssignments.end();
6553 ++it)
6554 {
6555 LONG iHostAddress = -1;
6556 ComPtr<PCIDeviceAttachment> pAttach;
6557 pAttach = *it;
6558 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6559 if (iHostAddress == aHostAddress)
6560 return setError(E_INVALIDARG,
6561 tr("Device with host PCI address already attached to this VM"));
6562 }
6563
6564 ComObjPtr<PCIDeviceAttachment> pda;
6565 char name[32];
6566
6567 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6568 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6569 pda.createObject();
6570 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6571 i_setModified(IsModified_MachineData);
6572 mHWData.backup();
6573 mHWData->mPCIDeviceAssignments.push_back(pda);
6574 }
6575
6576 return S_OK;
6577}
6578
6579/**
6580 * Currently this method doesn't detach device from the running VM,
6581 * just makes sure it's not plugged on next VM start.
6582 */
6583HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6584{
6585 ComObjPtr<PCIDeviceAttachment> pAttach;
6586 bool fRemoved = false;
6587 HRESULT rc;
6588
6589 // lock scope
6590 {
6591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6592
6593 rc = i_checkStateDependency(MutableStateDep);
6594 if (FAILED(rc)) return rc;
6595
6596 for (HWData::PCIDeviceAssignmentList::const_iterator
6597 it = mHWData->mPCIDeviceAssignments.begin();
6598 it != mHWData->mPCIDeviceAssignments.end();
6599 ++it)
6600 {
6601 LONG iHostAddress = -1;
6602 pAttach = *it;
6603 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6604 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6605 {
6606 i_setModified(IsModified_MachineData);
6607 mHWData.backup();
6608 mHWData->mPCIDeviceAssignments.remove(pAttach);
6609 fRemoved = true;
6610 break;
6611 }
6612 }
6613 }
6614
6615
6616 /* Fire event outside of the lock */
6617 if (fRemoved)
6618 {
6619 Assert(!pAttach.isNull());
6620 ComPtr<IEventSource> es;
6621 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6622 Assert(SUCCEEDED(rc));
6623 Bstr mid;
6624 rc = this->COMGETTER(Id)(mid.asOutParam());
6625 Assert(SUCCEEDED(rc));
6626 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6627 }
6628
6629 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6630 tr("No host PCI device %08x attached"),
6631 aHostAddress
6632 );
6633}
6634
6635HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6636{
6637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6638
6639 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6640 size_t i = 0;
6641 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6642 it = mHWData->mPCIDeviceAssignments.begin();
6643 it != mHWData->mPCIDeviceAssignments.end();
6644 ++it, ++i)
6645 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6646
6647 return S_OK;
6648}
6649
6650HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6651{
6652 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6653
6654 return S_OK;
6655}
6656
6657HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6658{
6659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6660
6661 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6662
6663 return S_OK;
6664}
6665
6666HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6667{
6668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6669 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6670 if (SUCCEEDED(hrc))
6671 {
6672 hrc = mHWData.backupEx();
6673 if (SUCCEEDED(hrc))
6674 {
6675 i_setModified(IsModified_MachineData);
6676 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6677 }
6678 }
6679 return hrc;
6680}
6681
6682HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6683{
6684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6685 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6686 return S_OK;
6687}
6688
6689HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6690{
6691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6692 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6693 if (SUCCEEDED(hrc))
6694 {
6695 hrc = mHWData.backupEx();
6696 if (SUCCEEDED(hrc))
6697 {
6698 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6699 if (SUCCEEDED(hrc))
6700 i_setModified(IsModified_MachineData);
6701 }
6702 }
6703 return hrc;
6704}
6705
6706HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6707{
6708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6709
6710 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6711
6712 return S_OK;
6713}
6714
6715HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6716{
6717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6718 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6719 if (SUCCEEDED(hrc))
6720 {
6721 hrc = mHWData.backupEx();
6722 if (SUCCEEDED(hrc))
6723 {
6724 i_setModified(IsModified_MachineData);
6725 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6726 }
6727 }
6728 return hrc;
6729}
6730
6731HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6732{
6733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6734
6735 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6736
6737 return S_OK;
6738}
6739
6740HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6741{
6742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6743
6744 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6745 if ( SUCCEEDED(hrc)
6746 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6747 {
6748 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6749 int vrc;
6750
6751 if (aAutostartEnabled)
6752 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6753 else
6754 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6755
6756 if (RT_SUCCESS(vrc))
6757 {
6758 hrc = mHWData.backupEx();
6759 if (SUCCEEDED(hrc))
6760 {
6761 i_setModified(IsModified_MachineData);
6762 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6763 }
6764 }
6765 else if (vrc == VERR_NOT_SUPPORTED)
6766 hrc = setError(VBOX_E_NOT_SUPPORTED,
6767 tr("The VM autostart feature is not supported on this platform"));
6768 else if (vrc == VERR_PATH_NOT_FOUND)
6769 hrc = setError(E_FAIL,
6770 tr("The path to the autostart database is not set"));
6771 else
6772 hrc = setError(E_UNEXPECTED,
6773 aAutostartEnabled ?
6774 tr("Adding machine '%s' to the autostart database failed with %Rrc") :
6775 tr("Removing machine '%s' from the autostart database failed with %Rrc"),
6776 mUserData->s.strName.c_str(), vrc);
6777 }
6778 return hrc;
6779}
6780
6781HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6782{
6783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6784
6785 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6786
6787 return S_OK;
6788}
6789
6790HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6791{
6792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6793 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6794 if (SUCCEEDED(hrc))
6795 {
6796 hrc = mHWData.backupEx();
6797 if (SUCCEEDED(hrc))
6798 {
6799 i_setModified(IsModified_MachineData);
6800 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6801 }
6802 }
6803 return hrc;
6804}
6805
6806HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6807{
6808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6809
6810 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6811
6812 return S_OK;
6813}
6814
6815HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6816{
6817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6818 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6819 if ( SUCCEEDED(hrc)
6820 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6821 {
6822 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6823 int vrc;
6824
6825 if (aAutostopType != AutostopType_Disabled)
6826 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6827 else
6828 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6829
6830 if (RT_SUCCESS(vrc))
6831 {
6832 hrc = mHWData.backupEx();
6833 if (SUCCEEDED(hrc))
6834 {
6835 i_setModified(IsModified_MachineData);
6836 mHWData->mAutostart.enmAutostopType = aAutostopType;
6837 }
6838 }
6839 else if (vrc == VERR_NOT_SUPPORTED)
6840 hrc = setError(VBOX_E_NOT_SUPPORTED,
6841 tr("The VM autostop feature is not supported on this platform"));
6842 else if (vrc == VERR_PATH_NOT_FOUND)
6843 hrc = setError(E_FAIL,
6844 tr("The path to the autostart database is not set"));
6845 else
6846 hrc = setError(E_UNEXPECTED,
6847 aAutostopType != AutostopType_Disabled ?
6848 tr("Adding machine '%s' to the autostop database failed with %Rrc") :
6849 tr("Removing machine '%s' from the autostop database failed with %Rrc"),
6850 mUserData->s.strName.c_str(), vrc);
6851 }
6852 return hrc;
6853}
6854
6855HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6856{
6857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6858
6859 aDefaultFrontend = mHWData->mDefaultFrontend;
6860
6861 return S_OK;
6862}
6863
6864HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6865{
6866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6867 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6868 if (SUCCEEDED(hrc))
6869 {
6870 hrc = mHWData.backupEx();
6871 if (SUCCEEDED(hrc))
6872 {
6873 i_setModified(IsModified_MachineData);
6874 mHWData->mDefaultFrontend = aDefaultFrontend;
6875 }
6876 }
6877 return hrc;
6878}
6879
6880HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6881{
6882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6883 size_t cbIcon = mUserData->s.ovIcon.size();
6884 aIcon.resize(cbIcon);
6885 if (cbIcon)
6886 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6887 return S_OK;
6888}
6889
6890HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6891{
6892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6893 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6894 if (SUCCEEDED(hrc))
6895 {
6896 i_setModified(IsModified_MachineData);
6897 mUserData.backup();
6898 size_t cbIcon = aIcon.size();
6899 mUserData->s.ovIcon.resize(cbIcon);
6900 if (cbIcon)
6901 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6902 }
6903 return hrc;
6904}
6905
6906HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6907{
6908#ifdef VBOX_WITH_USB
6909 *aUSBProxyAvailable = true;
6910#else
6911 *aUSBProxyAvailable = false;
6912#endif
6913 return S_OK;
6914}
6915
6916HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6917{
6918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6919
6920 *aVMProcessPriority = mUserData->s.enmVMPriority;
6921
6922 return S_OK;
6923}
6924
6925HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6926{
6927 RT_NOREF(aVMProcessPriority);
6928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6929 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6930 if (SUCCEEDED(hrc))
6931 {
6932 hrc = mUserData.backupEx();
6933 if (SUCCEEDED(hrc))
6934 {
6935 i_setModified(IsModified_MachineData);
6936 mUserData->s.enmVMPriority = aVMProcessPriority;
6937 }
6938 }
6939 alock.release();
6940 if (SUCCEEDED(hrc))
6941 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6942 return hrc;
6943}
6944
6945HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6946 ComPtr<IProgress> &aProgress)
6947{
6948 ComObjPtr<Progress> pP;
6949 Progress *ppP = pP;
6950 IProgress *iP = static_cast<IProgress *>(ppP);
6951 IProgress **pProgress = &iP;
6952
6953 IMachine *pTarget = aTarget;
6954
6955 /* Convert the options. */
6956 RTCList<CloneOptions_T> optList;
6957 if (aOptions.size())
6958 for (size_t i = 0; i < aOptions.size(); ++i)
6959 optList.append(aOptions[i]);
6960
6961 if (optList.contains(CloneOptions_Link))
6962 {
6963 if (!i_isSnapshotMachine())
6964 return setError(E_INVALIDARG,
6965 tr("Linked clone can only be created from a snapshot"));
6966 if (aMode != CloneMode_MachineState)
6967 return setError(E_INVALIDARG,
6968 tr("Linked clone can only be created for a single machine state"));
6969 }
6970 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6971
6972 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6973
6974 HRESULT rc = pWorker->start(pProgress);
6975
6976 pP = static_cast<Progress *>(*pProgress);
6977 pP.queryInterfaceTo(aProgress.asOutParam());
6978
6979 return rc;
6980
6981}
6982
6983HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6984 const com::Utf8Str &aType,
6985 ComPtr<IProgress> &aProgress)
6986{
6987 LogFlowThisFuncEnter();
6988
6989 ComObjPtr<Progress> ptrProgress;
6990 HRESULT hrc = ptrProgress.createObject();
6991 if (SUCCEEDED(hrc))
6992 {
6993 com::Utf8Str strDefaultPath;
6994 if (aTargetPath.isEmpty())
6995 i_calculateFullPath(".", strDefaultPath);
6996
6997 /* Initialize our worker task */
6998 MachineMoveVM *pTask = NULL;
6999 try
7000 {
7001 pTask = new MachineMoveVM(this, aTargetPath.isEmpty() ? strDefaultPath : aTargetPath, aType, ptrProgress);
7002 }
7003 catch (std::bad_alloc &)
7004 {
7005 return E_OUTOFMEMORY;
7006 }
7007
7008 hrc = pTask->init();//no exceptions are thrown
7009
7010 if (SUCCEEDED(hrc))
7011 {
7012 hrc = pTask->createThread();
7013 pTask = NULL; /* Consumed by createThread(). */
7014 if (SUCCEEDED(hrc))
7015 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7016 else
7017 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7018 }
7019 else
7020 delete pTask;
7021 }
7022
7023 LogFlowThisFuncLeave();
7024 return hrc;
7025
7026}
7027
7028HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7029{
7030 NOREF(aProgress);
7031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7032
7033 // This check should always fail.
7034 HRESULT rc = i_checkStateDependency(MutableStateDep);
7035 if (FAILED(rc)) return rc;
7036
7037 AssertFailedReturn(E_NOTIMPL);
7038}
7039
7040HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7041{
7042 NOREF(aSavedStateFile);
7043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7044
7045 // This check should always fail.
7046 HRESULT rc = i_checkStateDependency(MutableStateDep);
7047 if (FAILED(rc)) return rc;
7048
7049 AssertFailedReturn(E_NOTIMPL);
7050}
7051
7052HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7053{
7054 NOREF(aFRemoveFile);
7055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7056
7057 // This check should always fail.
7058 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7059 if (FAILED(rc)) return rc;
7060
7061 AssertFailedReturn(E_NOTIMPL);
7062}
7063
7064// public methods for internal purposes
7065/////////////////////////////////////////////////////////////////////////////
7066
7067/**
7068 * Adds the given IsModified_* flag to the dirty flags of the machine.
7069 * This must be called either during i_loadSettings or under the machine write lock.
7070 * @param fl Flag
7071 * @param fAllowStateModification If state modifications are allowed.
7072 */
7073void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7074{
7075 mData->flModifications |= fl;
7076 if (fAllowStateModification && i_isStateModificationAllowed())
7077 mData->mCurrentStateModified = true;
7078}
7079
7080/**
7081 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7082 * care of the write locking.
7083 *
7084 * @param fModification The flag to add.
7085 * @param fAllowStateModification If state modifications are allowed.
7086 */
7087void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7088{
7089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7090 i_setModified(fModification, fAllowStateModification);
7091}
7092
7093/**
7094 * Saves the registry entry of this machine to the given configuration node.
7095 *
7096 * @param data Machine registry data.
7097 *
7098 * @note locks this object for reading.
7099 */
7100HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7101{
7102 AutoLimitedCaller autoCaller(this);
7103 AssertComRCReturnRC(autoCaller.rc());
7104
7105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7106
7107 data.uuid = mData->mUuid;
7108 data.strSettingsFile = mData->m_strConfigFile;
7109
7110 return S_OK;
7111}
7112
7113/**
7114 * Calculates the absolute path of the given path taking the directory of the
7115 * machine settings file as the current directory.
7116 *
7117 * @param strPath Path to calculate the absolute path for.
7118 * @param aResult Where to put the result (used only on success, can be the
7119 * same Utf8Str instance as passed in @a aPath).
7120 * @return IPRT result.
7121 *
7122 * @note Locks this object for reading.
7123 */
7124int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7125{
7126 AutoCaller autoCaller(this);
7127 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7128
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7132
7133 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7134
7135 strSettingsDir.stripFilename();
7136 char szFolder[RTPATH_MAX];
7137 size_t cbFolder = sizeof(szFolder);
7138 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7139 if (RT_SUCCESS(vrc))
7140 aResult = szFolder;
7141
7142 return vrc;
7143}
7144
7145/**
7146 * Copies strSource to strTarget, making it relative to the machine folder
7147 * if it is a subdirectory thereof, or simply copying it otherwise.
7148 *
7149 * @param strSource Path to evaluate and copy.
7150 * @param strTarget Buffer to receive target path.
7151 *
7152 * @note Locks this object for reading.
7153 */
7154void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7155 Utf8Str &strTarget)
7156{
7157 AutoCaller autoCaller(this);
7158 AssertComRCReturn(autoCaller.rc(), (void)0);
7159
7160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7161
7162 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7163 // use strTarget as a temporary buffer to hold the machine settings dir
7164 strTarget = mData->m_strConfigFileFull;
7165 strTarget.stripFilename();
7166 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7167 {
7168 // is relative: then append what's left
7169 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7170 // for empty paths (only possible for subdirs) use "." to avoid
7171 // triggering default settings for not present config attributes.
7172 if (strTarget.isEmpty())
7173 strTarget = ".";
7174 }
7175 else
7176 // is not relative: then overwrite
7177 strTarget = strSource;
7178}
7179
7180/**
7181 * Returns the full path to the machine's log folder in the
7182 * \a aLogFolder argument.
7183 */
7184void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7185{
7186 AutoCaller autoCaller(this);
7187 AssertComRCReturnVoid(autoCaller.rc());
7188
7189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7190
7191 char szTmp[RTPATH_MAX];
7192 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7193 if (RT_SUCCESS(vrc))
7194 {
7195 if (szTmp[0] && !mUserData.isNull())
7196 {
7197 char szTmp2[RTPATH_MAX];
7198 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7199 if (RT_SUCCESS(vrc))
7200 aLogFolder.printf("%s%c%s",
7201 szTmp2,
7202 RTPATH_DELIMITER,
7203 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7204 }
7205 else
7206 vrc = VERR_PATH_IS_RELATIVE;
7207 }
7208
7209 if (RT_FAILURE(vrc))
7210 {
7211 // fallback if VBOX_USER_LOGHOME is not set or invalid
7212 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7213 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7214 aLogFolder.append(RTPATH_DELIMITER);
7215 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7216 }
7217}
7218
7219/**
7220 * Returns the full path to the machine's log file for an given index.
7221 */
7222Utf8Str Machine::i_getLogFilename(ULONG idx)
7223{
7224 Utf8Str logFolder;
7225 getLogFolder(logFolder);
7226 Assert(logFolder.length());
7227
7228 Utf8Str log;
7229 if (idx == 0)
7230 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7231#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7232 else if (idx == 1)
7233 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7234 else
7235 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7236#else
7237 else
7238 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7239#endif
7240 return log;
7241}
7242
7243/**
7244 * Returns the full path to the machine's hardened log file.
7245 */
7246Utf8Str Machine::i_getHardeningLogFilename(void)
7247{
7248 Utf8Str strFilename;
7249 getLogFolder(strFilename);
7250 Assert(strFilename.length());
7251 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7252 return strFilename;
7253}
7254
7255/**
7256 * Returns the default NVRAM filename based on the location of the VM config.
7257 * Note that this is a relative path.
7258 */
7259Utf8Str Machine::i_getDefaultNVRAMFilename()
7260{
7261 AutoCaller autoCaller(this);
7262 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7263
7264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7265
7266 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7267 || i_isSnapshotMachine())
7268 return Utf8Str::Empty;
7269
7270 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7271 strNVRAMFilePath.stripPath();
7272 strNVRAMFilePath.stripSuffix();
7273 strNVRAMFilePath += ".nvram";
7274
7275 return strNVRAMFilePath;
7276}
7277
7278/**
7279 * Returns the NVRAM filename for a new snapshot. This intentionally works
7280 * similarly to the saved state file naming. Note that this is usually
7281 * a relative path, unless the snapshot folder is absolute.
7282 */
7283Utf8Str Machine::i_getSnapshotNVRAMFilename()
7284{
7285 AutoCaller autoCaller(this);
7286 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7287
7288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7289
7290 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7291 return Utf8Str::Empty;
7292
7293 RTTIMESPEC ts;
7294 RTTimeNow(&ts);
7295 RTTIME time;
7296 RTTimeExplode(&time, &ts);
7297
7298 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7299 strNVRAMFilePath += RTPATH_DELIMITER;
7300 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7301 time.i32Year, time.u8Month, time.u8MonthDay,
7302 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7303
7304 return strNVRAMFilePath;
7305}
7306
7307/**
7308 * Returns the version of the settings file.
7309 */
7310SettingsVersion_T Machine::i_getSettingsVersion(void)
7311{
7312 AutoCaller autoCaller(this);
7313 AssertComRCReturn(autoCaller.rc(), SettingsVersion_Null);
7314
7315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7316
7317 return mData->pMachineConfigFile->getSettingsVersion();
7318}
7319
7320/**
7321 * Composes a unique saved state filename based on the current system time. The filename is
7322 * granular to the second so this will work so long as no more than one snapshot is taken on
7323 * a machine per second.
7324 *
7325 * Before version 4.1, we used this formula for saved state files:
7326 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7327 * which no longer works because saved state files can now be shared between the saved state of the
7328 * "saved" machine and an online snapshot, and the following would cause problems:
7329 * 1) save machine
7330 * 2) create online snapshot from that machine state --> reusing saved state file
7331 * 3) save machine again --> filename would be reused, breaking the online snapshot
7332 *
7333 * So instead we now use a timestamp.
7334 *
7335 * @param strStateFilePath
7336 */
7337
7338void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7339{
7340 AutoCaller autoCaller(this);
7341 AssertComRCReturnVoid(autoCaller.rc());
7342
7343 {
7344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7345 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7346 }
7347
7348 RTTIMESPEC ts;
7349 RTTimeNow(&ts);
7350 RTTIME time;
7351 RTTimeExplode(&time, &ts);
7352
7353 strStateFilePath += RTPATH_DELIMITER;
7354 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7355 time.i32Year, time.u8Month, time.u8MonthDay,
7356 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7357}
7358
7359/**
7360 * Returns whether at least one USB controller is present for the VM.
7361 */
7362bool Machine::i_isUSBControllerPresent()
7363{
7364 AutoCaller autoCaller(this);
7365 AssertComRCReturn(autoCaller.rc(), false);
7366
7367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7368
7369 return (mUSBControllers->size() > 0);
7370}
7371
7372
7373/**
7374 * @note Locks this object for writing, calls the client process
7375 * (inside the lock).
7376 */
7377HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7378 const Utf8Str &strFrontend,
7379 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7380 ProgressProxy *aProgress)
7381{
7382 LogFlowThisFuncEnter();
7383
7384 AssertReturn(aControl, E_FAIL);
7385 AssertReturn(aProgress, E_FAIL);
7386 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7387
7388 AutoCaller autoCaller(this);
7389 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7390
7391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7392
7393 if (!mData->mRegistered)
7394 return setError(E_UNEXPECTED,
7395 tr("The machine '%s' is not registered"),
7396 mUserData->s.strName.c_str());
7397
7398 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7399
7400 /* The process started when launching a VM with separate UI/VM processes is always
7401 * the UI process, i.e. needs special handling as it won't claim the session. */
7402 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7403
7404 if (fSeparate)
7405 {
7406 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7407 return setError(VBOX_E_INVALID_OBJECT_STATE,
7408 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7409 mUserData->s.strName.c_str());
7410 }
7411 else
7412 {
7413 if ( mData->mSession.mState == SessionState_Locked
7414 || mData->mSession.mState == SessionState_Spawning
7415 || mData->mSession.mState == SessionState_Unlocking)
7416 return setError(VBOX_E_INVALID_OBJECT_STATE,
7417 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7418 mUserData->s.strName.c_str());
7419
7420 /* may not be busy */
7421 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7422 }
7423
7424 /* Hardening logging */
7425#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7426 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7427 {
7428 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7429 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7430 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7431 {
7432 Utf8Str strStartupLogDir = strHardeningLogFile;
7433 strStartupLogDir.stripFilename();
7434 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7435 file without stripping the file. */
7436 }
7437 strSupHardeningLogArg.append(strHardeningLogFile);
7438
7439 /* Remove legacy log filename to avoid confusion. */
7440 Utf8Str strOldStartupLogFile;
7441 getLogFolder(strOldStartupLogFile);
7442 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7443 RTFileDelete(strOldStartupLogFile.c_str());
7444 }
7445#else
7446 Utf8Str strSupHardeningLogArg;
7447#endif
7448
7449 Utf8Str strAppOverride;
7450#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7451 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7452#endif
7453
7454 bool fUseVBoxSDS = false;
7455 Utf8Str strCanonicalName;
7456 if (false)
7457 { }
7458#ifdef VBOX_WITH_QTGUI
7459 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7460 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7461 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7462 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7463 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7464 {
7465 strCanonicalName = "GUI/Qt";
7466 fUseVBoxSDS = true;
7467 }
7468#endif
7469#ifdef VBOX_WITH_VBOXSDL
7470 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7471 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7472 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7473 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7474 {
7475 strCanonicalName = "GUI/SDL";
7476 fUseVBoxSDS = true;
7477 }
7478#endif
7479#ifdef VBOX_WITH_HEADLESS
7480 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7481 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7482 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7483 {
7484 strCanonicalName = "headless";
7485 }
7486#endif
7487 else
7488 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7489
7490 Utf8Str idStr = mData->mUuid.toString();
7491 Utf8Str const &strMachineName = mUserData->s.strName;
7492 RTPROCESS pid = NIL_RTPROCESS;
7493
7494#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7495 RT_NOREF(fUseVBoxSDS);
7496#else
7497 DWORD idCallerSession = ~(DWORD)0;
7498 if (fUseVBoxSDS)
7499 {
7500 /*
7501 * The VBoxSDS should be used for process launching the VM with
7502 * GUI only if the caller and the VBoxSDS are in different Windows
7503 * sessions and the caller in the interactive one.
7504 */
7505 fUseVBoxSDS = false;
7506
7507 /* Get windows session of the current process. The process token used
7508 due to several reasons:
7509 1. The token is absent for the current thread except someone set it
7510 for us.
7511 2. Needs to get the id of the session where the process is started.
7512 We only need to do this once, though. */
7513 static DWORD s_idCurrentSession = ~(DWORD)0;
7514 DWORD idCurrentSession = s_idCurrentSession;
7515 if (idCurrentSession == ~(DWORD)0)
7516 {
7517 HANDLE hCurrentProcessToken = NULL;
7518 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7519 {
7520 DWORD cbIgn = 0;
7521 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7522 s_idCurrentSession = idCurrentSession;
7523 else
7524 {
7525 idCurrentSession = ~(DWORD)0;
7526 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7527 }
7528 CloseHandle(hCurrentProcessToken);
7529 }
7530 else
7531 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7532 }
7533
7534 /* get the caller's session */
7535 HRESULT hrc = CoImpersonateClient();
7536 if (SUCCEEDED(hrc))
7537 {
7538 HANDLE hCallerThreadToken;
7539 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7540 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7541 &hCallerThreadToken))
7542 {
7543 SetLastError(NO_ERROR);
7544 DWORD cbIgn = 0;
7545 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7546 {
7547 /* Only need to use SDS if the session ID differs: */
7548 if (idCurrentSession != idCallerSession)
7549 {
7550 fUseVBoxSDS = false;
7551
7552 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7553 DWORD cbTokenGroups = 0;
7554 PTOKEN_GROUPS pTokenGroups = NULL;
7555 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7556 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7557 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7558 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7559 {
7560 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7561 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7562 PSID pInteractiveSid = NULL;
7563 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7564 {
7565 /* Iterate over the groups looking for the interactive SID: */
7566 fUseVBoxSDS = false;
7567 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7568 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7569 {
7570 fUseVBoxSDS = true;
7571 break;
7572 }
7573 FreeSid(pInteractiveSid);
7574 }
7575 }
7576 else
7577 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7578 RTMemTmpFree(pTokenGroups);
7579 }
7580 }
7581 else
7582 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7583 CloseHandle(hCallerThreadToken);
7584 }
7585 else
7586 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7587 CoRevertToSelf();
7588 }
7589 else
7590 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7591 }
7592 if (fUseVBoxSDS)
7593 {
7594 /* connect to VBoxSDS */
7595 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7596 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7597 if (FAILED(rc))
7598 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7599 strMachineName.c_str());
7600
7601 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7602 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7603 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7604 service to access the files. */
7605 rc = CoSetProxyBlanket(pVBoxSDS,
7606 RPC_C_AUTHN_DEFAULT,
7607 RPC_C_AUTHZ_DEFAULT,
7608 COLE_DEFAULT_PRINCIPAL,
7609 RPC_C_AUTHN_LEVEL_DEFAULT,
7610 RPC_C_IMP_LEVEL_IMPERSONATE,
7611 NULL,
7612 EOAC_DEFAULT);
7613 if (FAILED(rc))
7614 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7615
7616 size_t const cEnvVars = aEnvironmentChanges.size();
7617 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7618 for (size_t i = 0; i < cEnvVars; i++)
7619 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7620
7621 ULONG uPid = 0;
7622 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7623 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7624 idCallerSession, &uPid);
7625 if (FAILED(rc))
7626 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7627 pid = (RTPROCESS)uPid;
7628 }
7629 else
7630#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7631 {
7632 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7633 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7634 if (RT_FAILURE(vrc))
7635 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7636 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7637 }
7638
7639 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7640 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7641
7642 if (!fSeparate)
7643 {
7644 /*
7645 * Note that we don't release the lock here before calling the client,
7646 * because it doesn't need to call us back if called with a NULL argument.
7647 * Releasing the lock here is dangerous because we didn't prepare the
7648 * launch data yet, but the client we've just started may happen to be
7649 * too fast and call LockMachine() that will fail (because of PID, etc.),
7650 * so that the Machine will never get out of the Spawning session state.
7651 */
7652
7653 /* inform the session that it will be a remote one */
7654 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7655#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7656 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7657#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7658 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7659#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7660 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7661
7662 if (FAILED(rc))
7663 {
7664 /* restore the session state */
7665 mData->mSession.mState = SessionState_Unlocked;
7666 alock.release();
7667 mParent->i_addProcessToReap(pid);
7668 /* The failure may occur w/o any error info (from RPC), so provide one */
7669 return setError(VBOX_E_VM_ERROR,
7670 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7671 }
7672
7673 /* attach launch data to the machine */
7674 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7675 mData->mSession.mRemoteControls.push_back(aControl);
7676 mData->mSession.mProgress = aProgress;
7677 mData->mSession.mPID = pid;
7678 mData->mSession.mState = SessionState_Spawning;
7679 Assert(strCanonicalName.isNotEmpty());
7680 mData->mSession.mName = strCanonicalName;
7681 }
7682 else
7683 {
7684 /* For separate UI process we declare the launch as completed instantly, as the
7685 * actual headless VM start may or may not come. No point in remembering anything
7686 * yet, as what matters for us is when the headless VM gets started. */
7687 aProgress->i_notifyComplete(S_OK);
7688 }
7689
7690 alock.release();
7691 mParent->i_addProcessToReap(pid);
7692
7693 LogFlowThisFuncLeave();
7694 return S_OK;
7695}
7696
7697/**
7698 * Returns @c true if the given session machine instance has an open direct
7699 * session (and optionally also for direct sessions which are closing) and
7700 * returns the session control machine instance if so.
7701 *
7702 * Note that when the method returns @c false, the arguments remain unchanged.
7703 *
7704 * @param aMachine Session machine object.
7705 * @param aControl Direct session control object (optional).
7706 * @param aRequireVM If true then only allow VM sessions.
7707 * @param aAllowClosing If true then additionally a session which is currently
7708 * being closed will also be allowed.
7709 *
7710 * @note locks this object for reading.
7711 */
7712bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7713 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7714 bool aRequireVM /*= false*/,
7715 bool aAllowClosing /*= false*/)
7716{
7717 AutoLimitedCaller autoCaller(this);
7718 AssertComRCReturn(autoCaller.rc(), false);
7719
7720 /* just return false for inaccessible machines */
7721 if (getObjectState().getState() != ObjectState::Ready)
7722 return false;
7723
7724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7725
7726 if ( ( mData->mSession.mState == SessionState_Locked
7727 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7728 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7729 )
7730 {
7731 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7732
7733 aMachine = mData->mSession.mMachine;
7734
7735 if (aControl != NULL)
7736 *aControl = mData->mSession.mDirectControl;
7737
7738 return true;
7739 }
7740
7741 return false;
7742}
7743
7744/**
7745 * Returns @c true if the given machine has an spawning direct session.
7746 *
7747 * @note locks this object for reading.
7748 */
7749bool Machine::i_isSessionSpawning()
7750{
7751 AutoLimitedCaller autoCaller(this);
7752 AssertComRCReturn(autoCaller.rc(), false);
7753
7754 /* just return false for inaccessible machines */
7755 if (getObjectState().getState() != ObjectState::Ready)
7756 return false;
7757
7758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7759
7760 if (mData->mSession.mState == SessionState_Spawning)
7761 return true;
7762
7763 return false;
7764}
7765
7766/**
7767 * Called from the client watcher thread to check for unexpected client process
7768 * death during Session_Spawning state (e.g. before it successfully opened a
7769 * direct session).
7770 *
7771 * On Win32 and on OS/2, this method is called only when we've got the
7772 * direct client's process termination notification, so it always returns @c
7773 * true.
7774 *
7775 * On other platforms, this method returns @c true if the client process is
7776 * terminated and @c false if it's still alive.
7777 *
7778 * @note Locks this object for writing.
7779 */
7780bool Machine::i_checkForSpawnFailure()
7781{
7782 AutoCaller autoCaller(this);
7783 if (!autoCaller.isOk())
7784 {
7785 /* nothing to do */
7786 LogFlowThisFunc(("Already uninitialized!\n"));
7787 return true;
7788 }
7789
7790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7791
7792 if (mData->mSession.mState != SessionState_Spawning)
7793 {
7794 /* nothing to do */
7795 LogFlowThisFunc(("Not spawning any more!\n"));
7796 return true;
7797 }
7798
7799 HRESULT rc = S_OK;
7800
7801 /* PID not yet initialized, skip check. */
7802 if (mData->mSession.mPID == NIL_RTPROCESS)
7803 return false;
7804
7805 RTPROCSTATUS status;
7806 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7807
7808 if (vrc != VERR_PROCESS_RUNNING)
7809 {
7810 Utf8Str strExtraInfo;
7811
7812#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7813 /* If the startup logfile exists and is of non-zero length, tell the
7814 user to look there for more details to encourage them to attach it
7815 when reporting startup issues. */
7816 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7817 uint64_t cbStartupLogFile = 0;
7818 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7819 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7820 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7821#endif
7822
7823 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7824 rc = setError(E_FAIL,
7825 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7826 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7827 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7828 rc = setError(E_FAIL,
7829 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7830 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7831 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7832 rc = setError(E_FAIL,
7833 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7834 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7835 else
7836 rc = setErrorBoth(E_FAIL, vrc,
7837 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7838 i_getName().c_str(), vrc, strExtraInfo.c_str());
7839 }
7840
7841 if (FAILED(rc))
7842 {
7843 /* Close the remote session, remove the remote control from the list
7844 * and reset session state to Closed (@note keep the code in sync with
7845 * the relevant part in LockMachine()). */
7846
7847 Assert(mData->mSession.mRemoteControls.size() == 1);
7848 if (mData->mSession.mRemoteControls.size() == 1)
7849 {
7850 ErrorInfoKeeper eik;
7851 mData->mSession.mRemoteControls.front()->Uninitialize();
7852 }
7853
7854 mData->mSession.mRemoteControls.clear();
7855 mData->mSession.mState = SessionState_Unlocked;
7856
7857 /* finalize the progress after setting the state */
7858 if (!mData->mSession.mProgress.isNull())
7859 {
7860 mData->mSession.mProgress->notifyComplete(rc);
7861 mData->mSession.mProgress.setNull();
7862 }
7863
7864 mData->mSession.mPID = NIL_RTPROCESS;
7865
7866 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7867 return true;
7868 }
7869
7870 return false;
7871}
7872
7873/**
7874 * Checks whether the machine can be registered. If so, commits and saves
7875 * all settings.
7876 *
7877 * @note Must be called from mParent's write lock. Locks this object and
7878 * children for writing.
7879 */
7880HRESULT Machine::i_prepareRegister()
7881{
7882 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7883
7884 AutoLimitedCaller autoCaller(this);
7885 AssertComRCReturnRC(autoCaller.rc());
7886
7887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7888
7889 /* wait for state dependents to drop to zero */
7890 i_ensureNoStateDependencies(alock);
7891
7892 if (!mData->mAccessible)
7893 return setError(VBOX_E_INVALID_OBJECT_STATE,
7894 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7895 mUserData->s.strName.c_str(),
7896 mData->mUuid.toString().c_str());
7897
7898 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7899
7900 if (mData->mRegistered)
7901 return setError(VBOX_E_INVALID_OBJECT_STATE,
7902 tr("The machine '%s' with UUID {%s} is already registered"),
7903 mUserData->s.strName.c_str(),
7904 mData->mUuid.toString().c_str());
7905
7906 HRESULT rc = S_OK;
7907
7908 // Ensure the settings are saved. If we are going to be registered and
7909 // no config file exists yet, create it by calling i_saveSettings() too.
7910 if ( (mData->flModifications)
7911 || (!mData->pMachineConfigFile->fileExists())
7912 )
7913 {
7914 rc = i_saveSettings(NULL, alock);
7915 // no need to check whether VirtualBox.xml needs saving too since
7916 // we can't have a machine XML file rename pending
7917 if (FAILED(rc)) return rc;
7918 }
7919
7920 /* more config checking goes here */
7921
7922 if (SUCCEEDED(rc))
7923 {
7924 /* we may have had implicit modifications we want to fix on success */
7925 i_commit();
7926
7927 mData->mRegistered = true;
7928 }
7929 else
7930 {
7931 /* we may have had implicit modifications we want to cancel on failure*/
7932 i_rollback(false /* aNotify */);
7933 }
7934
7935 return rc;
7936}
7937
7938/**
7939 * Increases the number of objects dependent on the machine state or on the
7940 * registered state. Guarantees that these two states will not change at least
7941 * until #i_releaseStateDependency() is called.
7942 *
7943 * Depending on the @a aDepType value, additional state checks may be made.
7944 * These checks will set extended error info on failure. See
7945 * #i_checkStateDependency() for more info.
7946 *
7947 * If this method returns a failure, the dependency is not added and the caller
7948 * is not allowed to rely on any particular machine state or registration state
7949 * value and may return the failed result code to the upper level.
7950 *
7951 * @param aDepType Dependency type to add.
7952 * @param aState Current machine state (NULL if not interested).
7953 * @param aRegistered Current registered state (NULL if not interested).
7954 *
7955 * @note Locks this object for writing.
7956 */
7957HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7958 MachineState_T *aState /* = NULL */,
7959 BOOL *aRegistered /* = NULL */)
7960{
7961 AutoCaller autoCaller(this);
7962 AssertComRCReturnRC(autoCaller.rc());
7963
7964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7965
7966 HRESULT rc = i_checkStateDependency(aDepType);
7967 if (FAILED(rc)) return rc;
7968
7969 {
7970 if (mData->mMachineStateChangePending != 0)
7971 {
7972 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7973 * drop to zero so don't add more. It may make sense to wait a bit
7974 * and retry before reporting an error (since the pending state
7975 * transition should be really quick) but let's just assert for
7976 * now to see if it ever happens on practice. */
7977
7978 AssertFailed();
7979
7980 return setError(E_ACCESSDENIED,
7981 tr("Machine state change is in progress. Please retry the operation later."));
7982 }
7983
7984 ++mData->mMachineStateDeps;
7985 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7986 }
7987
7988 if (aState)
7989 *aState = mData->mMachineState;
7990 if (aRegistered)
7991 *aRegistered = mData->mRegistered;
7992
7993 return S_OK;
7994}
7995
7996/**
7997 * Decreases the number of objects dependent on the machine state.
7998 * Must always complete the #i_addStateDependency() call after the state
7999 * dependency is no more necessary.
8000 */
8001void Machine::i_releaseStateDependency()
8002{
8003 AutoCaller autoCaller(this);
8004 AssertComRCReturnVoid(autoCaller.rc());
8005
8006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8007
8008 /* releaseStateDependency() w/o addStateDependency()? */
8009 AssertReturnVoid(mData->mMachineStateDeps != 0);
8010 -- mData->mMachineStateDeps;
8011
8012 if (mData->mMachineStateDeps == 0)
8013 {
8014 /* inform i_ensureNoStateDependencies() that there are no more deps */
8015 if (mData->mMachineStateChangePending != 0)
8016 {
8017 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8018 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8019 }
8020 }
8021}
8022
8023Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8024{
8025 /* start with nothing found */
8026 Utf8Str strResult("");
8027
8028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8029
8030 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8031 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8032 // found:
8033 strResult = it->second; // source is a Utf8Str
8034
8035 return strResult;
8036}
8037
8038// protected methods
8039/////////////////////////////////////////////////////////////////////////////
8040
8041/**
8042 * Performs machine state checks based on the @a aDepType value. If a check
8043 * fails, this method will set extended error info, otherwise it will return
8044 * S_OK. It is supposed, that on failure, the caller will immediately return
8045 * the return value of this method to the upper level.
8046 *
8047 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8048 *
8049 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8050 * current state of this machine object allows to change settings of the
8051 * machine (i.e. the machine is not registered, or registered but not running
8052 * and not saved). It is useful to call this method from Machine setters
8053 * before performing any change.
8054 *
8055 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8056 * as for MutableStateDep except that if the machine is saved, S_OK is also
8057 * returned. This is useful in setters which allow changing machine
8058 * properties when it is in the saved state.
8059 *
8060 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8061 * if the current state of this machine object allows to change runtime
8062 * changeable settings of the machine (i.e. the machine is not registered, or
8063 * registered but either running or not running and not saved). It is useful
8064 * to call this method from Machine setters before performing any changes to
8065 * runtime changeable settings.
8066 *
8067 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8068 * the same as for MutableOrRunningStateDep except that if the machine is
8069 * saved, S_OK is also returned. This is useful in setters which allow
8070 * changing runtime and saved state changeable machine properties.
8071 *
8072 * @param aDepType Dependency type to check.
8073 *
8074 * @note Non Machine based classes should use #i_addStateDependency() and
8075 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8076 * template.
8077 *
8078 * @note This method must be called from under this object's read or write
8079 * lock.
8080 */
8081HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8082{
8083 switch (aDepType)
8084 {
8085 case AnyStateDep:
8086 {
8087 break;
8088 }
8089 case MutableStateDep:
8090 {
8091 if ( mData->mRegistered
8092 && ( !i_isSessionMachine()
8093 || ( mData->mMachineState != MachineState_Aborted
8094 && mData->mMachineState != MachineState_Teleported
8095 && mData->mMachineState != MachineState_PoweredOff
8096 )
8097 )
8098 )
8099 return setError(VBOX_E_INVALID_VM_STATE,
8100 tr("The machine is not mutable (state is %s)"),
8101 Global::stringifyMachineState(mData->mMachineState));
8102 break;
8103 }
8104 case MutableOrSavedStateDep:
8105 {
8106 if ( mData->mRegistered
8107 && ( !i_isSessionMachine()
8108 || ( mData->mMachineState != MachineState_Aborted
8109 && mData->mMachineState != MachineState_Teleported
8110 && mData->mMachineState != MachineState_Saved
8111 && mData->mMachineState != MachineState_AbortedSaved
8112 && mData->mMachineState != MachineState_PoweredOff
8113 )
8114 )
8115 )
8116 return setError(VBOX_E_INVALID_VM_STATE,
8117 tr("The machine is not mutable or saved (state is %s)"),
8118 Global::stringifyMachineState(mData->mMachineState));
8119 break;
8120 }
8121 case MutableOrRunningStateDep:
8122 {
8123 if ( mData->mRegistered
8124 && ( !i_isSessionMachine()
8125 || ( mData->mMachineState != MachineState_Aborted
8126 && mData->mMachineState != MachineState_Teleported
8127 && mData->mMachineState != MachineState_PoweredOff
8128 && !Global::IsOnline(mData->mMachineState)
8129 )
8130 )
8131 )
8132 return setError(VBOX_E_INVALID_VM_STATE,
8133 tr("The machine is not mutable or running (state is %s)"),
8134 Global::stringifyMachineState(mData->mMachineState));
8135 break;
8136 }
8137 case MutableOrSavedOrRunningStateDep:
8138 {
8139 if ( mData->mRegistered
8140 && ( !i_isSessionMachine()
8141 || ( mData->mMachineState != MachineState_Aborted
8142 && mData->mMachineState != MachineState_Teleported
8143 && mData->mMachineState != MachineState_Saved
8144 && mData->mMachineState != MachineState_AbortedSaved
8145 && mData->mMachineState != MachineState_PoweredOff
8146 && !Global::IsOnline(mData->mMachineState)
8147 )
8148 )
8149 )
8150 return setError(VBOX_E_INVALID_VM_STATE,
8151 tr("The machine is not mutable, saved or running (state is %s)"),
8152 Global::stringifyMachineState(mData->mMachineState));
8153 break;
8154 }
8155 }
8156
8157 return S_OK;
8158}
8159
8160/**
8161 * Helper to initialize all associated child objects and allocate data
8162 * structures.
8163 *
8164 * This method must be called as a part of the object's initialization procedure
8165 * (usually done in the #init() method).
8166 *
8167 * @note Must be called only from #init() or from #i_registeredInit().
8168 */
8169HRESULT Machine::initDataAndChildObjects()
8170{
8171 AutoCaller autoCaller(this);
8172 AssertComRCReturnRC(autoCaller.rc());
8173 AssertReturn( getObjectState().getState() == ObjectState::InInit
8174 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8175
8176 AssertReturn(!mData->mAccessible, E_FAIL);
8177
8178 /* allocate data structures */
8179 mSSData.allocate();
8180 mUserData.allocate();
8181 mHWData.allocate();
8182 mMediumAttachments.allocate();
8183 mStorageControllers.allocate();
8184 mUSBControllers.allocate();
8185
8186 /* initialize mOSTypeId */
8187 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8188
8189/** @todo r=bird: init() methods never fails, right? Why don't we make them
8190 * return void then! */
8191
8192 /* create associated BIOS settings object */
8193 unconst(mBIOSSettings).createObject();
8194 mBIOSSettings->init(this);
8195
8196 /* create associated trusted platform module object */
8197 unconst(mTrustedPlatformModule).createObject();
8198 mTrustedPlatformModule->init(this);
8199
8200 /* create associated NVRAM store object */
8201 unconst(mNvramStore).createObject();
8202 mNvramStore->init(this);
8203
8204 /* create associated record settings object */
8205 unconst(mRecordingSettings).createObject();
8206 mRecordingSettings->init(this);
8207
8208 /* create the graphics adapter object (always present) */
8209 unconst(mGraphicsAdapter).createObject();
8210 mGraphicsAdapter->init(this);
8211
8212 /* create an associated VRDE object (default is disabled) */
8213 unconst(mVRDEServer).createObject();
8214 mVRDEServer->init(this);
8215
8216 /* create associated serial port objects */
8217 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8218 {
8219 unconst(mSerialPorts[slot]).createObject();
8220 mSerialPorts[slot]->init(this, slot);
8221 }
8222
8223 /* create associated parallel port objects */
8224 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8225 {
8226 unconst(mParallelPorts[slot]).createObject();
8227 mParallelPorts[slot]->init(this, slot);
8228 }
8229
8230 /* create the audio adapter object (always present, default is disabled) */
8231 unconst(mAudioAdapter).createObject();
8232 mAudioAdapter->init(this);
8233
8234 /* create the USB device filters object (always present) */
8235 unconst(mUSBDeviceFilters).createObject();
8236 mUSBDeviceFilters->init(this);
8237
8238 /* create associated network adapter objects */
8239 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8240 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8241 {
8242 unconst(mNetworkAdapters[slot]).createObject();
8243 mNetworkAdapters[slot]->init(this, slot);
8244 }
8245
8246 /* create the bandwidth control */
8247 unconst(mBandwidthControl).createObject();
8248 mBandwidthControl->init(this);
8249
8250 return S_OK;
8251}
8252
8253/**
8254 * Helper to uninitialize all associated child objects and to free all data
8255 * structures.
8256 *
8257 * This method must be called as a part of the object's uninitialization
8258 * procedure (usually done in the #uninit() method).
8259 *
8260 * @note Must be called only from #uninit() or from #i_registeredInit().
8261 */
8262void Machine::uninitDataAndChildObjects()
8263{
8264 AutoCaller autoCaller(this);
8265 AssertComRCReturnVoid(autoCaller.rc());
8266 /* Machine object has state = ObjectState::InInit during registeredInit, even if it fails to get settings */
8267 AssertReturnVoid( getObjectState().getState() == ObjectState::InInit
8268 || getObjectState().getState() == ObjectState::InUninit
8269 || getObjectState().getState() == ObjectState::Limited);
8270
8271 /* tell all our other child objects we've been uninitialized */
8272 if (mBandwidthControl)
8273 {
8274 mBandwidthControl->uninit();
8275 unconst(mBandwidthControl).setNull();
8276 }
8277
8278 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8279 {
8280 if (mNetworkAdapters[slot])
8281 {
8282 mNetworkAdapters[slot]->uninit();
8283 unconst(mNetworkAdapters[slot]).setNull();
8284 }
8285 }
8286
8287 if (mUSBDeviceFilters)
8288 {
8289 mUSBDeviceFilters->uninit();
8290 unconst(mUSBDeviceFilters).setNull();
8291 }
8292
8293 if (mAudioAdapter)
8294 {
8295 mAudioAdapter->uninit();
8296 unconst(mAudioAdapter).setNull();
8297 }
8298
8299 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8300 {
8301 if (mParallelPorts[slot])
8302 {
8303 mParallelPorts[slot]->uninit();
8304 unconst(mParallelPorts[slot]).setNull();
8305 }
8306 }
8307
8308 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8309 {
8310 if (mSerialPorts[slot])
8311 {
8312 mSerialPorts[slot]->uninit();
8313 unconst(mSerialPorts[slot]).setNull();
8314 }
8315 }
8316
8317 if (mVRDEServer)
8318 {
8319 mVRDEServer->uninit();
8320 unconst(mVRDEServer).setNull();
8321 }
8322
8323 if (mGraphicsAdapter)
8324 {
8325 mGraphicsAdapter->uninit();
8326 unconst(mGraphicsAdapter).setNull();
8327 }
8328
8329 if (mBIOSSettings)
8330 {
8331 mBIOSSettings->uninit();
8332 unconst(mBIOSSettings).setNull();
8333 }
8334
8335 if (mTrustedPlatformModule)
8336 {
8337 mTrustedPlatformModule->uninit();
8338 unconst(mTrustedPlatformModule).setNull();
8339 }
8340
8341 if (mNvramStore)
8342 {
8343 mNvramStore->uninit();
8344 unconst(mNvramStore).setNull();
8345 }
8346
8347 if (mRecordingSettings)
8348 {
8349 mRecordingSettings->uninit();
8350 unconst(mRecordingSettings).setNull();
8351 }
8352
8353 /* Deassociate media (only when a real Machine or a SnapshotMachine
8354 * instance is uninitialized; SessionMachine instances refer to real
8355 * Machine media). This is necessary for a clean re-initialization of
8356 * the VM after successfully re-checking the accessibility state. Note
8357 * that in case of normal Machine or SnapshotMachine uninitialization (as
8358 * a result of unregistering or deleting the snapshot), outdated media
8359 * attachments will already be uninitialized and deleted, so this
8360 * code will not affect them. */
8361 if ( !mMediumAttachments.isNull()
8362 && !i_isSessionMachine()
8363 )
8364 {
8365 for (MediumAttachmentList::const_iterator
8366 it = mMediumAttachments->begin();
8367 it != mMediumAttachments->end();
8368 ++it)
8369 {
8370 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8371 if (pMedium.isNull())
8372 continue;
8373 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8374 AssertComRC(rc);
8375 }
8376 }
8377
8378 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8379 {
8380 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8381 if (mData->mFirstSnapshot)
8382 {
8383 // snapshots tree is protected by machine write lock; strictly
8384 // this isn't necessary here since we're deleting the entire
8385 // machine, but otherwise we assert in Snapshot::uninit()
8386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8387 mData->mFirstSnapshot->uninit();
8388 mData->mFirstSnapshot.setNull();
8389 }
8390
8391 mData->mCurrentSnapshot.setNull();
8392 }
8393
8394 /* free data structures (the essential mData structure is not freed here
8395 * since it may be still in use) */
8396 mMediumAttachments.free();
8397 mStorageControllers.free();
8398 mUSBControllers.free();
8399 mHWData.free();
8400 mUserData.free();
8401 mSSData.free();
8402}
8403
8404/**
8405 * Returns a pointer to the Machine object for this machine that acts like a
8406 * parent for complex machine data objects such as shared folders, etc.
8407 *
8408 * For primary Machine objects and for SnapshotMachine objects, returns this
8409 * object's pointer itself. For SessionMachine objects, returns the peer
8410 * (primary) machine pointer.
8411 */
8412Machine *Machine::i_getMachine()
8413{
8414 if (i_isSessionMachine())
8415 return (Machine*)mPeer;
8416 return this;
8417}
8418
8419/**
8420 * Makes sure that there are no machine state dependents. If necessary, waits
8421 * for the number of dependents to drop to zero.
8422 *
8423 * Make sure this method is called from under this object's write lock to
8424 * guarantee that no new dependents may be added when this method returns
8425 * control to the caller.
8426 *
8427 * @note Receives a lock to this object for writing. The lock will be released
8428 * while waiting (if necessary).
8429 *
8430 * @warning To be used only in methods that change the machine state!
8431 */
8432void Machine::i_ensureNoStateDependencies(AutoWriteLock &alock)
8433{
8434 AssertReturnVoid(isWriteLockOnCurrentThread());
8435
8436 /* Wait for all state dependents if necessary */
8437 if (mData->mMachineStateDeps != 0)
8438 {
8439 /* lazy semaphore creation */
8440 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8441 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8442
8443 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8444 mData->mMachineStateDeps));
8445
8446 ++mData->mMachineStateChangePending;
8447
8448 /* reset the semaphore before waiting, the last dependent will signal
8449 * it */
8450 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8451
8452 alock.release();
8453
8454 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8455
8456 alock.acquire();
8457
8458 -- mData->mMachineStateChangePending;
8459 }
8460}
8461
8462/**
8463 * Changes the machine state and informs callbacks.
8464 *
8465 * This method is not intended to fail so it either returns S_OK or asserts (and
8466 * returns a failure).
8467 *
8468 * @note Locks this object for writing.
8469 */
8470HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8471{
8472 LogFlowThisFuncEnter();
8473 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8474 Assert(aMachineState != MachineState_Null);
8475
8476 AutoCaller autoCaller(this);
8477 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8478
8479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8480
8481 /* wait for state dependents to drop to zero */
8482 i_ensureNoStateDependencies(alock);
8483
8484 MachineState_T const enmOldState = mData->mMachineState;
8485 if (enmOldState != aMachineState)
8486 {
8487 mData->mMachineState = aMachineState;
8488 RTTimeNow(&mData->mLastStateChange);
8489
8490#ifdef VBOX_WITH_DTRACE_R3_MAIN
8491 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8492#endif
8493 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8494 }
8495
8496 LogFlowThisFuncLeave();
8497 return S_OK;
8498}
8499
8500/**
8501 * Searches for a shared folder with the given logical name
8502 * in the collection of shared folders.
8503 *
8504 * @param aName logical name of the shared folder
8505 * @param aSharedFolder where to return the found object
8506 * @param aSetError whether to set the error info if the folder is
8507 * not found
8508 * @return
8509 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8510 *
8511 * @note
8512 * must be called from under the object's lock!
8513 */
8514HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8515 ComObjPtr<SharedFolder> &aSharedFolder,
8516 bool aSetError /* = false */)
8517{
8518 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8519 for (HWData::SharedFolderList::const_iterator
8520 it = mHWData->mSharedFolders.begin();
8521 it != mHWData->mSharedFolders.end();
8522 ++it)
8523 {
8524 SharedFolder *pSF = *it;
8525 AutoCaller autoCaller(pSF);
8526 if (pSF->i_getName() == aName)
8527 {
8528 aSharedFolder = pSF;
8529 rc = S_OK;
8530 break;
8531 }
8532 }
8533
8534 if (aSetError && FAILED(rc))
8535 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8536
8537 return rc;
8538}
8539
8540/**
8541 * Initializes all machine instance data from the given settings structures
8542 * from XML. The exception is the machine UUID which needs special handling
8543 * depending on the caller's use case, so the caller needs to set that herself.
8544 *
8545 * This gets called in several contexts during machine initialization:
8546 *
8547 * -- When machine XML exists on disk already and needs to be loaded into memory,
8548 * for example, from #i_registeredInit() to load all registered machines on
8549 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8550 * attached to the machine should be part of some media registry already.
8551 *
8552 * -- During OVF import, when a machine config has been constructed from an
8553 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8554 * ensure that the media listed as attachments in the config (which have
8555 * been imported from the OVF) receive the correct registry ID.
8556 *
8557 * -- During VM cloning.
8558 *
8559 * @param config Machine settings from XML.
8560 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8561 * for each attached medium in the config.
8562 * @return
8563 */
8564HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8565 const Guid *puuidRegistry)
8566{
8567 // copy name, description, OS type, teleporter, UTC etc.
8568 mUserData->s = config.machineUserData;
8569
8570 // look up the object by Id to check it is valid
8571 ComObjPtr<GuestOSType> pGuestOSType;
8572 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8573 if (!pGuestOSType.isNull())
8574 mUserData->s.strOsType = pGuestOSType->i_id();
8575
8576 // stateFile (optional)
8577 if (config.strStateFile.isEmpty())
8578 mSSData->strStateFilePath.setNull();
8579 else
8580 {
8581 Utf8Str stateFilePathFull(config.strStateFile);
8582 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8583 if (RT_FAILURE(vrc))
8584 return setErrorBoth(E_FAIL, vrc,
8585 tr("Invalid saved state file path '%s' (%Rrc)"),
8586 config.strStateFile.c_str(),
8587 vrc);
8588 mSSData->strStateFilePath = stateFilePathFull;
8589 }
8590
8591 // snapshot folder needs special processing so set it again
8592 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8593 if (FAILED(rc)) return rc;
8594
8595 /* Copy the extra data items (config may or may not be the same as
8596 * mData->pMachineConfigFile) if necessary. When loading the XML files
8597 * from disk they are the same, but not for OVF import. */
8598 if (mData->pMachineConfigFile != &config)
8599 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8600
8601 /* currentStateModified (optional, default is true) */
8602 mData->mCurrentStateModified = config.fCurrentStateModified;
8603
8604 mData->mLastStateChange = config.timeLastStateChange;
8605
8606 /*
8607 * note: all mUserData members must be assigned prior this point because
8608 * we need to commit changes in order to let mUserData be shared by all
8609 * snapshot machine instances.
8610 */
8611 mUserData.commitCopy();
8612
8613 // machine registry, if present (must be loaded before snapshots)
8614 if (config.canHaveOwnMediaRegistry())
8615 {
8616 // determine machine folder
8617 Utf8Str strMachineFolder = i_getSettingsFileFull();
8618 strMachineFolder.stripFilename();
8619 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8620 config.mediaRegistry,
8621 strMachineFolder);
8622 if (FAILED(rc)) return rc;
8623 }
8624
8625 /* Snapshot node (optional) */
8626 size_t cRootSnapshots;
8627 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8628 {
8629 // there must be only one root snapshot
8630 Assert(cRootSnapshots == 1);
8631
8632 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8633
8634 rc = i_loadSnapshot(snap,
8635 config.uuidCurrentSnapshot,
8636 NULL); // no parent == first snapshot
8637 if (FAILED(rc)) return rc;
8638 }
8639
8640 // hardware data
8641 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8642 if (FAILED(rc)) return rc;
8643
8644 /*
8645 * NOTE: the assignment below must be the last thing to do,
8646 * otherwise it will be not possible to change the settings
8647 * somewhere in the code above because all setters will be
8648 * blocked by i_checkStateDependency(MutableStateDep).
8649 */
8650
8651 /* set the machine state to either Aborted-Saved, Aborted, or Saved if appropriate */
8652 if (config.fAborted && !mSSData->strStateFilePath.isEmpty())
8653 {
8654 /* no need to use i_setMachineState() during init() */
8655 mData->mMachineState = MachineState_AbortedSaved;
8656 }
8657 else if (config.fAborted)
8658 {
8659 mSSData->strStateFilePath.setNull();
8660
8661 /* no need to use i_setMachineState() during init() */
8662 mData->mMachineState = MachineState_Aborted;
8663 }
8664 else if (!mSSData->strStateFilePath.isEmpty())
8665 {
8666 /* no need to use i_setMachineState() during init() */
8667 mData->mMachineState = MachineState_Saved;
8668 }
8669
8670 // after loading settings, we are no longer different from the XML on disk
8671 mData->flModifications = 0;
8672
8673 return S_OK;
8674}
8675
8676/**
8677 * Recursively loads all snapshots starting from the given.
8678 *
8679 * @param data snapshot settings.
8680 * @param aCurSnapshotId Current snapshot ID from the settings file.
8681 * @param aParentSnapshot Parent snapshot.
8682 */
8683HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8684 const Guid &aCurSnapshotId,
8685 Snapshot *aParentSnapshot)
8686{
8687 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8688 AssertReturn(!i_isSessionMachine(), E_FAIL);
8689
8690 HRESULT rc = S_OK;
8691
8692 Utf8Str strStateFile;
8693 if (!data.strStateFile.isEmpty())
8694 {
8695 /* optional */
8696 strStateFile = data.strStateFile;
8697 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8698 if (RT_FAILURE(vrc))
8699 return setErrorBoth(E_FAIL, vrc,
8700 tr("Invalid saved state file path '%s' (%Rrc)"),
8701 strStateFile.c_str(),
8702 vrc);
8703 }
8704
8705 /* create a snapshot machine object */
8706 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8707 pSnapshotMachine.createObject();
8708 rc = pSnapshotMachine->initFromSettings(this,
8709 data.hardware,
8710 &data.debugging,
8711 &data.autostart,
8712 data.uuid.ref(),
8713 strStateFile);
8714 if (FAILED(rc)) return rc;
8715
8716 /* create a snapshot object */
8717 ComObjPtr<Snapshot> pSnapshot;
8718 pSnapshot.createObject();
8719 /* initialize the snapshot */
8720 rc = pSnapshot->init(mParent, // VirtualBox object
8721 data.uuid,
8722 data.strName,
8723 data.strDescription,
8724 data.timestamp,
8725 pSnapshotMachine,
8726 aParentSnapshot);
8727 if (FAILED(rc)) return rc;
8728
8729 /* memorize the first snapshot if necessary */
8730 if (!mData->mFirstSnapshot)
8731 mData->mFirstSnapshot = pSnapshot;
8732
8733 /* memorize the current snapshot when appropriate */
8734 if ( !mData->mCurrentSnapshot
8735 && pSnapshot->i_getId() == aCurSnapshotId
8736 )
8737 mData->mCurrentSnapshot = pSnapshot;
8738
8739 // now create the children
8740 for (settings::SnapshotsList::const_iterator
8741 it = data.llChildSnapshots.begin();
8742 it != data.llChildSnapshots.end();
8743 ++it)
8744 {
8745 const settings::Snapshot &childData = *it;
8746 // recurse
8747 rc = i_loadSnapshot(childData,
8748 aCurSnapshotId,
8749 pSnapshot); // parent = the one we created above
8750 if (FAILED(rc)) return rc;
8751 }
8752
8753 return rc;
8754}
8755
8756/**
8757 * Loads settings into mHWData.
8758 *
8759 * @param puuidRegistry Registry ID.
8760 * @param puuidSnapshot Snapshot ID
8761 * @param data Reference to the hardware settings.
8762 * @param pDbg Pointer to the debugging settings.
8763 * @param pAutostart Pointer to the autostart settings.
8764 */
8765HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8766 const Guid *puuidSnapshot,
8767 const settings::Hardware &data,
8768 const settings::Debugging *pDbg,
8769 const settings::Autostart *pAutostart)
8770{
8771 AssertReturn(!i_isSessionMachine(), E_FAIL);
8772
8773 HRESULT rc = S_OK;
8774
8775 try
8776 {
8777 ComObjPtr<GuestOSType> pGuestOSType;
8778 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8779
8780 /* The hardware version attribute (optional). */
8781 mHWData->mHWVersion = data.strVersion;
8782 mHWData->mHardwareUUID = data.uuid;
8783
8784 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8785 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8786 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8787 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8788 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8789 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8790 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8791 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8792 mHWData->mPAEEnabled = data.fPAE;
8793 mHWData->mLongMode = data.enmLongMode;
8794 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8795 mHWData->mAPIC = data.fAPIC;
8796 mHWData->mX2APIC = data.fX2APIC;
8797 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8798 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8799 mHWData->mSpecCtrl = data.fSpecCtrl;
8800 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8801 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8802 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8803 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8804 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8805 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8806 mHWData->mCPUCount = data.cCPUs;
8807 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8808 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8809 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8810 mHWData->mCpuProfile = data.strCpuProfile;
8811
8812 // cpu
8813 if (mHWData->mCPUHotPlugEnabled)
8814 {
8815 for (settings::CpuList::const_iterator
8816 it = data.llCpus.begin();
8817 it != data.llCpus.end();
8818 ++it)
8819 {
8820 const settings::Cpu &cpu = *it;
8821
8822 mHWData->mCPUAttached[cpu.ulId] = true;
8823 }
8824 }
8825
8826 // cpuid leafs
8827 for (settings::CpuIdLeafsList::const_iterator
8828 it = data.llCpuIdLeafs.begin();
8829 it != data.llCpuIdLeafs.end();
8830 ++it)
8831 {
8832 const settings::CpuIdLeaf &rLeaf= *it;
8833 if ( rLeaf.idx < UINT32_C(0x20)
8834 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8835 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8836 mHWData->mCpuIdLeafList.push_back(rLeaf);
8837 /* else: just ignore */
8838 }
8839
8840 mHWData->mMemorySize = data.ulMemorySizeMB;
8841 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8842
8843 // boot order
8844 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8845 {
8846 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8847 if (it == data.mapBootOrder.end())
8848 mHWData->mBootOrder[i] = DeviceType_Null;
8849 else
8850 mHWData->mBootOrder[i] = it->second;
8851 }
8852
8853 mHWData->mFirmwareType = data.firmwareType;
8854 mHWData->mPointingHIDType = data.pointingHIDType;
8855 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8856 mHWData->mChipsetType = data.chipsetType;
8857 mHWData->mIommuType = data.iommuType;
8858 mHWData->mParavirtProvider = data.paravirtProvider;
8859 mHWData->mParavirtDebug = data.strParavirtDebug;
8860 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8861 mHWData->mHPETEnabled = data.fHPETEnabled;
8862
8863 /* GraphicsAdapter */
8864 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8865 if (FAILED(rc)) return rc;
8866
8867 /* VRDEServer */
8868 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8869 if (FAILED(rc)) return rc;
8870
8871 /* BIOS */
8872 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8873 if (FAILED(rc)) return rc;
8874
8875 /* Trusted Platform Module */
8876 rc = mTrustedPlatformModule->i_loadSettings(data.tpmSettings);
8877 if (FAILED(rc)) return rc;
8878
8879 rc = mNvramStore->i_loadSettings(data.nvramSettings);
8880 if (FAILED(rc)) return rc;
8881
8882 /* Recording settings */
8883 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8884 if (FAILED(rc)) return rc;
8885
8886 // Bandwidth control (must come before network adapters)
8887 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8888 if (FAILED(rc)) return rc;
8889
8890 /* USB controllers */
8891 for (settings::USBControllerList::const_iterator
8892 it = data.usbSettings.llUSBControllers.begin();
8893 it != data.usbSettings.llUSBControllers.end();
8894 ++it)
8895 {
8896 const settings::USBController &settingsCtrl = *it;
8897 ComObjPtr<USBController> newCtrl;
8898
8899 newCtrl.createObject();
8900 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8901 mUSBControllers->push_back(newCtrl);
8902 }
8903
8904 /* USB device filters */
8905 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8906 if (FAILED(rc)) return rc;
8907
8908 // network adapters (establish array size first and apply defaults, to
8909 // ensure reading the same settings as we saved, since the list skips
8910 // adapters having defaults)
8911 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8912 size_t oldCount = mNetworkAdapters.size();
8913 if (newCount > oldCount)
8914 {
8915 mNetworkAdapters.resize(newCount);
8916 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8917 {
8918 unconst(mNetworkAdapters[slot]).createObject();
8919 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8920 }
8921 }
8922 else if (newCount < oldCount)
8923 mNetworkAdapters.resize(newCount);
8924 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8925 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8926 for (settings::NetworkAdaptersList::const_iterator
8927 it = data.llNetworkAdapters.begin();
8928 it != data.llNetworkAdapters.end();
8929 ++it)
8930 {
8931 const settings::NetworkAdapter &nic = *it;
8932
8933 /* slot uniqueness is guaranteed by XML Schema */
8934 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8935 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8936 if (FAILED(rc)) return rc;
8937 }
8938
8939 // serial ports (establish defaults first, to ensure reading the same
8940 // settings as we saved, since the list skips ports having defaults)
8941 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8942 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8943 for (settings::SerialPortsList::const_iterator
8944 it = data.llSerialPorts.begin();
8945 it != data.llSerialPorts.end();
8946 ++it)
8947 {
8948 const settings::SerialPort &s = *it;
8949
8950 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8951 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8952 if (FAILED(rc)) return rc;
8953 }
8954
8955 // parallel ports (establish defaults first, to ensure reading the same
8956 // settings as we saved, since the list skips ports having defaults)
8957 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8958 mParallelPorts[i]->i_applyDefaults();
8959 for (settings::ParallelPortsList::const_iterator
8960 it = data.llParallelPorts.begin();
8961 it != data.llParallelPorts.end();
8962 ++it)
8963 {
8964 const settings::ParallelPort &p = *it;
8965
8966 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8967 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8968 if (FAILED(rc)) return rc;
8969 }
8970
8971 /* AudioAdapter */
8972 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8973 if (FAILED(rc)) return rc;
8974
8975 /* storage controllers */
8976 rc = i_loadStorageControllers(data.storage,
8977 puuidRegistry,
8978 puuidSnapshot);
8979 if (FAILED(rc)) return rc;
8980
8981 /* Shared folders */
8982 for (settings::SharedFoldersList::const_iterator
8983 it = data.llSharedFolders.begin();
8984 it != data.llSharedFolders.end();
8985 ++it)
8986 {
8987 const settings::SharedFolder &sf = *it;
8988
8989 ComObjPtr<SharedFolder> sharedFolder;
8990 /* Check for double entries. Not allowed! */
8991 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8992 if (SUCCEEDED(rc))
8993 return setError(VBOX_E_OBJECT_IN_USE,
8994 tr("Shared folder named '%s' already exists"),
8995 sf.strName.c_str());
8996
8997 /* Create the new shared folder. Don't break on error. This will be
8998 * reported when the machine starts. */
8999 sharedFolder.createObject();
9000 rc = sharedFolder->init(i_getMachine(),
9001 sf.strName,
9002 sf.strHostPath,
9003 RT_BOOL(sf.fWritable),
9004 RT_BOOL(sf.fAutoMount),
9005 sf.strAutoMountPoint,
9006 false /* fFailOnError */);
9007 if (FAILED(rc)) return rc;
9008 mHWData->mSharedFolders.push_back(sharedFolder);
9009 }
9010
9011 // Clipboard
9012 mHWData->mClipboardMode = data.clipboardMode;
9013 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
9014
9015 // drag'n'drop
9016 mHWData->mDnDMode = data.dndMode;
9017
9018 // guest settings
9019 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9020
9021 // IO settings
9022 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9023 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9024
9025 // Host PCI devices
9026 for (settings::HostPCIDeviceAttachmentList::const_iterator
9027 it = data.pciAttachments.begin();
9028 it != data.pciAttachments.end();
9029 ++it)
9030 {
9031 const settings::HostPCIDeviceAttachment &hpda = *it;
9032 ComObjPtr<PCIDeviceAttachment> pda;
9033
9034 pda.createObject();
9035 pda->i_loadSettings(this, hpda);
9036 mHWData->mPCIDeviceAssignments.push_back(pda);
9037 }
9038
9039 /*
9040 * (The following isn't really real hardware, but it lives in HWData
9041 * for reasons of convenience.)
9042 */
9043
9044#ifdef VBOX_WITH_GUEST_PROPS
9045 /* Guest properties (optional) */
9046
9047 /* Only load transient guest properties for configs which have saved
9048 * state, because there shouldn't be any for powered off VMs. The same
9049 * logic applies for snapshots, as offline snapshots shouldn't have
9050 * any such properties. They confuse the code in various places.
9051 * Note: can't rely on the machine state, as it isn't set yet. */
9052 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9053 /* apologies for the hacky unconst() usage, but this needs hacking
9054 * actually inconsistent settings into consistency, otherwise there
9055 * will be some corner cases where the inconsistency survives
9056 * surprisingly long without getting fixed, especially for snapshots
9057 * as there are no config changes. */
9058 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9059 for (settings::GuestPropertiesList::iterator
9060 it = llGuestProperties.begin();
9061 it != llGuestProperties.end();
9062 /*nothing*/)
9063 {
9064 const settings::GuestProperty &prop = *it;
9065 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9066 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9067 if ( fSkipTransientGuestProperties
9068 && ( fFlags & GUEST_PROP_F_TRANSIENT
9069 || fFlags & GUEST_PROP_F_TRANSRESET))
9070 {
9071 it = llGuestProperties.erase(it);
9072 continue;
9073 }
9074 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9075 mHWData->mGuestProperties[prop.strName] = property;
9076 ++it;
9077 }
9078#endif /* VBOX_WITH_GUEST_PROPS defined */
9079
9080 rc = i_loadDebugging(pDbg);
9081 if (FAILED(rc))
9082 return rc;
9083
9084 mHWData->mAutostart = *pAutostart;
9085
9086 /* default frontend */
9087 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9088 }
9089 catch (std::bad_alloc &)
9090 {
9091 return E_OUTOFMEMORY;
9092 }
9093
9094 AssertComRC(rc);
9095 return rc;
9096}
9097
9098/**
9099 * Called from i_loadHardware() to load the debugging settings of the
9100 * machine.
9101 *
9102 * @param pDbg Pointer to the settings.
9103 */
9104HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9105{
9106 mHWData->mDebugging = *pDbg;
9107 /* no more processing currently required, this will probably change. */
9108 return S_OK;
9109}
9110
9111/**
9112 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9113 *
9114 * @param data storage settings.
9115 * @param puuidRegistry media registry ID to set media to or NULL;
9116 * see Machine::i_loadMachineDataFromSettings()
9117 * @param puuidSnapshot snapshot ID
9118 * @return
9119 */
9120HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9121 const Guid *puuidRegistry,
9122 const Guid *puuidSnapshot)
9123{
9124 AssertReturn(!i_isSessionMachine(), E_FAIL);
9125
9126 HRESULT rc = S_OK;
9127
9128 for (settings::StorageControllersList::const_iterator
9129 it = data.llStorageControllers.begin();
9130 it != data.llStorageControllers.end();
9131 ++it)
9132 {
9133 const settings::StorageController &ctlData = *it;
9134
9135 ComObjPtr<StorageController> pCtl;
9136 /* Try to find one with the name first. */
9137 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9138 if (SUCCEEDED(rc))
9139 return setError(VBOX_E_OBJECT_IN_USE,
9140 tr("Storage controller named '%s' already exists"),
9141 ctlData.strName.c_str());
9142
9143 pCtl.createObject();
9144 rc = pCtl->init(this,
9145 ctlData.strName,
9146 ctlData.storageBus,
9147 ctlData.ulInstance,
9148 ctlData.fBootable);
9149 if (FAILED(rc)) return rc;
9150
9151 mStorageControllers->push_back(pCtl);
9152
9153 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9154 if (FAILED(rc)) return rc;
9155
9156 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9157 if (FAILED(rc)) return rc;
9158
9159 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9160 if (FAILED(rc)) return rc;
9161
9162 /* Load the attached devices now. */
9163 rc = i_loadStorageDevices(pCtl,
9164 ctlData,
9165 puuidRegistry,
9166 puuidSnapshot);
9167 if (FAILED(rc)) return rc;
9168 }
9169
9170 return S_OK;
9171}
9172
9173/**
9174 * Called from i_loadStorageControllers for a controller's devices.
9175 *
9176 * @param aStorageController
9177 * @param data
9178 * @param puuidRegistry media registry ID to set media to or NULL; see
9179 * Machine::i_loadMachineDataFromSettings()
9180 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9181 * @return
9182 */
9183HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9184 const settings::StorageController &data,
9185 const Guid *puuidRegistry,
9186 const Guid *puuidSnapshot)
9187{
9188 HRESULT rc = S_OK;
9189
9190 /* paranoia: detect duplicate attachments */
9191 for (settings::AttachedDevicesList::const_iterator
9192 it = data.llAttachedDevices.begin();
9193 it != data.llAttachedDevices.end();
9194 ++it)
9195 {
9196 const settings::AttachedDevice &ad = *it;
9197
9198 for (settings::AttachedDevicesList::const_iterator it2 = it;
9199 it2 != data.llAttachedDevices.end();
9200 ++it2)
9201 {
9202 if (it == it2)
9203 continue;
9204
9205 const settings::AttachedDevice &ad2 = *it2;
9206
9207 if ( ad.lPort == ad2.lPort
9208 && ad.lDevice == ad2.lDevice)
9209 {
9210 return setError(E_FAIL,
9211 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9212 aStorageController->i_getName().c_str(),
9213 ad.lPort,
9214 ad.lDevice,
9215 mUserData->s.strName.c_str());
9216 }
9217 }
9218 }
9219
9220 for (settings::AttachedDevicesList::const_iterator
9221 it = data.llAttachedDevices.begin();
9222 it != data.llAttachedDevices.end();
9223 ++it)
9224 {
9225 const settings::AttachedDevice &dev = *it;
9226 ComObjPtr<Medium> medium;
9227
9228 switch (dev.deviceType)
9229 {
9230 case DeviceType_Floppy:
9231 case DeviceType_DVD:
9232 if (dev.strHostDriveSrc.isNotEmpty())
9233 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9234 false /* fRefresh */, medium);
9235 else
9236 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9237 dev.uuid,
9238 false /* fRefresh */,
9239 false /* aSetError */,
9240 medium);
9241 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9242 // This is not an error. The host drive or UUID might have vanished, so just go
9243 // ahead without this removeable medium attachment
9244 rc = S_OK;
9245 break;
9246
9247 case DeviceType_HardDisk:
9248 {
9249 /* find a hard disk by UUID */
9250 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9251 if (FAILED(rc))
9252 {
9253 if (i_isSnapshotMachine())
9254 {
9255 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9256 // so the user knows that the bad disk is in a snapshot somewhere
9257 com::ErrorInfo info;
9258 return setError(E_FAIL,
9259 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9260 puuidSnapshot->raw(),
9261 info.getText().raw());
9262 }
9263 else
9264 return rc;
9265 }
9266
9267 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9268
9269 if (medium->i_getType() == MediumType_Immutable)
9270 {
9271 if (i_isSnapshotMachine())
9272 return setError(E_FAIL,
9273 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9274 "of the virtual machine '%s' ('%s')"),
9275 medium->i_getLocationFull().c_str(),
9276 dev.uuid.raw(),
9277 puuidSnapshot->raw(),
9278 mUserData->s.strName.c_str(),
9279 mData->m_strConfigFileFull.c_str());
9280
9281 return setError(E_FAIL,
9282 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9283 medium->i_getLocationFull().c_str(),
9284 dev.uuid.raw(),
9285 mUserData->s.strName.c_str(),
9286 mData->m_strConfigFileFull.c_str());
9287 }
9288
9289 if (medium->i_getType() == MediumType_MultiAttach)
9290 {
9291 if (i_isSnapshotMachine())
9292 return setError(E_FAIL,
9293 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9294 "of the virtual machine '%s' ('%s')"),
9295 medium->i_getLocationFull().c_str(),
9296 dev.uuid.raw(),
9297 puuidSnapshot->raw(),
9298 mUserData->s.strName.c_str(),
9299 mData->m_strConfigFileFull.c_str());
9300
9301 return setError(E_FAIL,
9302 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9303 medium->i_getLocationFull().c_str(),
9304 dev.uuid.raw(),
9305 mUserData->s.strName.c_str(),
9306 mData->m_strConfigFileFull.c_str());
9307 }
9308
9309 if ( !i_isSnapshotMachine()
9310 && medium->i_getChildren().size() != 0
9311 )
9312 return setError(E_FAIL,
9313 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9314 "because it has %d differencing child hard disks"),
9315 medium->i_getLocationFull().c_str(),
9316 dev.uuid.raw(),
9317 mUserData->s.strName.c_str(),
9318 mData->m_strConfigFileFull.c_str(),
9319 medium->i_getChildren().size());
9320
9321 if (i_findAttachment(*mMediumAttachments.data(),
9322 medium))
9323 return setError(E_FAIL,
9324 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9325 medium->i_getLocationFull().c_str(),
9326 dev.uuid.raw(),
9327 mUserData->s.strName.c_str(),
9328 mData->m_strConfigFileFull.c_str());
9329
9330 break;
9331 }
9332
9333 default:
9334 return setError(E_FAIL,
9335 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9336 medium->i_getLocationFull().c_str(),
9337 mUserData->s.strName.c_str(),
9338 mData->m_strConfigFileFull.c_str());
9339 }
9340
9341 if (FAILED(rc))
9342 break;
9343
9344 /* Bandwidth groups are loaded at this point. */
9345 ComObjPtr<BandwidthGroup> pBwGroup;
9346
9347 if (!dev.strBwGroup.isEmpty())
9348 {
9349 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9350 if (FAILED(rc))
9351 return setError(E_FAIL,
9352 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9353 medium->i_getLocationFull().c_str(),
9354 dev.strBwGroup.c_str(),
9355 mUserData->s.strName.c_str(),
9356 mData->m_strConfigFileFull.c_str());
9357 pBwGroup->i_reference();
9358 }
9359
9360 const Utf8Str controllerName = aStorageController->i_getName();
9361 ComObjPtr<MediumAttachment> pAttachment;
9362 pAttachment.createObject();
9363 rc = pAttachment->init(this,
9364 medium,
9365 controllerName,
9366 dev.lPort,
9367 dev.lDevice,
9368 dev.deviceType,
9369 false,
9370 dev.fPassThrough,
9371 dev.fTempEject,
9372 dev.fNonRotational,
9373 dev.fDiscard,
9374 dev.fHotPluggable,
9375 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9376 if (FAILED(rc)) break;
9377
9378 /* associate the medium with this machine and snapshot */
9379 if (!medium.isNull())
9380 {
9381 AutoCaller medCaller(medium);
9382 if (FAILED(medCaller.rc())) return medCaller.rc();
9383 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9384
9385 if (i_isSnapshotMachine())
9386 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9387 else
9388 rc = medium->i_addBackReference(mData->mUuid);
9389 /* If the medium->addBackReference fails it sets an appropriate
9390 * error message, so no need to do any guesswork here. */
9391
9392 if (puuidRegistry)
9393 // caller wants registry ID to be set on all attached media (OVF import case)
9394 medium->i_addRegistry(*puuidRegistry);
9395 }
9396
9397 if (FAILED(rc))
9398 break;
9399
9400 /* back up mMediumAttachments to let registeredInit() properly rollback
9401 * on failure (= limited accessibility) */
9402 i_setModified(IsModified_Storage);
9403 mMediumAttachments.backup();
9404 mMediumAttachments->push_back(pAttachment);
9405 }
9406
9407 return rc;
9408}
9409
9410/**
9411 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9412 *
9413 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9414 * @param aSnapshot where to return the found snapshot
9415 * @param aSetError true to set extended error info on failure
9416 */
9417HRESULT Machine::i_findSnapshotById(const Guid &aId,
9418 ComObjPtr<Snapshot> &aSnapshot,
9419 bool aSetError /* = false */)
9420{
9421 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9422
9423 if (!mData->mFirstSnapshot)
9424 {
9425 if (aSetError)
9426 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9427 return E_FAIL;
9428 }
9429
9430 if (aId.isZero())
9431 aSnapshot = mData->mFirstSnapshot;
9432 else
9433 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9434
9435 if (!aSnapshot)
9436 {
9437 if (aSetError)
9438 return setError(E_FAIL,
9439 tr("Could not find a snapshot with UUID {%s}"),
9440 aId.toString().c_str());
9441 return E_FAIL;
9442 }
9443
9444 return S_OK;
9445}
9446
9447/**
9448 * Returns the snapshot with the given name or fails of no such snapshot.
9449 *
9450 * @param strName snapshot name to find
9451 * @param aSnapshot where to return the found snapshot
9452 * @param aSetError true to set extended error info on failure
9453 */
9454HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9455 ComObjPtr<Snapshot> &aSnapshot,
9456 bool aSetError /* = false */)
9457{
9458 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9459
9460 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9461
9462 if (!mData->mFirstSnapshot)
9463 {
9464 if (aSetError)
9465 return setError(VBOX_E_OBJECT_NOT_FOUND,
9466 tr("This machine does not have any snapshots"));
9467 return VBOX_E_OBJECT_NOT_FOUND;
9468 }
9469
9470 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9471
9472 if (!aSnapshot)
9473 {
9474 if (aSetError)
9475 return setError(VBOX_E_OBJECT_NOT_FOUND,
9476 tr("Could not find a snapshot named '%s'"), strName.c_str());
9477 return VBOX_E_OBJECT_NOT_FOUND;
9478 }
9479
9480 return S_OK;
9481}
9482
9483/**
9484 * Returns a storage controller object with the given name.
9485 *
9486 * @param aName storage controller name to find
9487 * @param aStorageController where to return the found storage controller
9488 * @param aSetError true to set extended error info on failure
9489 */
9490HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9491 ComObjPtr<StorageController> &aStorageController,
9492 bool aSetError /* = false */)
9493{
9494 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9495
9496 for (StorageControllerList::const_iterator
9497 it = mStorageControllers->begin();
9498 it != mStorageControllers->end();
9499 ++it)
9500 {
9501 if ((*it)->i_getName() == aName)
9502 {
9503 aStorageController = (*it);
9504 return S_OK;
9505 }
9506 }
9507
9508 if (aSetError)
9509 return setError(VBOX_E_OBJECT_NOT_FOUND,
9510 tr("Could not find a storage controller named '%s'"),
9511 aName.c_str());
9512 return VBOX_E_OBJECT_NOT_FOUND;
9513}
9514
9515/**
9516 * Returns a USB controller object with the given name.
9517 *
9518 * @param aName USB controller name to find
9519 * @param aUSBController where to return the found USB controller
9520 * @param aSetError true to set extended error info on failure
9521 */
9522HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9523 ComObjPtr<USBController> &aUSBController,
9524 bool aSetError /* = false */)
9525{
9526 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9527
9528 for (USBControllerList::const_iterator
9529 it = mUSBControllers->begin();
9530 it != mUSBControllers->end();
9531 ++it)
9532 {
9533 if ((*it)->i_getName() == aName)
9534 {
9535 aUSBController = (*it);
9536 return S_OK;
9537 }
9538 }
9539
9540 if (aSetError)
9541 return setError(VBOX_E_OBJECT_NOT_FOUND,
9542 tr("Could not find a storage controller named '%s'"),
9543 aName.c_str());
9544 return VBOX_E_OBJECT_NOT_FOUND;
9545}
9546
9547/**
9548 * Returns the number of USB controller instance of the given type.
9549 *
9550 * @param enmType USB controller type.
9551 */
9552ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9553{
9554 ULONG cCtrls = 0;
9555
9556 for (USBControllerList::const_iterator
9557 it = mUSBControllers->begin();
9558 it != mUSBControllers->end();
9559 ++it)
9560 {
9561 if ((*it)->i_getControllerType() == enmType)
9562 cCtrls++;
9563 }
9564
9565 return cCtrls;
9566}
9567
9568HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9569 MediumAttachmentList &atts)
9570{
9571 AutoCaller autoCaller(this);
9572 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9573
9574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9575
9576 for (MediumAttachmentList::const_iterator
9577 it = mMediumAttachments->begin();
9578 it != mMediumAttachments->end();
9579 ++it)
9580 {
9581 const ComObjPtr<MediumAttachment> &pAtt = *it;
9582 // should never happen, but deal with NULL pointers in the list.
9583 AssertContinue(!pAtt.isNull());
9584
9585 // getControllerName() needs caller+read lock
9586 AutoCaller autoAttCaller(pAtt);
9587 if (FAILED(autoAttCaller.rc()))
9588 {
9589 atts.clear();
9590 return autoAttCaller.rc();
9591 }
9592 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9593
9594 if (pAtt->i_getControllerName() == aName)
9595 atts.push_back(pAtt);
9596 }
9597
9598 return S_OK;
9599}
9600
9601
9602/**
9603 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9604 * file if the machine name was changed and about creating a new settings file
9605 * if this is a new machine.
9606 *
9607 * @note Must be never called directly but only from #saveSettings().
9608 */
9609HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9610{
9611 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9612
9613 HRESULT rc = S_OK;
9614
9615 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9616
9617 /// @todo need to handle primary group change, too
9618
9619 /* attempt to rename the settings file if machine name is changed */
9620 if ( mUserData->s.fNameSync
9621 && mUserData.isBackedUp()
9622 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9623 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9624 )
9625 {
9626 bool dirRenamed = false;
9627 bool fileRenamed = false;
9628
9629 Utf8Str configFile, newConfigFile;
9630 Utf8Str configFilePrev, newConfigFilePrev;
9631 Utf8Str NVRAMFile, newNVRAMFile;
9632 Utf8Str configDir, newConfigDir;
9633
9634 do
9635 {
9636 int vrc = VINF_SUCCESS;
9637
9638 Utf8Str name = mUserData.backedUpData()->s.strName;
9639 Utf8Str newName = mUserData->s.strName;
9640 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9641 if (group == "/")
9642 group.setNull();
9643 Utf8Str newGroup = mUserData->s.llGroups.front();
9644 if (newGroup == "/")
9645 newGroup.setNull();
9646
9647 configFile = mData->m_strConfigFileFull;
9648
9649 /* first, rename the directory if it matches the group and machine name */
9650 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9651 /** @todo hack, make somehow use of ComposeMachineFilename */
9652 if (mUserData->s.fDirectoryIncludesUUID)
9653 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9654 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9655 /** @todo hack, make somehow use of ComposeMachineFilename */
9656 if (mUserData->s.fDirectoryIncludesUUID)
9657 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9658 configDir = configFile;
9659 configDir.stripFilename();
9660 newConfigDir = configDir;
9661 if ( configDir.length() >= groupPlusName.length()
9662 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9663 groupPlusName.c_str()))
9664 {
9665 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9666 Utf8Str newConfigBaseDir(newConfigDir);
9667 newConfigDir.append(newGroupPlusName);
9668 /* consistency: use \ if appropriate on the platform */
9669 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9670 /* new dir and old dir cannot be equal here because of 'if'
9671 * above and because name != newName */
9672 Assert(configDir != newConfigDir);
9673 if (!fSettingsFileIsNew)
9674 {
9675 /* perform real rename only if the machine is not new */
9676 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9677 if ( vrc == VERR_FILE_NOT_FOUND
9678 || vrc == VERR_PATH_NOT_FOUND)
9679 {
9680 /* create the parent directory, then retry renaming */
9681 Utf8Str parent(newConfigDir);
9682 parent.stripFilename();
9683 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9684 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9685 }
9686 if (RT_FAILURE(vrc))
9687 {
9688 rc = setErrorBoth(E_FAIL, vrc,
9689 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9690 configDir.c_str(),
9691 newConfigDir.c_str(),
9692 vrc);
9693 break;
9694 }
9695 /* delete subdirectories which are no longer needed */
9696 Utf8Str dir(configDir);
9697 dir.stripFilename();
9698 while (dir != newConfigBaseDir && dir != ".")
9699 {
9700 vrc = RTDirRemove(dir.c_str());
9701 if (RT_FAILURE(vrc))
9702 break;
9703 dir.stripFilename();
9704 }
9705 dirRenamed = true;
9706 }
9707 }
9708
9709 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9710
9711 /* then try to rename the settings file itself */
9712 if (newConfigFile != configFile)
9713 {
9714 /* get the path to old settings file in renamed directory */
9715 Assert(mData->m_strConfigFileFull == configFile);
9716 configFile.printf("%s%c%s",
9717 newConfigDir.c_str(),
9718 RTPATH_DELIMITER,
9719 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9720 if (!fSettingsFileIsNew)
9721 {
9722 /* perform real rename only if the machine is not new */
9723 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9724 if (RT_FAILURE(vrc))
9725 {
9726 rc = setErrorBoth(E_FAIL, vrc,
9727 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9728 configFile.c_str(),
9729 newConfigFile.c_str(),
9730 vrc);
9731 break;
9732 }
9733 fileRenamed = true;
9734 configFilePrev = configFile;
9735 configFilePrev += "-prev";
9736 newConfigFilePrev = newConfigFile;
9737 newConfigFilePrev += "-prev";
9738 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9739 NVRAMFile = mNvramStore->i_getNonVolatileStorageFile();
9740 if (NVRAMFile.isNotEmpty())
9741 {
9742 // in the NVRAM file path, replace the old directory with the new directory
9743 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9744 {
9745 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9746 NVRAMFile = newConfigDir + strNVRAMFile;
9747 }
9748 newNVRAMFile = newConfigFile;
9749 newNVRAMFile.stripSuffix();
9750 newNVRAMFile += ".nvram";
9751 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9752 }
9753 }
9754 }
9755
9756 // update m_strConfigFileFull amd mConfigFile
9757 mData->m_strConfigFileFull = newConfigFile;
9758 // compute the relative path too
9759 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9760
9761 // store the old and new so that VirtualBox::i_saveSettings() can update
9762 // the media registry
9763 if ( mData->mRegistered
9764 && (configDir != newConfigDir || configFile != newConfigFile))
9765 {
9766 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9767
9768 if (pfNeedsGlobalSaveSettings)
9769 *pfNeedsGlobalSaveSettings = true;
9770 }
9771
9772 // in the saved state file path, replace the old directory with the new directory
9773 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9774 {
9775 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9776 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9777 }
9778 if (newNVRAMFile.isNotEmpty())
9779 mNvramStore->i_updateNonVolatileStorageFile(newNVRAMFile);
9780
9781 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9782 if (mData->mFirstSnapshot)
9783 {
9784 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9785 newConfigDir.c_str());
9786 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9787 newConfigDir.c_str());
9788 }
9789 }
9790 while (0);
9791
9792 if (FAILED(rc))
9793 {
9794 /* silently try to rename everything back */
9795 if (fileRenamed)
9796 {
9797 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9798 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9799 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9800 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9801 }
9802 if (dirRenamed)
9803 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9804 }
9805
9806 if (FAILED(rc)) return rc;
9807 }
9808
9809 if (fSettingsFileIsNew)
9810 {
9811 /* create a virgin config file */
9812 int vrc = VINF_SUCCESS;
9813
9814 /* ensure the settings directory exists */
9815 Utf8Str path(mData->m_strConfigFileFull);
9816 path.stripFilename();
9817 if (!RTDirExists(path.c_str()))
9818 {
9819 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9820 if (RT_FAILURE(vrc))
9821 {
9822 return setErrorBoth(E_FAIL, vrc,
9823 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9824 path.c_str(),
9825 vrc);
9826 }
9827 }
9828
9829 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9830 path = Utf8Str(mData->m_strConfigFileFull);
9831 RTFILE f = NIL_RTFILE;
9832 vrc = RTFileOpen(&f, path.c_str(),
9833 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9834 if (RT_FAILURE(vrc))
9835 return setErrorBoth(E_FAIL, vrc,
9836 tr("Could not create the settings file '%s' (%Rrc)"),
9837 path.c_str(),
9838 vrc);
9839 RTFileClose(f);
9840 }
9841
9842 return rc;
9843}
9844
9845/**
9846 * Saves and commits machine data, user data and hardware data.
9847 *
9848 * Note that on failure, the data remains uncommitted.
9849 *
9850 * @a aFlags may combine the following flags:
9851 *
9852 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9853 * Used when saving settings after an operation that makes them 100%
9854 * correspond to the settings from the current snapshot.
9855 * - SaveS_Force: settings will be saved without doing a deep compare of the
9856 * settings structures. This is used when this is called because snapshots
9857 * have changed to avoid the overhead of the deep compare.
9858 *
9859 * @note Must be called from under this object's write lock. Locks children for
9860 * writing.
9861 *
9862 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9863 * initialized to false and that will be set to true by this function if
9864 * the caller must invoke VirtualBox::i_saveSettings() because the global
9865 * settings have changed. This will happen if a machine rename has been
9866 * saved and the global machine and media registries will therefore need
9867 * updating.
9868 * @param alock Reference to the lock for this machine object.
9869 * @param aFlags Flags.
9870 */
9871HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9872 AutoWriteLock &alock,
9873 int aFlags /*= 0*/)
9874{
9875 LogFlowThisFuncEnter();
9876
9877 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9878
9879 /* make sure child objects are unable to modify the settings while we are
9880 * saving them */
9881 i_ensureNoStateDependencies(alock);
9882
9883 AssertReturn(!i_isSnapshotMachine(),
9884 E_FAIL);
9885
9886 if (!mData->mAccessible)
9887 return setError(VBOX_E_INVALID_VM_STATE,
9888 tr("The machine is not accessible, so cannot save settings"));
9889
9890 HRESULT rc = S_OK;
9891 bool fNeedsWrite = false;
9892
9893 /* First, prepare to save settings. It will care about renaming the
9894 * settings directory and file if the machine name was changed and about
9895 * creating a new settings file if this is a new machine. */
9896 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9897 if (FAILED(rc)) return rc;
9898
9899 // keep a pointer to the current settings structures
9900 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9901 settings::MachineConfigFile *pNewConfig = NULL;
9902
9903 try
9904 {
9905 // make a fresh one to have everyone write stuff into
9906 pNewConfig = new settings::MachineConfigFile(NULL);
9907 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9908
9909 // now go and copy all the settings data from COM to the settings structures
9910 // (this calls i_saveSettings() on all the COM objects in the machine)
9911 i_copyMachineDataToSettings(*pNewConfig);
9912
9913 if (aFlags & SaveS_ResetCurStateModified)
9914 {
9915 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9916 mData->mCurrentStateModified = FALSE;
9917 fNeedsWrite = true; // always, no need to compare
9918 }
9919 else if (aFlags & SaveS_Force)
9920 {
9921 fNeedsWrite = true; // always, no need to compare
9922 }
9923 else
9924 {
9925 if (!mData->mCurrentStateModified)
9926 {
9927 // do a deep compare of the settings that we just saved with the settings
9928 // previously stored in the config file; this invokes MachineConfigFile::operator==
9929 // which does a deep compare of all the settings, which is expensive but less expensive
9930 // than writing out XML in vain
9931 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9932
9933 // could still be modified if any settings changed
9934 mData->mCurrentStateModified = fAnySettingsChanged;
9935
9936 fNeedsWrite = fAnySettingsChanged;
9937 }
9938 else
9939 fNeedsWrite = true;
9940 }
9941
9942 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9943
9944 if (fNeedsWrite)
9945 // now spit it all out!
9946 pNewConfig->write(mData->m_strConfigFileFull);
9947
9948 mData->pMachineConfigFile = pNewConfig;
9949 delete pOldConfig;
9950 i_commit();
9951
9952 // after saving settings, we are no longer different from the XML on disk
9953 mData->flModifications = 0;
9954 }
9955 catch (HRESULT err)
9956 {
9957 // we assume that error info is set by the thrower
9958 rc = err;
9959
9960 // restore old config
9961 delete pNewConfig;
9962 mData->pMachineConfigFile = pOldConfig;
9963 }
9964 catch (...)
9965 {
9966 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9967 }
9968
9969 if (fNeedsWrite)
9970 {
9971 /* Fire the data change event, even on failure (since we've already
9972 * committed all data). This is done only for SessionMachines because
9973 * mutable Machine instances are always not registered (i.e. private
9974 * to the client process that creates them) and thus don't need to
9975 * inform callbacks. */
9976 if (i_isSessionMachine())
9977 mParent->i_onMachineDataChanged(mData->mUuid);
9978 }
9979
9980 LogFlowThisFunc(("rc=%08X\n", rc));
9981 LogFlowThisFuncLeave();
9982 return rc;
9983}
9984
9985/**
9986 * Implementation for saving the machine settings into the given
9987 * settings::MachineConfigFile instance. This copies machine extradata
9988 * from the previous machine config file in the instance data, if any.
9989 *
9990 * This gets called from two locations:
9991 *
9992 * -- Machine::i_saveSettings(), during the regular XML writing;
9993 *
9994 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9995 * exported to OVF and we write the VirtualBox proprietary XML
9996 * into a <vbox:Machine> tag.
9997 *
9998 * This routine fills all the fields in there, including snapshots, *except*
9999 * for the following:
10000 *
10001 * -- fCurrentStateModified. There is some special logic associated with that.
10002 *
10003 * The caller can then call MachineConfigFile::write() or do something else
10004 * with it.
10005 *
10006 * Caller must hold the machine lock!
10007 *
10008 * This throws XML errors and HRESULT, so the caller must have a catch block!
10009 */
10010void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10011{
10012 // deep copy extradata, being extra careful with self assignment (the STL
10013 // map assignment on Mac OS X clang based Xcode isn't checking)
10014 if (&config != mData->pMachineConfigFile)
10015 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10016
10017 config.uuid = mData->mUuid;
10018
10019 // copy name, description, OS type, teleport, UTC etc.
10020 config.machineUserData = mUserData->s;
10021
10022 if ( mData->mMachineState == MachineState_Saved
10023 || mData->mMachineState == MachineState_AbortedSaved
10024 || mData->mMachineState == MachineState_Restoring
10025 // when doing certain snapshot operations we may or may not have
10026 // a saved state in the current state, so keep everything as is
10027 || ( ( mData->mMachineState == MachineState_Snapshotting
10028 || mData->mMachineState == MachineState_DeletingSnapshot
10029 || mData->mMachineState == MachineState_RestoringSnapshot)
10030 && (!mSSData->strStateFilePath.isEmpty())
10031 )
10032 )
10033 {
10034 Assert(!mSSData->strStateFilePath.isEmpty());
10035 /* try to make the file name relative to the settings file dir */
10036 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10037 }
10038 else
10039 {
10040 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10041 config.strStateFile.setNull();
10042 }
10043
10044 if (mData->mCurrentSnapshot)
10045 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10046 else
10047 config.uuidCurrentSnapshot.clear();
10048
10049 config.timeLastStateChange = mData->mLastStateChange;
10050 config.fAborted = (mData->mMachineState == MachineState_Aborted || mData->mMachineState == MachineState_AbortedSaved);
10051 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10052
10053 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10054 if (FAILED(rc)) throw rc;
10055
10056 // save machine's media registry if this is VirtualBox 4.0 or later
10057 if (config.canHaveOwnMediaRegistry())
10058 {
10059 // determine machine folder
10060 Utf8Str strMachineFolder = i_getSettingsFileFull();
10061 strMachineFolder.stripFilename();
10062 mParent->i_saveMediaRegistry(config.mediaRegistry,
10063 i_getId(), // only media with registry ID == machine UUID
10064 strMachineFolder);
10065 // this throws HRESULT
10066 }
10067
10068 // save snapshots
10069 rc = i_saveAllSnapshots(config);
10070 if (FAILED(rc)) throw rc;
10071}
10072
10073/**
10074 * Saves all snapshots of the machine into the given machine config file. Called
10075 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10076 * @param config
10077 * @return
10078 */
10079HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10080{
10081 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10082
10083 HRESULT rc = S_OK;
10084
10085 try
10086 {
10087 config.llFirstSnapshot.clear();
10088
10089 if (mData->mFirstSnapshot)
10090 {
10091 // the settings use a list for "the first snapshot"
10092 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10093
10094 // get reference to the snapshot on the list and work on that
10095 // element straight in the list to avoid excessive copying later
10096 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10097 if (FAILED(rc)) throw rc;
10098 }
10099
10100// if (mType == IsSessionMachine)
10101// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10102
10103 }
10104 catch (HRESULT err)
10105 {
10106 /* we assume that error info is set by the thrower */
10107 rc = err;
10108 }
10109 catch (...)
10110 {
10111 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10112 }
10113
10114 return rc;
10115}
10116
10117/**
10118 * Saves the VM hardware configuration. It is assumed that the
10119 * given node is empty.
10120 *
10121 * @param data Reference to the settings object for the hardware config.
10122 * @param pDbg Pointer to the settings object for the debugging config
10123 * which happens to live in mHWData.
10124 * @param pAutostart Pointer to the settings object for the autostart config
10125 * which happens to live in mHWData.
10126 */
10127HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10128 settings::Autostart *pAutostart)
10129{
10130 HRESULT rc = S_OK;
10131
10132 try
10133 {
10134 /* The hardware version attribute (optional).
10135 Automatically upgrade from 1 to current default hardware version
10136 when there is no saved state. (ugly!) */
10137 if ( mHWData->mHWVersion == "1"
10138 && mSSData->strStateFilePath.isEmpty()
10139 )
10140 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10141
10142 data.strVersion = mHWData->mHWVersion;
10143 data.uuid = mHWData->mHardwareUUID;
10144
10145 // CPU
10146 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10147 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10148 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10149 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10150 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10151 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10152 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10153 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10154 data.fPAE = !!mHWData->mPAEEnabled;
10155 data.enmLongMode = mHWData->mLongMode;
10156 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10157 data.fAPIC = !!mHWData->mAPIC;
10158 data.fX2APIC = !!mHWData->mX2APIC;
10159 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10160 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10161 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10162 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10163 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10164 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10165 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10166 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10167 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10168 data.cCPUs = mHWData->mCPUCount;
10169 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10170 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10171 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10172 data.strCpuProfile = mHWData->mCpuProfile;
10173
10174 data.llCpus.clear();
10175 if (data.fCpuHotPlug)
10176 {
10177 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10178 {
10179 if (mHWData->mCPUAttached[idx])
10180 {
10181 settings::Cpu cpu;
10182 cpu.ulId = idx;
10183 data.llCpus.push_back(cpu);
10184 }
10185 }
10186 }
10187
10188 /* Standard and Extended CPUID leafs. */
10189 data.llCpuIdLeafs.clear();
10190 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10191
10192 // memory
10193 data.ulMemorySizeMB = mHWData->mMemorySize;
10194 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10195
10196 // firmware
10197 data.firmwareType = mHWData->mFirmwareType;
10198
10199 // HID
10200 data.pointingHIDType = mHWData->mPointingHIDType;
10201 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10202
10203 // chipset
10204 data.chipsetType = mHWData->mChipsetType;
10205
10206 // iommu
10207 data.iommuType = mHWData->mIommuType;
10208
10209 // paravirt
10210 data.paravirtProvider = mHWData->mParavirtProvider;
10211 data.strParavirtDebug = mHWData->mParavirtDebug;
10212
10213 // emulated USB card reader
10214 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10215
10216 // HPET
10217 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10218
10219 // boot order
10220 data.mapBootOrder.clear();
10221 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10222 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10223
10224 /* VRDEServer settings (optional) */
10225 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10226 if (FAILED(rc)) throw rc;
10227
10228 /* BIOS settings (required) */
10229 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10230 if (FAILED(rc)) throw rc;
10231
10232 /* Trusted Platform Module settings (required) */
10233 rc = mTrustedPlatformModule->i_saveSettings(data.tpmSettings);
10234 if (FAILED(rc)) throw rc;
10235
10236 /* NVRAM settings (required) */
10237 rc = mNvramStore->i_saveSettings(data.nvramSettings);
10238 if (FAILED(rc)) throw rc;
10239
10240 /* Recording settings (required) */
10241 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10242 if (FAILED(rc)) throw rc;
10243
10244 /* GraphicsAdapter settings (required) */
10245 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10246 if (FAILED(rc)) throw rc;
10247
10248 /* USB Controller (required) */
10249 data.usbSettings.llUSBControllers.clear();
10250 for (USBControllerList::const_iterator
10251 it = mUSBControllers->begin();
10252 it != mUSBControllers->end();
10253 ++it)
10254 {
10255 ComObjPtr<USBController> ctrl = *it;
10256 settings::USBController settingsCtrl;
10257
10258 settingsCtrl.strName = ctrl->i_getName();
10259 settingsCtrl.enmType = ctrl->i_getControllerType();
10260
10261 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10262 }
10263
10264 /* USB device filters (required) */
10265 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10266 if (FAILED(rc)) throw rc;
10267
10268 /* Network adapters (required) */
10269 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10270 data.llNetworkAdapters.clear();
10271 /* Write out only the nominal number of network adapters for this
10272 * chipset type. Since Machine::commit() hasn't been called there
10273 * may be extra NIC settings in the vector. */
10274 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10275 {
10276 settings::NetworkAdapter nic;
10277 nic.ulSlot = (uint32_t)slot;
10278 /* paranoia check... must not be NULL, but must not crash either. */
10279 if (mNetworkAdapters[slot])
10280 {
10281 if (mNetworkAdapters[slot]->i_hasDefaults())
10282 continue;
10283
10284 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10285 if (FAILED(rc)) throw rc;
10286
10287 data.llNetworkAdapters.push_back(nic);
10288 }
10289 }
10290
10291 /* Serial ports */
10292 data.llSerialPorts.clear();
10293 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10294 {
10295 if (mSerialPorts[slot]->i_hasDefaults())
10296 continue;
10297
10298 settings::SerialPort s;
10299 s.ulSlot = slot;
10300 rc = mSerialPorts[slot]->i_saveSettings(s);
10301 if (FAILED(rc)) return rc;
10302
10303 data.llSerialPorts.push_back(s);
10304 }
10305
10306 /* Parallel ports */
10307 data.llParallelPorts.clear();
10308 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10309 {
10310 if (mParallelPorts[slot]->i_hasDefaults())
10311 continue;
10312
10313 settings::ParallelPort p;
10314 p.ulSlot = slot;
10315 rc = mParallelPorts[slot]->i_saveSettings(p);
10316 if (FAILED(rc)) return rc;
10317
10318 data.llParallelPorts.push_back(p);
10319 }
10320
10321 /* Audio adapter */
10322 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10323 if (FAILED(rc)) return rc;
10324
10325 rc = i_saveStorageControllers(data.storage);
10326 if (FAILED(rc)) return rc;
10327
10328 /* Shared folders */
10329 data.llSharedFolders.clear();
10330 for (HWData::SharedFolderList::const_iterator
10331 it = mHWData->mSharedFolders.begin();
10332 it != mHWData->mSharedFolders.end();
10333 ++it)
10334 {
10335 SharedFolder *pSF = *it;
10336 AutoCaller sfCaller(pSF);
10337 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10338 settings::SharedFolder sf;
10339 sf.strName = pSF->i_getName();
10340 sf.strHostPath = pSF->i_getHostPath();
10341 sf.fWritable = !!pSF->i_isWritable();
10342 sf.fAutoMount = !!pSF->i_isAutoMounted();
10343 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10344
10345 data.llSharedFolders.push_back(sf);
10346 }
10347
10348 // clipboard
10349 data.clipboardMode = mHWData->mClipboardMode;
10350 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10351
10352 // drag'n'drop
10353 data.dndMode = mHWData->mDnDMode;
10354
10355 /* Guest */
10356 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10357
10358 // IO settings
10359 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10360 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10361
10362 /* BandwidthControl (required) */
10363 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10364 if (FAILED(rc)) throw rc;
10365
10366 /* Host PCI devices */
10367 data.pciAttachments.clear();
10368 for (HWData::PCIDeviceAssignmentList::const_iterator
10369 it = mHWData->mPCIDeviceAssignments.begin();
10370 it != mHWData->mPCIDeviceAssignments.end();
10371 ++it)
10372 {
10373 ComObjPtr<PCIDeviceAttachment> pda = *it;
10374 settings::HostPCIDeviceAttachment hpda;
10375
10376 rc = pda->i_saveSettings(hpda);
10377 if (FAILED(rc)) throw rc;
10378
10379 data.pciAttachments.push_back(hpda);
10380 }
10381
10382 // guest properties
10383 data.llGuestProperties.clear();
10384#ifdef VBOX_WITH_GUEST_PROPS
10385 for (HWData::GuestPropertyMap::const_iterator
10386 it = mHWData->mGuestProperties.begin();
10387 it != mHWData->mGuestProperties.end();
10388 ++it)
10389 {
10390 HWData::GuestProperty property = it->second;
10391
10392 /* Remove transient guest properties at shutdown unless we
10393 * are saving state. Note that restoring snapshot intentionally
10394 * keeps them, they will be removed if appropriate once the final
10395 * machine state is set (as crashes etc. need to work). */
10396 if ( ( mData->mMachineState == MachineState_PoweredOff
10397 || mData->mMachineState == MachineState_Aborted
10398 || mData->mMachineState == MachineState_Teleported)
10399 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10400 continue;
10401 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10402 prop.strName = it->first;
10403 prop.strValue = property.strValue;
10404 prop.timestamp = (uint64_t)property.mTimestamp;
10405 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10406 GuestPropWriteFlags(property.mFlags, szFlags);
10407 prop.strFlags = szFlags;
10408
10409 data.llGuestProperties.push_back(prop);
10410 }
10411
10412 /* I presume this doesn't require a backup(). */
10413 mData->mGuestPropertiesModified = FALSE;
10414#endif /* VBOX_WITH_GUEST_PROPS defined */
10415
10416 *pDbg = mHWData->mDebugging;
10417 *pAutostart = mHWData->mAutostart;
10418
10419 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10420 }
10421 catch (std::bad_alloc &)
10422 {
10423 return E_OUTOFMEMORY;
10424 }
10425
10426 AssertComRC(rc);
10427 return rc;
10428}
10429
10430/**
10431 * Saves the storage controller configuration.
10432 *
10433 * @param data storage settings.
10434 */
10435HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10436{
10437 data.llStorageControllers.clear();
10438
10439 for (StorageControllerList::const_iterator
10440 it = mStorageControllers->begin();
10441 it != mStorageControllers->end();
10442 ++it)
10443 {
10444 HRESULT rc;
10445 ComObjPtr<StorageController> pCtl = *it;
10446
10447 settings::StorageController ctl;
10448 ctl.strName = pCtl->i_getName();
10449 ctl.controllerType = pCtl->i_getControllerType();
10450 ctl.storageBus = pCtl->i_getStorageBus();
10451 ctl.ulInstance = pCtl->i_getInstance();
10452 ctl.fBootable = pCtl->i_getBootable();
10453
10454 /* Save the port count. */
10455 ULONG portCount;
10456 rc = pCtl->COMGETTER(PortCount)(&portCount);
10457 ComAssertComRCRet(rc, rc);
10458 ctl.ulPortCount = portCount;
10459
10460 /* Save fUseHostIOCache */
10461 BOOL fUseHostIOCache;
10462 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10463 ComAssertComRCRet(rc, rc);
10464 ctl.fUseHostIOCache = !!fUseHostIOCache;
10465
10466 /* save the devices now. */
10467 rc = i_saveStorageDevices(pCtl, ctl);
10468 ComAssertComRCRet(rc, rc);
10469
10470 data.llStorageControllers.push_back(ctl);
10471 }
10472
10473 return S_OK;
10474}
10475
10476/**
10477 * Saves the hard disk configuration.
10478 */
10479HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10480 settings::StorageController &data)
10481{
10482 MediumAttachmentList atts;
10483
10484 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10485 if (FAILED(rc)) return rc;
10486
10487 data.llAttachedDevices.clear();
10488 for (MediumAttachmentList::const_iterator
10489 it = atts.begin();
10490 it != atts.end();
10491 ++it)
10492 {
10493 settings::AttachedDevice dev;
10494 IMediumAttachment *iA = *it;
10495 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10496 Medium *pMedium = pAttach->i_getMedium();
10497
10498 dev.deviceType = pAttach->i_getType();
10499 dev.lPort = pAttach->i_getPort();
10500 dev.lDevice = pAttach->i_getDevice();
10501 dev.fPassThrough = pAttach->i_getPassthrough();
10502 dev.fHotPluggable = pAttach->i_getHotPluggable();
10503 if (pMedium)
10504 {
10505 if (pMedium->i_isHostDrive())
10506 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10507 else
10508 dev.uuid = pMedium->i_getId();
10509 dev.fTempEject = pAttach->i_getTempEject();
10510 dev.fNonRotational = pAttach->i_getNonRotational();
10511 dev.fDiscard = pAttach->i_getDiscard();
10512 }
10513
10514 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10515
10516 data.llAttachedDevices.push_back(dev);
10517 }
10518
10519 return S_OK;
10520}
10521
10522/**
10523 * Saves machine state settings as defined by aFlags
10524 * (SaveSTS_* values).
10525 *
10526 * @param aFlags Combination of SaveSTS_* flags.
10527 *
10528 * @note Locks objects for writing.
10529 */
10530HRESULT Machine::i_saveStateSettings(int aFlags)
10531{
10532 if (aFlags == 0)
10533 return S_OK;
10534
10535 AutoCaller autoCaller(this);
10536 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10537
10538 /* This object's write lock is also necessary to serialize file access
10539 * (prevent concurrent reads and writes) */
10540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10541
10542 HRESULT rc = S_OK;
10543
10544 Assert(mData->pMachineConfigFile);
10545
10546 try
10547 {
10548 if (aFlags & SaveSTS_CurStateModified)
10549 mData->pMachineConfigFile->fCurrentStateModified = true;
10550
10551 if (aFlags & SaveSTS_StateFilePath)
10552 {
10553 if (!mSSData->strStateFilePath.isEmpty())
10554 /* try to make the file name relative to the settings file dir */
10555 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10556 else
10557 mData->pMachineConfigFile->strStateFile.setNull();
10558 }
10559
10560 if (aFlags & SaveSTS_StateTimeStamp)
10561 {
10562 Assert( mData->mMachineState != MachineState_Aborted
10563 || mSSData->strStateFilePath.isEmpty());
10564
10565 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10566
10567 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted
10568 || mData->mMachineState == MachineState_AbortedSaved);
10569/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10570 }
10571
10572 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10573 }
10574 catch (...)
10575 {
10576 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10577 }
10578
10579 return rc;
10580}
10581
10582/**
10583 * Ensures that the given medium is added to a media registry. If this machine
10584 * was created with 4.0 or later, then the machine registry is used. Otherwise
10585 * the global VirtualBox media registry is used.
10586 *
10587 * Caller must NOT hold machine lock, media tree or any medium locks!
10588 *
10589 * @param pMedium
10590 */
10591void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10592{
10593 /* Paranoia checks: do not hold machine or media tree locks. */
10594 AssertReturnVoid(!isWriteLockOnCurrentThread());
10595 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10596
10597 ComObjPtr<Medium> pBase;
10598 {
10599 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10600 pBase = pMedium->i_getBase();
10601 }
10602
10603 /* Paranoia checks: do not hold medium locks. */
10604 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10605 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10606
10607 // decide which medium registry to use now that the medium is attached:
10608 Guid uuid;
10609 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10610 if (fCanHaveOwnMediaRegistry)
10611 // machine XML is VirtualBox 4.0 or higher:
10612 uuid = i_getId(); // machine UUID
10613 else
10614 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10615
10616 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10617 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10618 if (pMedium->i_addRegistry(uuid))
10619 mParent->i_markRegistryModified(uuid);
10620
10621 /* For more complex hard disk structures it can happen that the base
10622 * medium isn't yet associated with any medium registry. Do that now. */
10623 if (pMedium != pBase)
10624 {
10625 /* Tree lock needed by Medium::addRegistry when recursing. */
10626 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10627 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10628 {
10629 treeLock.release();
10630 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10631 treeLock.acquire();
10632 }
10633 if (pBase->i_addRegistryRecursive(uuid))
10634 {
10635 treeLock.release();
10636 mParent->i_markRegistryModified(uuid);
10637 }
10638 }
10639}
10640
10641/**
10642 * Creates differencing hard disks for all normal hard disks attached to this
10643 * machine and a new set of attachments to refer to created disks.
10644 *
10645 * Used when taking a snapshot or when deleting the current state. Gets called
10646 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10647 *
10648 * This method assumes that mMediumAttachments contains the original hard disk
10649 * attachments it needs to create diffs for. On success, these attachments will
10650 * be replaced with the created diffs.
10651 *
10652 * Attachments with non-normal hard disks are left as is.
10653 *
10654 * If @a aOnline is @c false then the original hard disks that require implicit
10655 * diffs will be locked for reading. Otherwise it is assumed that they are
10656 * already locked for writing (when the VM was started). Note that in the latter
10657 * case it is responsibility of the caller to lock the newly created diffs for
10658 * writing if this method succeeds.
10659 *
10660 * @param aProgress Progress object to run (must contain at least as
10661 * many operations left as the number of hard disks
10662 * attached).
10663 * @param aWeight Weight of this operation.
10664 * @param aOnline Whether the VM was online prior to this operation.
10665 *
10666 * @note The progress object is not marked as completed, neither on success nor
10667 * on failure. This is a responsibility of the caller.
10668 *
10669 * @note Locks this object and the media tree for writing.
10670 */
10671HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10672 ULONG aWeight,
10673 bool aOnline)
10674{
10675 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10676
10677 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10678 AssertReturn(!!pProgressControl, E_INVALIDARG);
10679
10680 AutoCaller autoCaller(this);
10681 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10682
10683 AutoMultiWriteLock2 alock(this->lockHandle(),
10684 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10685
10686 /* must be in a protective state because we release the lock below */
10687 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10688 || mData->mMachineState == MachineState_OnlineSnapshotting
10689 || mData->mMachineState == MachineState_LiveSnapshotting
10690 || mData->mMachineState == MachineState_RestoringSnapshot
10691 || mData->mMachineState == MachineState_DeletingSnapshot
10692 , E_FAIL);
10693
10694 HRESULT rc = S_OK;
10695
10696 // use appropriate locked media map (online or offline)
10697 MediumLockListMap lockedMediaOffline;
10698 MediumLockListMap *lockedMediaMap;
10699 if (aOnline)
10700 lockedMediaMap = &mData->mSession.mLockedMedia;
10701 else
10702 lockedMediaMap = &lockedMediaOffline;
10703
10704 try
10705 {
10706 if (!aOnline)
10707 {
10708 /* lock all attached hard disks early to detect "in use"
10709 * situations before creating actual diffs */
10710 for (MediumAttachmentList::const_iterator
10711 it = mMediumAttachments->begin();
10712 it != mMediumAttachments->end();
10713 ++it)
10714 {
10715 MediumAttachment *pAtt = *it;
10716 if (pAtt->i_getType() == DeviceType_HardDisk)
10717 {
10718 Medium *pMedium = pAtt->i_getMedium();
10719 Assert(pMedium);
10720
10721 MediumLockList *pMediumLockList(new MediumLockList());
10722 alock.release();
10723 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10724 NULL /* pToLockWrite */,
10725 false /* fMediumLockWriteAll */,
10726 NULL,
10727 *pMediumLockList);
10728 alock.acquire();
10729 if (FAILED(rc))
10730 {
10731 delete pMediumLockList;
10732 throw rc;
10733 }
10734 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10735 if (FAILED(rc))
10736 {
10737 throw setError(rc,
10738 tr("Collecting locking information for all attached media failed"));
10739 }
10740 }
10741 }
10742
10743 /* Now lock all media. If this fails, nothing is locked. */
10744 alock.release();
10745 rc = lockedMediaMap->Lock();
10746 alock.acquire();
10747 if (FAILED(rc))
10748 {
10749 throw setError(rc,
10750 tr("Locking of attached media failed"));
10751 }
10752 }
10753
10754 /* remember the current list (note that we don't use backup() since
10755 * mMediumAttachments may be already backed up) */
10756 MediumAttachmentList atts = *mMediumAttachments.data();
10757
10758 /* start from scratch */
10759 mMediumAttachments->clear();
10760
10761 /* go through remembered attachments and create diffs for normal hard
10762 * disks and attach them */
10763 for (MediumAttachmentList::const_iterator
10764 it = atts.begin();
10765 it != atts.end();
10766 ++it)
10767 {
10768 MediumAttachment *pAtt = *it;
10769
10770 DeviceType_T devType = pAtt->i_getType();
10771 Medium *pMedium = pAtt->i_getMedium();
10772
10773 if ( devType != DeviceType_HardDisk
10774 || pMedium == NULL
10775 || pMedium->i_getType() != MediumType_Normal)
10776 {
10777 /* copy the attachment as is */
10778
10779 /** @todo the progress object created in SessionMachine::TakeSnaphot
10780 * only expects operations for hard disks. Later other
10781 * device types need to show up in the progress as well. */
10782 if (devType == DeviceType_HardDisk)
10783 {
10784 if (pMedium == NULL)
10785 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10786 aWeight); // weight
10787 else
10788 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10789 pMedium->i_getBase()->i_getName().c_str()).raw(),
10790 aWeight); // weight
10791 }
10792
10793 mMediumAttachments->push_back(pAtt);
10794 continue;
10795 }
10796
10797 /* need a diff */
10798 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10799 pMedium->i_getBase()->i_getName().c_str()).raw(),
10800 aWeight); // weight
10801
10802 Utf8Str strFullSnapshotFolder;
10803 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10804
10805 ComObjPtr<Medium> diff;
10806 diff.createObject();
10807 // store the diff in the same registry as the parent
10808 // (this cannot fail here because we can't create implicit diffs for
10809 // unregistered images)
10810 Guid uuidRegistryParent;
10811 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10812 Assert(fInRegistry); NOREF(fInRegistry);
10813 rc = diff->init(mParent,
10814 pMedium->i_getPreferredDiffFormat(),
10815 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10816 uuidRegistryParent,
10817 DeviceType_HardDisk);
10818 if (FAILED(rc)) throw rc;
10819
10820 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10821 * the push_back? Looks like we're going to release medium with the
10822 * wrong kind of lock (general issue with if we fail anywhere at all)
10823 * and an orphaned VDI in the snapshots folder. */
10824
10825 /* update the appropriate lock list */
10826 MediumLockList *pMediumLockList;
10827 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10828 AssertComRCThrowRC(rc);
10829 if (aOnline)
10830 {
10831 alock.release();
10832 /* The currently attached medium will be read-only, change
10833 * the lock type to read. */
10834 rc = pMediumLockList->Update(pMedium, false);
10835 alock.acquire();
10836 AssertComRCThrowRC(rc);
10837 }
10838
10839 /* release the locks before the potentially lengthy operation */
10840 alock.release();
10841 rc = pMedium->i_createDiffStorage(diff,
10842 pMedium->i_getPreferredDiffVariant(),
10843 pMediumLockList,
10844 NULL /* aProgress */,
10845 true /* aWait */,
10846 false /* aNotify */);
10847 alock.acquire();
10848 if (FAILED(rc)) throw rc;
10849
10850 /* actual lock list update is done in Machine::i_commitMedia */
10851
10852 rc = diff->i_addBackReference(mData->mUuid);
10853 AssertComRCThrowRC(rc);
10854
10855 /* add a new attachment */
10856 ComObjPtr<MediumAttachment> attachment;
10857 attachment.createObject();
10858 rc = attachment->init(this,
10859 diff,
10860 pAtt->i_getControllerName(),
10861 pAtt->i_getPort(),
10862 pAtt->i_getDevice(),
10863 DeviceType_HardDisk,
10864 true /* aImplicit */,
10865 false /* aPassthrough */,
10866 false /* aTempEject */,
10867 pAtt->i_getNonRotational(),
10868 pAtt->i_getDiscard(),
10869 pAtt->i_getHotPluggable(),
10870 pAtt->i_getBandwidthGroup());
10871 if (FAILED(rc)) throw rc;
10872
10873 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10874 AssertComRCThrowRC(rc);
10875 mMediumAttachments->push_back(attachment);
10876 }
10877 }
10878 catch (HRESULT aRC) { rc = aRC; }
10879
10880 /* unlock all hard disks we locked when there is no VM */
10881 if (!aOnline)
10882 {
10883 ErrorInfoKeeper eik;
10884
10885 HRESULT rc1 = lockedMediaMap->Clear();
10886 AssertComRC(rc1);
10887 }
10888
10889 return rc;
10890}
10891
10892/**
10893 * Deletes implicit differencing hard disks created either by
10894 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10895 * mMediumAttachments.
10896 *
10897 * Note that to delete hard disks created by #attachDevice() this method is
10898 * called from #i_rollbackMedia() when the changes are rolled back.
10899 *
10900 * @note Locks this object and the media tree for writing.
10901 */
10902HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10903{
10904 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10905
10906 AutoCaller autoCaller(this);
10907 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10908
10909 AutoMultiWriteLock2 alock(this->lockHandle(),
10910 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10911
10912 /* We absolutely must have backed up state. */
10913 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10914
10915 /* Check if there are any implicitly created diff images. */
10916 bool fImplicitDiffs = false;
10917 for (MediumAttachmentList::const_iterator
10918 it = mMediumAttachments->begin();
10919 it != mMediumAttachments->end();
10920 ++it)
10921 {
10922 const ComObjPtr<MediumAttachment> &pAtt = *it;
10923 if (pAtt->i_isImplicit())
10924 {
10925 fImplicitDiffs = true;
10926 break;
10927 }
10928 }
10929 /* If there is nothing to do, leave early. This saves lots of image locking
10930 * effort. It also avoids a MachineStateChanged event without real reason.
10931 * This is important e.g. when loading a VM config, because there should be
10932 * no events. Otherwise API clients can become thoroughly confused for
10933 * inaccessible VMs (the code for loading VM configs uses this method for
10934 * cleanup if the config makes no sense), as they take such events as an
10935 * indication that the VM is alive, and they would force the VM config to
10936 * be reread, leading to an endless loop. */
10937 if (!fImplicitDiffs)
10938 return S_OK;
10939
10940 HRESULT rc = S_OK;
10941 MachineState_T oldState = mData->mMachineState;
10942
10943 /* will release the lock before the potentially lengthy operation,
10944 * so protect with the special state (unless already protected) */
10945 if ( oldState != MachineState_Snapshotting
10946 && oldState != MachineState_OnlineSnapshotting
10947 && oldState != MachineState_LiveSnapshotting
10948 && oldState != MachineState_RestoringSnapshot
10949 && oldState != MachineState_DeletingSnapshot
10950 && oldState != MachineState_DeletingSnapshotOnline
10951 && oldState != MachineState_DeletingSnapshotPaused
10952 )
10953 i_setMachineState(MachineState_SettingUp);
10954
10955 // use appropriate locked media map (online or offline)
10956 MediumLockListMap lockedMediaOffline;
10957 MediumLockListMap *lockedMediaMap;
10958 if (aOnline)
10959 lockedMediaMap = &mData->mSession.mLockedMedia;
10960 else
10961 lockedMediaMap = &lockedMediaOffline;
10962
10963 try
10964 {
10965 if (!aOnline)
10966 {
10967 /* lock all attached hard disks early to detect "in use"
10968 * situations before deleting actual diffs */
10969 for (MediumAttachmentList::const_iterator
10970 it = mMediumAttachments->begin();
10971 it != mMediumAttachments->end();
10972 ++it)
10973 {
10974 MediumAttachment *pAtt = *it;
10975 if (pAtt->i_getType() == DeviceType_HardDisk)
10976 {
10977 Medium *pMedium = pAtt->i_getMedium();
10978 Assert(pMedium);
10979
10980 MediumLockList *pMediumLockList(new MediumLockList());
10981 alock.release();
10982 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10983 NULL /* pToLockWrite */,
10984 false /* fMediumLockWriteAll */,
10985 NULL,
10986 *pMediumLockList);
10987 alock.acquire();
10988
10989 if (FAILED(rc))
10990 {
10991 delete pMediumLockList;
10992 throw rc;
10993 }
10994
10995 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10996 if (FAILED(rc))
10997 throw rc;
10998 }
10999 }
11000
11001 if (FAILED(rc))
11002 throw rc;
11003 } // end of offline
11004
11005 /* Lock lists are now up to date and include implicitly created media */
11006
11007 /* Go through remembered attachments and delete all implicitly created
11008 * diffs and fix up the attachment information */
11009 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11010 MediumAttachmentList implicitAtts;
11011 for (MediumAttachmentList::const_iterator
11012 it = mMediumAttachments->begin();
11013 it != mMediumAttachments->end();
11014 ++it)
11015 {
11016 ComObjPtr<MediumAttachment> pAtt = *it;
11017 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11018 if (pMedium.isNull())
11019 continue;
11020
11021 // Implicit attachments go on the list for deletion and back references are removed.
11022 if (pAtt->i_isImplicit())
11023 {
11024 /* Deassociate and mark for deletion */
11025 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11026 rc = pMedium->i_removeBackReference(mData->mUuid);
11027 if (FAILED(rc))
11028 throw rc;
11029 implicitAtts.push_back(pAtt);
11030 continue;
11031 }
11032
11033 /* Was this medium attached before? */
11034 if (!i_findAttachment(oldAtts, pMedium))
11035 {
11036 /* no: de-associate */
11037 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11038 rc = pMedium->i_removeBackReference(mData->mUuid);
11039 if (FAILED(rc))
11040 throw rc;
11041 continue;
11042 }
11043 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11044 }
11045
11046 /* If there are implicit attachments to delete, throw away the lock
11047 * map contents (which will unlock all media) since the medium
11048 * attachments will be rolled back. Below we need to completely
11049 * recreate the lock map anyway since it is infinitely complex to
11050 * do this incrementally (would need reconstructing each attachment
11051 * change, which would be extremely hairy). */
11052 if (implicitAtts.size() != 0)
11053 {
11054 ErrorInfoKeeper eik;
11055
11056 HRESULT rc1 = lockedMediaMap->Clear();
11057 AssertComRC(rc1);
11058 }
11059
11060 /* rollback hard disk changes */
11061 mMediumAttachments.rollback();
11062
11063 MultiResult mrc(S_OK);
11064
11065 // Delete unused implicit diffs.
11066 if (implicitAtts.size() != 0)
11067 {
11068 alock.release();
11069
11070 for (MediumAttachmentList::const_iterator
11071 it = implicitAtts.begin();
11072 it != implicitAtts.end();
11073 ++it)
11074 {
11075 // Remove medium associated with this attachment.
11076 ComObjPtr<MediumAttachment> pAtt = *it;
11077 Assert(pAtt);
11078 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11079 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11080 Assert(pMedium);
11081
11082 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11083 // continue on delete failure, just collect error messages
11084 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11085 pMedium->i_getLocationFull().c_str() ));
11086 mrc = rc;
11087 }
11088 // Clear the list of deleted implicit attachments now, while not
11089 // holding the lock, as it will ultimately trigger Medium::uninit()
11090 // calls which assume that the media tree lock isn't held.
11091 implicitAtts.clear();
11092
11093 alock.acquire();
11094
11095 /* if there is a VM recreate media lock map as mentioned above,
11096 * otherwise it is a waste of time and we leave things unlocked */
11097 if (aOnline)
11098 {
11099 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11100 /* must never be NULL, but better safe than sorry */
11101 if (!pMachine.isNull())
11102 {
11103 alock.release();
11104 rc = mData->mSession.mMachine->i_lockMedia();
11105 alock.acquire();
11106 if (FAILED(rc))
11107 throw rc;
11108 }
11109 }
11110 }
11111 }
11112 catch (HRESULT aRC) {rc = aRC;}
11113
11114 if (mData->mMachineState == MachineState_SettingUp)
11115 i_setMachineState(oldState);
11116
11117 /* unlock all hard disks we locked when there is no VM */
11118 if (!aOnline)
11119 {
11120 ErrorInfoKeeper eik;
11121
11122 HRESULT rc1 = lockedMediaMap->Clear();
11123 AssertComRC(rc1);
11124 }
11125
11126 return rc;
11127}
11128
11129
11130/**
11131 * Looks through the given list of media attachments for one with the given parameters
11132 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11133 * can be searched as well if needed.
11134 *
11135 * @param ll
11136 * @param aControllerName
11137 * @param aControllerPort
11138 * @param aDevice
11139 * @return
11140 */
11141MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11142 const Utf8Str &aControllerName,
11143 LONG aControllerPort,
11144 LONG aDevice)
11145{
11146 for (MediumAttachmentList::const_iterator
11147 it = ll.begin();
11148 it != ll.end();
11149 ++it)
11150 {
11151 MediumAttachment *pAttach = *it;
11152 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11153 return pAttach;
11154 }
11155
11156 return NULL;
11157}
11158
11159/**
11160 * Looks through the given list of media attachments for one with the given parameters
11161 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11162 * can be searched as well if needed.
11163 *
11164 * @param ll
11165 * @param pMedium
11166 * @return
11167 */
11168MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11169 ComObjPtr<Medium> pMedium)
11170{
11171 for (MediumAttachmentList::const_iterator
11172 it = ll.begin();
11173 it != ll.end();
11174 ++it)
11175 {
11176 MediumAttachment *pAttach = *it;
11177 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11178 if (pMediumThis == pMedium)
11179 return pAttach;
11180 }
11181
11182 return NULL;
11183}
11184
11185/**
11186 * Looks through the given list of media attachments for one with the given parameters
11187 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11188 * can be searched as well if needed.
11189 *
11190 * @param ll
11191 * @param id
11192 * @return
11193 */
11194MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11195 Guid &id)
11196{
11197 for (MediumAttachmentList::const_iterator
11198 it = ll.begin();
11199 it != ll.end();
11200 ++it)
11201 {
11202 MediumAttachment *pAttach = *it;
11203 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11204 if (pMediumThis->i_getId() == id)
11205 return pAttach;
11206 }
11207
11208 return NULL;
11209}
11210
11211/**
11212 * Main implementation for Machine::DetachDevice. This also gets called
11213 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11214 *
11215 * @param pAttach Medium attachment to detach.
11216 * @param writeLock Machine write lock which the caller must have locked once.
11217 * This may be released temporarily in here.
11218 * @param pSnapshot If NULL, then the detachment is for the current machine.
11219 * Otherwise this is for a SnapshotMachine, and this must be
11220 * its snapshot.
11221 * @return
11222 */
11223HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11224 AutoWriteLock &writeLock,
11225 Snapshot *pSnapshot)
11226{
11227 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11228 DeviceType_T mediumType = pAttach->i_getType();
11229
11230 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11231
11232 if (pAttach->i_isImplicit())
11233 {
11234 /* attempt to implicitly delete the implicitly created diff */
11235
11236 /// @todo move the implicit flag from MediumAttachment to Medium
11237 /// and forbid any hard disk operation when it is implicit. Or maybe
11238 /// a special media state for it to make it even more simple.
11239
11240 Assert(mMediumAttachments.isBackedUp());
11241
11242 /* will release the lock before the potentially lengthy operation, so
11243 * protect with the special state */
11244 MachineState_T oldState = mData->mMachineState;
11245 i_setMachineState(MachineState_SettingUp);
11246
11247 writeLock.release();
11248
11249 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11250 true /*aWait*/,
11251 false /*aNotify*/);
11252
11253 writeLock.acquire();
11254
11255 i_setMachineState(oldState);
11256
11257 if (FAILED(rc)) return rc;
11258 }
11259
11260 i_setModified(IsModified_Storage);
11261 mMediumAttachments.backup();
11262 mMediumAttachments->remove(pAttach);
11263
11264 if (!oldmedium.isNull())
11265 {
11266 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11267 if (pSnapshot)
11268 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11269 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11270 else if (mediumType != DeviceType_HardDisk)
11271 oldmedium->i_removeBackReference(mData->mUuid);
11272 }
11273
11274 return S_OK;
11275}
11276
11277/**
11278 * Goes thru all media of the given list and
11279 *
11280 * 1) calls i_detachDevice() on each of them for this machine and
11281 * 2) adds all Medium objects found in the process to the given list,
11282 * depending on cleanupMode.
11283 *
11284 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11285 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11286 * media to the list.
11287 *
11288 * This gets called from Machine::Unregister, both for the actual Machine and
11289 * the SnapshotMachine objects that might be found in the snapshots.
11290 *
11291 * Requires caller and locking. The machine lock must be passed in because it
11292 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11293 *
11294 * @param writeLock Machine lock from top-level caller; this gets passed to
11295 * i_detachDevice.
11296 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11297 * object if called for a SnapshotMachine.
11298 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11299 * added to llMedia; if Full, then all media get added;
11300 * otherwise no media get added.
11301 * @param llMedia Caller's list to receive Medium objects which got detached so
11302 * caller can close() them, depending on cleanupMode.
11303 * @return
11304 */
11305HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11306 Snapshot *pSnapshot,
11307 CleanupMode_T cleanupMode,
11308 MediaList &llMedia)
11309{
11310 Assert(isWriteLockOnCurrentThread());
11311
11312 HRESULT rc;
11313
11314 // make a temporary list because i_detachDevice invalidates iterators into
11315 // mMediumAttachments
11316 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11317
11318 for (MediumAttachmentList::iterator
11319 it = llAttachments2.begin();
11320 it != llAttachments2.end();
11321 ++it)
11322 {
11323 ComObjPtr<MediumAttachment> &pAttach = *it;
11324 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11325
11326 if (!pMedium.isNull())
11327 {
11328 AutoCaller mac(pMedium);
11329 if (FAILED(mac.rc())) return mac.rc();
11330 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11331 DeviceType_T devType = pMedium->i_getDeviceType();
11332 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11333 && devType == DeviceType_HardDisk)
11334 || (cleanupMode == CleanupMode_Full)
11335 )
11336 {
11337 llMedia.push_back(pMedium);
11338 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11339 /* Not allowed to keep this lock as below we need the parent
11340 * medium lock, and the lock order is parent to child. */
11341 lock.release();
11342 /*
11343 * Search for medias which are not attached to any machine, but
11344 * in the chain to an attached disk. Mediums are only consided
11345 * if they are:
11346 * - have only one child
11347 * - no references to any machines
11348 * - are of normal medium type
11349 */
11350 while (!pParent.isNull())
11351 {
11352 AutoCaller mac1(pParent);
11353 if (FAILED(mac1.rc())) return mac1.rc();
11354 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11355 if (pParent->i_getChildren().size() == 1)
11356 {
11357 if ( pParent->i_getMachineBackRefCount() == 0
11358 && pParent->i_getType() == MediumType_Normal
11359 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11360 llMedia.push_back(pParent);
11361 }
11362 else
11363 break;
11364 pParent = pParent->i_getParent();
11365 }
11366 }
11367 }
11368
11369 // real machine: then we need to use the proper method
11370 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11371
11372 if (FAILED(rc))
11373 return rc;
11374 }
11375
11376 return S_OK;
11377}
11378
11379/**
11380 * Perform deferred hard disk detachments.
11381 *
11382 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11383 * changed (not backed up).
11384 *
11385 * If @a aOnline is @c true then this method will also unlock the old hard
11386 * disks for which the new implicit diffs were created and will lock these new
11387 * diffs for writing.
11388 *
11389 * @param aOnline Whether the VM was online prior to this operation.
11390 *
11391 * @note Locks this object for writing!
11392 */
11393void Machine::i_commitMedia(bool aOnline /*= false*/)
11394{
11395 AutoCaller autoCaller(this);
11396 AssertComRCReturnVoid(autoCaller.rc());
11397
11398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11399
11400 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11401
11402 HRESULT rc = S_OK;
11403
11404 /* no attach/detach operations -- nothing to do */
11405 if (!mMediumAttachments.isBackedUp())
11406 return;
11407
11408 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11409 bool fMediaNeedsLocking = false;
11410
11411 /* enumerate new attachments */
11412 for (MediumAttachmentList::const_iterator
11413 it = mMediumAttachments->begin();
11414 it != mMediumAttachments->end();
11415 ++it)
11416 {
11417 MediumAttachment *pAttach = *it;
11418
11419 pAttach->i_commit();
11420
11421 Medium *pMedium = pAttach->i_getMedium();
11422 bool fImplicit = pAttach->i_isImplicit();
11423
11424 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11425 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11426 fImplicit));
11427
11428 /** @todo convert all this Machine-based voodoo to MediumAttachment
11429 * based commit logic. */
11430 if (fImplicit)
11431 {
11432 /* convert implicit attachment to normal */
11433 pAttach->i_setImplicit(false);
11434
11435 if ( aOnline
11436 && pMedium
11437 && pAttach->i_getType() == DeviceType_HardDisk
11438 )
11439 {
11440 /* update the appropriate lock list */
11441 MediumLockList *pMediumLockList;
11442 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11443 AssertComRC(rc);
11444 if (pMediumLockList)
11445 {
11446 /* unlock if there's a need to change the locking */
11447 if (!fMediaNeedsLocking)
11448 {
11449 rc = mData->mSession.mLockedMedia.Unlock();
11450 AssertComRC(rc);
11451 fMediaNeedsLocking = true;
11452 }
11453 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11454 AssertComRC(rc);
11455 rc = pMediumLockList->Append(pMedium, true);
11456 AssertComRC(rc);
11457 }
11458 }
11459
11460 continue;
11461 }
11462
11463 if (pMedium)
11464 {
11465 /* was this medium attached before? */
11466 for (MediumAttachmentList::iterator
11467 oldIt = oldAtts.begin();
11468 oldIt != oldAtts.end();
11469 ++oldIt)
11470 {
11471 MediumAttachment *pOldAttach = *oldIt;
11472 if (pOldAttach->i_getMedium() == pMedium)
11473 {
11474 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11475
11476 /* yes: remove from old to avoid de-association */
11477 oldAtts.erase(oldIt);
11478 break;
11479 }
11480 }
11481 }
11482 }
11483
11484 /* enumerate remaining old attachments and de-associate from the
11485 * current machine state */
11486 for (MediumAttachmentList::const_iterator
11487 it = oldAtts.begin();
11488 it != oldAtts.end();
11489 ++it)
11490 {
11491 MediumAttachment *pAttach = *it;
11492 Medium *pMedium = pAttach->i_getMedium();
11493
11494 /* Detach only hard disks, since DVD/floppy media is detached
11495 * instantly in MountMedium. */
11496 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11497 {
11498 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11499
11500 /* now de-associate from the current machine state */
11501 rc = pMedium->i_removeBackReference(mData->mUuid);
11502 AssertComRC(rc);
11503
11504 if (aOnline)
11505 {
11506 /* unlock since medium is not used anymore */
11507 MediumLockList *pMediumLockList;
11508 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11509 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11510 {
11511 /* this happens for online snapshots, there the attachment
11512 * is changing, but only to a diff image created under
11513 * the old one, so there is no separate lock list */
11514 Assert(!pMediumLockList);
11515 }
11516 else
11517 {
11518 AssertComRC(rc);
11519 if (pMediumLockList)
11520 {
11521 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11522 AssertComRC(rc);
11523 }
11524 }
11525 }
11526 }
11527 }
11528
11529 /* take media locks again so that the locking state is consistent */
11530 if (fMediaNeedsLocking)
11531 {
11532 Assert(aOnline);
11533 rc = mData->mSession.mLockedMedia.Lock();
11534 AssertComRC(rc);
11535 }
11536
11537 /* commit the hard disk changes */
11538 mMediumAttachments.commit();
11539
11540 if (i_isSessionMachine())
11541 {
11542 /*
11543 * Update the parent machine to point to the new owner.
11544 * This is necessary because the stored parent will point to the
11545 * session machine otherwise and cause crashes or errors later
11546 * when the session machine gets invalid.
11547 */
11548 /** @todo Change the MediumAttachment class to behave like any other
11549 * class in this regard by creating peer MediumAttachment
11550 * objects for session machines and share the data with the peer
11551 * machine.
11552 */
11553 for (MediumAttachmentList::const_iterator
11554 it = mMediumAttachments->begin();
11555 it != mMediumAttachments->end();
11556 ++it)
11557 (*it)->i_updateParentMachine(mPeer);
11558
11559 /* attach new data to the primary machine and reshare it */
11560 mPeer->mMediumAttachments.attach(mMediumAttachments);
11561 }
11562
11563 return;
11564}
11565
11566/**
11567 * Perform deferred deletion of implicitly created diffs.
11568 *
11569 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11570 * changed (not backed up).
11571 *
11572 * @note Locks this object for writing!
11573 */
11574void Machine::i_rollbackMedia()
11575{
11576 AutoCaller autoCaller(this);
11577 AssertComRCReturnVoid(autoCaller.rc());
11578
11579 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11580 LogFlowThisFunc(("Entering rollbackMedia\n"));
11581
11582 HRESULT rc = S_OK;
11583
11584 /* no attach/detach operations -- nothing to do */
11585 if (!mMediumAttachments.isBackedUp())
11586 return;
11587
11588 /* enumerate new attachments */
11589 for (MediumAttachmentList::const_iterator
11590 it = mMediumAttachments->begin();
11591 it != mMediumAttachments->end();
11592 ++it)
11593 {
11594 MediumAttachment *pAttach = *it;
11595 /* Fix up the backrefs for DVD/floppy media. */
11596 if (pAttach->i_getType() != DeviceType_HardDisk)
11597 {
11598 Medium *pMedium = pAttach->i_getMedium();
11599 if (pMedium)
11600 {
11601 rc = pMedium->i_removeBackReference(mData->mUuid);
11602 AssertComRC(rc);
11603 }
11604 }
11605
11606 (*it)->i_rollback();
11607
11608 pAttach = *it;
11609 /* Fix up the backrefs for DVD/floppy media. */
11610 if (pAttach->i_getType() != DeviceType_HardDisk)
11611 {
11612 Medium *pMedium = pAttach->i_getMedium();
11613 if (pMedium)
11614 {
11615 rc = pMedium->i_addBackReference(mData->mUuid);
11616 AssertComRC(rc);
11617 }
11618 }
11619 }
11620
11621 /** @todo convert all this Machine-based voodoo to MediumAttachment
11622 * based rollback logic. */
11623 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11624
11625 return;
11626}
11627
11628/**
11629 * Returns true if the settings file is located in the directory named exactly
11630 * as the machine; this means, among other things, that the machine directory
11631 * should be auto-renamed.
11632 *
11633 * @param aSettingsDir if not NULL, the full machine settings file directory
11634 * name will be assigned there.
11635 *
11636 * @note Doesn't lock anything.
11637 * @note Not thread safe (must be called from this object's lock).
11638 */
11639bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11640{
11641 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11642 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11643 if (aSettingsDir)
11644 *aSettingsDir = strMachineDirName;
11645 strMachineDirName.stripPath(); // vmname
11646 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11647 strConfigFileOnly.stripPath() // vmname.vbox
11648 .stripSuffix(); // vmname
11649 /** @todo hack, make somehow use of ComposeMachineFilename */
11650 if (mUserData->s.fDirectoryIncludesUUID)
11651 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11652
11653 AssertReturn(!strMachineDirName.isEmpty(), false);
11654 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11655
11656 return strMachineDirName == strConfigFileOnly;
11657}
11658
11659/**
11660 * Discards all changes to machine settings.
11661 *
11662 * @param aNotify Whether to notify the direct session about changes or not.
11663 *
11664 * @note Locks objects for writing!
11665 */
11666void Machine::i_rollback(bool aNotify)
11667{
11668 AutoCaller autoCaller(this);
11669 AssertComRCReturn(autoCaller.rc(), (void)0);
11670
11671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11672
11673 if (!mStorageControllers.isNull())
11674 {
11675 if (mStorageControllers.isBackedUp())
11676 {
11677 /* unitialize all new devices (absent in the backed up list). */
11678 StorageControllerList *backedList = mStorageControllers.backedUpData();
11679 for (StorageControllerList::const_iterator
11680 it = mStorageControllers->begin();
11681 it != mStorageControllers->end();
11682 ++it)
11683 {
11684 if ( std::find(backedList->begin(), backedList->end(), *it)
11685 == backedList->end()
11686 )
11687 {
11688 (*it)->uninit();
11689 }
11690 }
11691
11692 /* restore the list */
11693 mStorageControllers.rollback();
11694 }
11695
11696 /* rollback any changes to devices after restoring the list */
11697 if (mData->flModifications & IsModified_Storage)
11698 {
11699 for (StorageControllerList::const_iterator
11700 it = mStorageControllers->begin();
11701 it != mStorageControllers->end();
11702 ++it)
11703 {
11704 (*it)->i_rollback();
11705 }
11706 }
11707 }
11708
11709 if (!mUSBControllers.isNull())
11710 {
11711 if (mUSBControllers.isBackedUp())
11712 {
11713 /* unitialize all new devices (absent in the backed up list). */
11714 USBControllerList *backedList = mUSBControllers.backedUpData();
11715 for (USBControllerList::const_iterator
11716 it = mUSBControllers->begin();
11717 it != mUSBControllers->end();
11718 ++it)
11719 {
11720 if ( std::find(backedList->begin(), backedList->end(), *it)
11721 == backedList->end()
11722 )
11723 {
11724 (*it)->uninit();
11725 }
11726 }
11727
11728 /* restore the list */
11729 mUSBControllers.rollback();
11730 }
11731
11732 /* rollback any changes to devices after restoring the list */
11733 if (mData->flModifications & IsModified_USB)
11734 {
11735 for (USBControllerList::const_iterator
11736 it = mUSBControllers->begin();
11737 it != mUSBControllers->end();
11738 ++it)
11739 {
11740 (*it)->i_rollback();
11741 }
11742 }
11743 }
11744
11745 mUserData.rollback();
11746
11747 mHWData.rollback();
11748
11749 if (mData->flModifications & IsModified_Storage)
11750 i_rollbackMedia();
11751
11752 if (mBIOSSettings)
11753 mBIOSSettings->i_rollback();
11754
11755 if (mTrustedPlatformModule)
11756 mTrustedPlatformModule->i_rollback();
11757
11758 if (mNvramStore)
11759 mNvramStore->i_rollback();
11760
11761 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11762 mRecordingSettings->i_rollback();
11763
11764 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11765 mGraphicsAdapter->i_rollback();
11766
11767 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11768 mVRDEServer->i_rollback();
11769
11770 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11771 mAudioAdapter->i_rollback();
11772
11773 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11774 mUSBDeviceFilters->i_rollback();
11775
11776 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11777 mBandwidthControl->i_rollback();
11778
11779 if (!mHWData.isNull())
11780 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11781 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11782 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11783 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11784
11785 if (mData->flModifications & IsModified_NetworkAdapters)
11786 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11787 if ( mNetworkAdapters[slot]
11788 && mNetworkAdapters[slot]->i_isModified())
11789 {
11790 mNetworkAdapters[slot]->i_rollback();
11791 networkAdapters[slot] = mNetworkAdapters[slot];
11792 }
11793
11794 if (mData->flModifications & IsModified_SerialPorts)
11795 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11796 if ( mSerialPorts[slot]
11797 && mSerialPorts[slot]->i_isModified())
11798 {
11799 mSerialPorts[slot]->i_rollback();
11800 serialPorts[slot] = mSerialPorts[slot];
11801 }
11802
11803 if (mData->flModifications & IsModified_ParallelPorts)
11804 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11805 if ( mParallelPorts[slot]
11806 && mParallelPorts[slot]->i_isModified())
11807 {
11808 mParallelPorts[slot]->i_rollback();
11809 parallelPorts[slot] = mParallelPorts[slot];
11810 }
11811
11812 if (aNotify)
11813 {
11814 /* inform the direct session about changes */
11815
11816 ComObjPtr<Machine> that = this;
11817 uint32_t flModifications = mData->flModifications;
11818 alock.release();
11819
11820 if (flModifications & IsModified_SharedFolders)
11821 that->i_onSharedFolderChange();
11822
11823 if (flModifications & IsModified_VRDEServer)
11824 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11825 if (flModifications & IsModified_USB)
11826 that->i_onUSBControllerChange();
11827
11828 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11829 if (networkAdapters[slot])
11830 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11831 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11832 if (serialPorts[slot])
11833 that->i_onSerialPortChange(serialPorts[slot]);
11834 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11835 if (parallelPorts[slot])
11836 that->i_onParallelPortChange(parallelPorts[slot]);
11837
11838 if (flModifications & IsModified_Storage)
11839 {
11840 for (StorageControllerList::const_iterator
11841 it = mStorageControllers->begin();
11842 it != mStorageControllers->end();
11843 ++it)
11844 {
11845 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11846 }
11847 }
11848
11849
11850#if 0
11851 if (flModifications & IsModified_BandwidthControl)
11852 that->onBandwidthControlChange();
11853#endif
11854 }
11855}
11856
11857/**
11858 * Commits all the changes to machine settings.
11859 *
11860 * Note that this operation is supposed to never fail.
11861 *
11862 * @note Locks this object and children for writing.
11863 */
11864void Machine::i_commit()
11865{
11866 AutoCaller autoCaller(this);
11867 AssertComRCReturnVoid(autoCaller.rc());
11868
11869 AutoCaller peerCaller(mPeer);
11870 AssertComRCReturnVoid(peerCaller.rc());
11871
11872 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11873
11874 /*
11875 * use safe commit to ensure Snapshot machines (that share mUserData)
11876 * will still refer to a valid memory location
11877 */
11878 mUserData.commitCopy();
11879
11880 mHWData.commit();
11881
11882 if (mMediumAttachments.isBackedUp())
11883 i_commitMedia(Global::IsOnline(mData->mMachineState));
11884
11885 mBIOSSettings->i_commit();
11886 mTrustedPlatformModule->i_commit();
11887 mNvramStore->i_commit();
11888 mRecordingSettings->i_commit();
11889 mGraphicsAdapter->i_commit();
11890 mVRDEServer->i_commit();
11891 mAudioAdapter->i_commit();
11892 mUSBDeviceFilters->i_commit();
11893 mBandwidthControl->i_commit();
11894
11895 /* Since mNetworkAdapters is a list which might have been changed (resized)
11896 * without using the Backupable<> template we need to handle the copying
11897 * of the list entries manually, including the creation of peers for the
11898 * new objects. */
11899 bool commitNetworkAdapters = false;
11900 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11901 if (mPeer)
11902 {
11903 /* commit everything, even the ones which will go away */
11904 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11905 mNetworkAdapters[slot]->i_commit();
11906 /* copy over the new entries, creating a peer and uninit the original */
11907 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11908 for (size_t slot = 0; slot < newSize; slot++)
11909 {
11910 /* look if this adapter has a peer device */
11911 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11912 if (!peer)
11913 {
11914 /* no peer means the adapter is a newly created one;
11915 * create a peer owning data this data share it with */
11916 peer.createObject();
11917 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11918 }
11919 mPeer->mNetworkAdapters[slot] = peer;
11920 }
11921 /* uninit any no longer needed network adapters */
11922 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11923 mNetworkAdapters[slot]->uninit();
11924 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11925 {
11926 if (mPeer->mNetworkAdapters[slot])
11927 mPeer->mNetworkAdapters[slot]->uninit();
11928 }
11929 /* Keep the original network adapter count until this point, so that
11930 * discarding a chipset type change will not lose settings. */
11931 mNetworkAdapters.resize(newSize);
11932 mPeer->mNetworkAdapters.resize(newSize);
11933 }
11934 else
11935 {
11936 /* we have no peer (our parent is the newly created machine);
11937 * just commit changes to the network adapters */
11938 commitNetworkAdapters = true;
11939 }
11940 if (commitNetworkAdapters)
11941 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11942 mNetworkAdapters[slot]->i_commit();
11943
11944 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11945 mSerialPorts[slot]->i_commit();
11946 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11947 mParallelPorts[slot]->i_commit();
11948
11949 bool commitStorageControllers = false;
11950
11951 if (mStorageControllers.isBackedUp())
11952 {
11953 mStorageControllers.commit();
11954
11955 if (mPeer)
11956 {
11957 /* Commit all changes to new controllers (this will reshare data with
11958 * peers for those who have peers) */
11959 StorageControllerList *newList = new StorageControllerList();
11960 for (StorageControllerList::const_iterator
11961 it = mStorageControllers->begin();
11962 it != mStorageControllers->end();
11963 ++it)
11964 {
11965 (*it)->i_commit();
11966
11967 /* look if this controller has a peer device */
11968 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11969 if (!peer)
11970 {
11971 /* no peer means the device is a newly created one;
11972 * create a peer owning data this device share it with */
11973 peer.createObject();
11974 peer->init(mPeer, *it, true /* aReshare */);
11975 }
11976 else
11977 {
11978 /* remove peer from the old list */
11979 mPeer->mStorageControllers->remove(peer);
11980 }
11981 /* and add it to the new list */
11982 newList->push_back(peer);
11983 }
11984
11985 /* uninit old peer's controllers that are left */
11986 for (StorageControllerList::const_iterator
11987 it = mPeer->mStorageControllers->begin();
11988 it != mPeer->mStorageControllers->end();
11989 ++it)
11990 {
11991 (*it)->uninit();
11992 }
11993
11994 /* attach new list of controllers to our peer */
11995 mPeer->mStorageControllers.attach(newList);
11996 }
11997 else
11998 {
11999 /* we have no peer (our parent is the newly created machine);
12000 * just commit changes to devices */
12001 commitStorageControllers = true;
12002 }
12003 }
12004 else
12005 {
12006 /* the list of controllers itself is not changed,
12007 * just commit changes to controllers themselves */
12008 commitStorageControllers = true;
12009 }
12010
12011 if (commitStorageControllers)
12012 {
12013 for (StorageControllerList::const_iterator
12014 it = mStorageControllers->begin();
12015 it != mStorageControllers->end();
12016 ++it)
12017 {
12018 (*it)->i_commit();
12019 }
12020 }
12021
12022 bool commitUSBControllers = false;
12023
12024 if (mUSBControllers.isBackedUp())
12025 {
12026 mUSBControllers.commit();
12027
12028 if (mPeer)
12029 {
12030 /* Commit all changes to new controllers (this will reshare data with
12031 * peers for those who have peers) */
12032 USBControllerList *newList = new USBControllerList();
12033 for (USBControllerList::const_iterator
12034 it = mUSBControllers->begin();
12035 it != mUSBControllers->end();
12036 ++it)
12037 {
12038 (*it)->i_commit();
12039
12040 /* look if this controller has a peer device */
12041 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12042 if (!peer)
12043 {
12044 /* no peer means the device is a newly created one;
12045 * create a peer owning data this device share it with */
12046 peer.createObject();
12047 peer->init(mPeer, *it, true /* aReshare */);
12048 }
12049 else
12050 {
12051 /* remove peer from the old list */
12052 mPeer->mUSBControllers->remove(peer);
12053 }
12054 /* and add it to the new list */
12055 newList->push_back(peer);
12056 }
12057
12058 /* uninit old peer's controllers that are left */
12059 for (USBControllerList::const_iterator
12060 it = mPeer->mUSBControllers->begin();
12061 it != mPeer->mUSBControllers->end();
12062 ++it)
12063 {
12064 (*it)->uninit();
12065 }
12066
12067 /* attach new list of controllers to our peer */
12068 mPeer->mUSBControllers.attach(newList);
12069 }
12070 else
12071 {
12072 /* we have no peer (our parent is the newly created machine);
12073 * just commit changes to devices */
12074 commitUSBControllers = true;
12075 }
12076 }
12077 else
12078 {
12079 /* the list of controllers itself is not changed,
12080 * just commit changes to controllers themselves */
12081 commitUSBControllers = true;
12082 }
12083
12084 if (commitUSBControllers)
12085 {
12086 for (USBControllerList::const_iterator
12087 it = mUSBControllers->begin();
12088 it != mUSBControllers->end();
12089 ++it)
12090 {
12091 (*it)->i_commit();
12092 }
12093 }
12094
12095 if (i_isSessionMachine())
12096 {
12097 /* attach new data to the primary machine and reshare it */
12098 mPeer->mUserData.attach(mUserData);
12099 mPeer->mHWData.attach(mHWData);
12100 /* mmMediumAttachments is reshared by fixupMedia */
12101 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12102 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12103 }
12104}
12105
12106/**
12107 * Copies all the hardware data from the given machine.
12108 *
12109 * Currently, only called when the VM is being restored from a snapshot. In
12110 * particular, this implies that the VM is not running during this method's
12111 * call.
12112 *
12113 * @note This method must be called from under this object's lock.
12114 *
12115 * @note This method doesn't call #i_commit(), so all data remains backed up and
12116 * unsaved.
12117 */
12118void Machine::i_copyFrom(Machine *aThat)
12119{
12120 AssertReturnVoid(!i_isSnapshotMachine());
12121 AssertReturnVoid(aThat->i_isSnapshotMachine());
12122
12123 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12124
12125 mHWData.assignCopy(aThat->mHWData);
12126
12127 // create copies of all shared folders (mHWData after attaching a copy
12128 // contains just references to original objects)
12129 for (HWData::SharedFolderList::iterator
12130 it = mHWData->mSharedFolders.begin();
12131 it != mHWData->mSharedFolders.end();
12132 ++it)
12133 {
12134 ComObjPtr<SharedFolder> folder;
12135 folder.createObject();
12136 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12137 AssertComRC(rc);
12138 *it = folder;
12139 }
12140
12141 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12142 mTrustedPlatformModule->i_copyFrom(aThat->mTrustedPlatformModule);
12143 mNvramStore->i_copyFrom(aThat->mNvramStore);
12144 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12145 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12146 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12147 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12148 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12149 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12150
12151 /* create private copies of all controllers */
12152 mStorageControllers.backup();
12153 mStorageControllers->clear();
12154 for (StorageControllerList::const_iterator
12155 it = aThat->mStorageControllers->begin();
12156 it != aThat->mStorageControllers->end();
12157 ++it)
12158 {
12159 ComObjPtr<StorageController> ctrl;
12160 ctrl.createObject();
12161 ctrl->initCopy(this, *it);
12162 mStorageControllers->push_back(ctrl);
12163 }
12164
12165 /* create private copies of all USB controllers */
12166 mUSBControllers.backup();
12167 mUSBControllers->clear();
12168 for (USBControllerList::const_iterator
12169 it = aThat->mUSBControllers->begin();
12170 it != aThat->mUSBControllers->end();
12171 ++it)
12172 {
12173 ComObjPtr<USBController> ctrl;
12174 ctrl.createObject();
12175 ctrl->initCopy(this, *it);
12176 mUSBControllers->push_back(ctrl);
12177 }
12178
12179 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12180 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12181 {
12182 if (mNetworkAdapters[slot].isNotNull())
12183 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12184 else
12185 {
12186 unconst(mNetworkAdapters[slot]).createObject();
12187 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12188 }
12189 }
12190 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12191 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12192 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12193 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12194}
12195
12196/**
12197 * Returns whether the given storage controller is hotplug capable.
12198 *
12199 * @returns true if the controller supports hotplugging
12200 * false otherwise.
12201 * @param enmCtrlType The controller type to check for.
12202 */
12203bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12204{
12205 ComPtr<ISystemProperties> systemProperties;
12206 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12207 if (FAILED(rc))
12208 return false;
12209
12210 BOOL aHotplugCapable = FALSE;
12211 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12212
12213 return RT_BOOL(aHotplugCapable);
12214}
12215
12216#ifdef VBOX_WITH_RESOURCE_USAGE_API
12217
12218void Machine::i_getDiskList(MediaList &list)
12219{
12220 for (MediumAttachmentList::const_iterator
12221 it = mMediumAttachments->begin();
12222 it != mMediumAttachments->end();
12223 ++it)
12224 {
12225 MediumAttachment *pAttach = *it;
12226 /* just in case */
12227 AssertContinue(pAttach);
12228
12229 AutoCaller localAutoCallerA(pAttach);
12230 if (FAILED(localAutoCallerA.rc())) continue;
12231
12232 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12233
12234 if (pAttach->i_getType() == DeviceType_HardDisk)
12235 list.push_back(pAttach->i_getMedium());
12236 }
12237}
12238
12239void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12240{
12241 AssertReturnVoid(isWriteLockOnCurrentThread());
12242 AssertPtrReturnVoid(aCollector);
12243
12244 pm::CollectorHAL *hal = aCollector->getHAL();
12245 /* Create sub metrics */
12246 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12247 "Percentage of processor time spent in user mode by the VM process.");
12248 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12249 "Percentage of processor time spent in kernel mode by the VM process.");
12250 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12251 "Size of resident portion of VM process in memory.");
12252 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12253 "Actual size of all VM disks combined.");
12254 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12255 "Network receive rate.");
12256 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12257 "Network transmit rate.");
12258 /* Create and register base metrics */
12259 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12260 cpuLoadUser, cpuLoadKernel);
12261 aCollector->registerBaseMetric(cpuLoad);
12262 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12263 ramUsageUsed);
12264 aCollector->registerBaseMetric(ramUsage);
12265 MediaList disks;
12266 i_getDiskList(disks);
12267 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12268 diskUsageUsed);
12269 aCollector->registerBaseMetric(diskUsage);
12270
12271 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12272 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12273 new pm::AggregateAvg()));
12274 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12275 new pm::AggregateMin()));
12276 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12277 new pm::AggregateMax()));
12278 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12279 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12280 new pm::AggregateAvg()));
12281 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12282 new pm::AggregateMin()));
12283 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12284 new pm::AggregateMax()));
12285
12286 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12287 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12288 new pm::AggregateAvg()));
12289 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12290 new pm::AggregateMin()));
12291 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12292 new pm::AggregateMax()));
12293
12294 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12295 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12296 new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12298 new pm::AggregateMin()));
12299 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12300 new pm::AggregateMax()));
12301
12302
12303 /* Guest metrics collector */
12304 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12305 aCollector->registerGuest(mCollectorGuest);
12306 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12307
12308 /* Create sub metrics */
12309 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12310 "Percentage of processor time spent in user mode as seen by the guest.");
12311 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12312 "Percentage of processor time spent in kernel mode as seen by the guest.");
12313 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12314 "Percentage of processor time spent idling as seen by the guest.");
12315
12316 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12317 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12318 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12319 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12320 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12321 pm::SubMetric *guestMemCache = new pm::SubMetric(
12322 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12323
12324 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12325 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12326
12327 /* Create and register base metrics */
12328 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12329 machineNetRx, machineNetTx);
12330 aCollector->registerBaseMetric(machineNetRate);
12331
12332 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12333 guestLoadUser, guestLoadKernel, guestLoadIdle);
12334 aCollector->registerBaseMetric(guestCpuLoad);
12335
12336 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12337 guestMemTotal, guestMemFree,
12338 guestMemBalloon, guestMemShared,
12339 guestMemCache, guestPagedTotal);
12340 aCollector->registerBaseMetric(guestCpuMem);
12341
12342 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12343 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12344 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12345 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12346
12347 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12348 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12349 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12350 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12351
12352 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12353 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12354 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12355 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12356
12357 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12358 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12359 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12360 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12361
12362 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12363 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12364 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12365 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12366
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12371
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12376
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12380 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12381
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12385 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12386
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12389 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12390 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12391
12392 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12394 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12395 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12396}
12397
12398void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12399{
12400 AssertReturnVoid(isWriteLockOnCurrentThread());
12401
12402 if (aCollector)
12403 {
12404 aCollector->unregisterMetricsFor(aMachine);
12405 aCollector->unregisterBaseMetricsFor(aMachine);
12406 }
12407}
12408
12409#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12410
12411
12412////////////////////////////////////////////////////////////////////////////////
12413
12414DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12415
12416HRESULT SessionMachine::FinalConstruct()
12417{
12418 LogFlowThisFunc(("\n"));
12419
12420 mClientToken = NULL;
12421
12422 return BaseFinalConstruct();
12423}
12424
12425void SessionMachine::FinalRelease()
12426{
12427 LogFlowThisFunc(("\n"));
12428
12429 Assert(!mClientToken);
12430 /* paranoia, should not hang around any more */
12431 if (mClientToken)
12432 {
12433 delete mClientToken;
12434 mClientToken = NULL;
12435 }
12436
12437 uninit(Uninit::Unexpected);
12438
12439 BaseFinalRelease();
12440}
12441
12442/**
12443 * @note Must be called only by Machine::LockMachine() from its own write lock.
12444 */
12445HRESULT SessionMachine::init(Machine *aMachine)
12446{
12447 LogFlowThisFuncEnter();
12448 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12449
12450 AssertReturn(aMachine, E_INVALIDARG);
12451
12452 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12453
12454 /* Enclose the state transition NotReady->InInit->Ready */
12455 AutoInitSpan autoInitSpan(this);
12456 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12457
12458 HRESULT rc = S_OK;
12459
12460 RT_ZERO(mAuthLibCtx);
12461
12462 /* create the machine client token */
12463 try
12464 {
12465 mClientToken = new ClientToken(aMachine, this);
12466 if (!mClientToken->isReady())
12467 {
12468 delete mClientToken;
12469 mClientToken = NULL;
12470 rc = E_FAIL;
12471 }
12472 }
12473 catch (std::bad_alloc &)
12474 {
12475 rc = E_OUTOFMEMORY;
12476 }
12477 if (FAILED(rc))
12478 return rc;
12479
12480 /* memorize the peer Machine */
12481 unconst(mPeer) = aMachine;
12482 /* share the parent pointer */
12483 unconst(mParent) = aMachine->mParent;
12484
12485 /* take the pointers to data to share */
12486 mData.share(aMachine->mData);
12487 mSSData.share(aMachine->mSSData);
12488
12489 mUserData.share(aMachine->mUserData);
12490 mHWData.share(aMachine->mHWData);
12491 mMediumAttachments.share(aMachine->mMediumAttachments);
12492
12493 mStorageControllers.allocate();
12494 for (StorageControllerList::const_iterator
12495 it = aMachine->mStorageControllers->begin();
12496 it != aMachine->mStorageControllers->end();
12497 ++it)
12498 {
12499 ComObjPtr<StorageController> ctl;
12500 ctl.createObject();
12501 ctl->init(this, *it);
12502 mStorageControllers->push_back(ctl);
12503 }
12504
12505 mUSBControllers.allocate();
12506 for (USBControllerList::const_iterator
12507 it = aMachine->mUSBControllers->begin();
12508 it != aMachine->mUSBControllers->end();
12509 ++it)
12510 {
12511 ComObjPtr<USBController> ctl;
12512 ctl.createObject();
12513 ctl->init(this, *it);
12514 mUSBControllers->push_back(ctl);
12515 }
12516
12517 unconst(mBIOSSettings).createObject();
12518 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12519
12520 unconst(mTrustedPlatformModule).createObject();
12521 mTrustedPlatformModule->init(this, aMachine->mTrustedPlatformModule);
12522
12523 unconst(mNvramStore).createObject();
12524 mNvramStore->init(this, aMachine->mNvramStore);
12525
12526 unconst(mRecordingSettings).createObject();
12527 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12528 /* create another GraphicsAdapter object that will be mutable */
12529 unconst(mGraphicsAdapter).createObject();
12530 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12531 /* create another VRDEServer object that will be mutable */
12532 unconst(mVRDEServer).createObject();
12533 mVRDEServer->init(this, aMachine->mVRDEServer);
12534 /* create another audio adapter object that will be mutable */
12535 unconst(mAudioAdapter).createObject();
12536 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12537 /* create a list of serial ports that will be mutable */
12538 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12539 {
12540 unconst(mSerialPorts[slot]).createObject();
12541 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12542 }
12543 /* create a list of parallel ports that will be mutable */
12544 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12545 {
12546 unconst(mParallelPorts[slot]).createObject();
12547 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12548 }
12549
12550 /* create another USB device filters object that will be mutable */
12551 unconst(mUSBDeviceFilters).createObject();
12552 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12553
12554 /* create a list of network adapters that will be mutable */
12555 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12556 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12557 {
12558 unconst(mNetworkAdapters[slot]).createObject();
12559 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12560 }
12561
12562 /* create another bandwidth control object that will be mutable */
12563 unconst(mBandwidthControl).createObject();
12564 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12565
12566 /* default is to delete saved state on Saved -> PoweredOff transition */
12567 mRemoveSavedState = true;
12568
12569 /* Confirm a successful initialization when it's the case */
12570 autoInitSpan.setSucceeded();
12571
12572 miNATNetworksStarted = 0;
12573
12574 LogFlowThisFuncLeave();
12575 return rc;
12576}
12577
12578/**
12579 * Uninitializes this session object. If the reason is other than
12580 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12581 * or the client watcher code.
12582 *
12583 * @param aReason uninitialization reason
12584 *
12585 * @note Locks mParent + this object for writing.
12586 */
12587void SessionMachine::uninit(Uninit::Reason aReason)
12588{
12589 LogFlowThisFuncEnter();
12590 LogFlowThisFunc(("reason=%d\n", aReason));
12591
12592 /*
12593 * Strongly reference ourselves to prevent this object deletion after
12594 * mData->mSession.mMachine.setNull() below (which can release the last
12595 * reference and call the destructor). Important: this must be done before
12596 * accessing any members (and before AutoUninitSpan that does it as well).
12597 * This self reference will be released as the very last step on return.
12598 */
12599 ComObjPtr<SessionMachine> selfRef;
12600 if (aReason != Uninit::Unexpected)
12601 selfRef = this;
12602
12603 /* Enclose the state transition Ready->InUninit->NotReady */
12604 AutoUninitSpan autoUninitSpan(this);
12605 if (autoUninitSpan.uninitDone())
12606 {
12607 LogFlowThisFunc(("Already uninitialized\n"));
12608 LogFlowThisFuncLeave();
12609 return;
12610 }
12611
12612 if (autoUninitSpan.initFailed())
12613 {
12614 /* We've been called by init() because it's failed. It's not really
12615 * necessary (nor it's safe) to perform the regular uninit sequence
12616 * below, the following is enough.
12617 */
12618 LogFlowThisFunc(("Initialization failed.\n"));
12619 /* destroy the machine client token */
12620 if (mClientToken)
12621 {
12622 delete mClientToken;
12623 mClientToken = NULL;
12624 }
12625 uninitDataAndChildObjects();
12626 mData.free();
12627 unconst(mParent) = NULL;
12628 unconst(mPeer) = NULL;
12629 LogFlowThisFuncLeave();
12630 return;
12631 }
12632
12633 MachineState_T lastState;
12634 {
12635 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12636 lastState = mData->mMachineState;
12637 }
12638 NOREF(lastState);
12639
12640#ifdef VBOX_WITH_USB
12641 // release all captured USB devices, but do this before requesting the locks below
12642 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12643 {
12644 /* Console::captureUSBDevices() is called in the VM process only after
12645 * setting the machine state to Starting or Restoring.
12646 * Console::detachAllUSBDevices() will be called upon successful
12647 * termination. So, we need to release USB devices only if there was
12648 * an abnormal termination of a running VM.
12649 *
12650 * This is identical to SessionMachine::DetachAllUSBDevices except
12651 * for the aAbnormal argument. */
12652 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12653 AssertComRC(rc);
12654 NOREF(rc);
12655
12656 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12657 if (service)
12658 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12659 }
12660#endif /* VBOX_WITH_USB */
12661
12662 // we need to lock this object in uninit() because the lock is shared
12663 // with mPeer (as well as data we modify below). mParent lock is needed
12664 // by several calls to it.
12665 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12666
12667#ifdef VBOX_WITH_RESOURCE_USAGE_API
12668 /*
12669 * It is safe to call Machine::i_unregisterMetrics() here because
12670 * PerformanceCollector::samplerCallback no longer accesses guest methods
12671 * holding the lock.
12672 */
12673 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12674 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12675 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12676 if (mCollectorGuest)
12677 {
12678 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12679 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12680 mCollectorGuest = NULL;
12681 }
12682#endif
12683
12684 if (aReason == Uninit::Abnormal)
12685 {
12686 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12687
12688 /*
12689 * Move the VM to the 'Aborted' machine state unless we are restoring a
12690 * VM that was in the 'Saved' machine state. In that case, if the VM
12691 * fails before reaching either the 'Restoring' machine state or the
12692 * 'Running' machine state then we set the machine state to
12693 * 'AbortedSaved' in order to preserve the saved state file so that the
12694 * VM can be restored in the future.
12695 */
12696 if (mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring)
12697 i_setMachineState(MachineState_AbortedSaved);
12698 else if (mData->mMachineState != MachineState_Aborted && mData->mMachineState != MachineState_AbortedSaved)
12699 i_setMachineState(MachineState_Aborted);
12700 }
12701
12702 // any machine settings modified?
12703 if (mData->flModifications)
12704 {
12705 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12706 i_rollback(false /* aNotify */);
12707 }
12708
12709 mData->mSession.mPID = NIL_RTPROCESS;
12710
12711 if (aReason == Uninit::Unexpected)
12712 {
12713 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12714 * client watcher thread to update the set of machines that have open
12715 * sessions. */
12716 mParent->i_updateClientWatcher();
12717 }
12718
12719 /* uninitialize all remote controls */
12720 if (mData->mSession.mRemoteControls.size())
12721 {
12722 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12723 mData->mSession.mRemoteControls.size()));
12724
12725 /* Always restart a the beginning, since the iterator is invalidated
12726 * by using erase(). */
12727 for (Data::Session::RemoteControlList::iterator
12728 it = mData->mSession.mRemoteControls.begin();
12729 it != mData->mSession.mRemoteControls.end();
12730 it = mData->mSession.mRemoteControls.begin())
12731 {
12732 ComPtr<IInternalSessionControl> pControl = *it;
12733 mData->mSession.mRemoteControls.erase(it);
12734 multilock.release();
12735 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12736 HRESULT rc = pControl->Uninitialize();
12737 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12738 if (FAILED(rc))
12739 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12740 multilock.acquire();
12741 }
12742 mData->mSession.mRemoteControls.clear();
12743 }
12744
12745 /* Remove all references to the NAT network service. The service will stop
12746 * if all references (also from other VMs) are removed. */
12747 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12748 {
12749 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12750 {
12751 BOOL enabled;
12752 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12753 if ( FAILED(hrc)
12754 || !enabled)
12755 continue;
12756
12757 NetworkAttachmentType_T type;
12758 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12759 if ( SUCCEEDED(hrc)
12760 && type == NetworkAttachmentType_NATNetwork)
12761 {
12762 Bstr name;
12763 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12764 if (SUCCEEDED(hrc))
12765 {
12766 multilock.release();
12767 Utf8Str strName(name);
12768 LogRel(("VM '%s' stops using NAT network '%s'\n",
12769 mUserData->s.strName.c_str(), strName.c_str()));
12770 mParent->i_natNetworkRefDec(strName);
12771 multilock.acquire();
12772 }
12773 }
12774 }
12775 }
12776
12777 /*
12778 * An expected uninitialization can come only from #i_checkForDeath().
12779 * Otherwise it means that something's gone really wrong (for example,
12780 * the Session implementation has released the VirtualBox reference
12781 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12782 * etc). However, it's also possible, that the client releases the IPC
12783 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12784 * but the VirtualBox release event comes first to the server process.
12785 * This case is practically possible, so we should not assert on an
12786 * unexpected uninit, just log a warning.
12787 */
12788
12789 if (aReason == Uninit::Unexpected)
12790 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12791
12792 if (aReason != Uninit::Normal)
12793 {
12794 mData->mSession.mDirectControl.setNull();
12795 }
12796 else
12797 {
12798 /* this must be null here (see #OnSessionEnd()) */
12799 Assert(mData->mSession.mDirectControl.isNull());
12800 Assert(mData->mSession.mState == SessionState_Unlocking);
12801 Assert(!mData->mSession.mProgress.isNull());
12802 }
12803 if (mData->mSession.mProgress)
12804 {
12805 if (aReason == Uninit::Normal)
12806 mData->mSession.mProgress->i_notifyComplete(S_OK);
12807 else
12808 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12809 COM_IIDOF(ISession),
12810 getComponentName(),
12811 tr("The VM session was aborted"));
12812 mData->mSession.mProgress.setNull();
12813 }
12814
12815 if (mConsoleTaskData.mProgress)
12816 {
12817 Assert(aReason == Uninit::Abnormal);
12818 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12819 COM_IIDOF(ISession),
12820 getComponentName(),
12821 tr("The VM session was aborted"));
12822 mConsoleTaskData.mProgress.setNull();
12823 }
12824
12825 /* remove the association between the peer machine and this session machine */
12826 Assert( (SessionMachine*)mData->mSession.mMachine == this
12827 || aReason == Uninit::Unexpected);
12828
12829 /* reset the rest of session data */
12830 mData->mSession.mLockType = LockType_Null;
12831 mData->mSession.mMachine.setNull();
12832 mData->mSession.mState = SessionState_Unlocked;
12833 mData->mSession.mName.setNull();
12834
12835 /* destroy the machine client token before leaving the exclusive lock */
12836 if (mClientToken)
12837 {
12838 delete mClientToken;
12839 mClientToken = NULL;
12840 }
12841
12842 /* fire an event */
12843 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12844
12845 uninitDataAndChildObjects();
12846
12847 /* free the essential data structure last */
12848 mData.free();
12849
12850 /* release the exclusive lock before setting the below two to NULL */
12851 multilock.release();
12852
12853 unconst(mParent) = NULL;
12854 unconst(mPeer) = NULL;
12855
12856 AuthLibUnload(&mAuthLibCtx);
12857
12858 LogFlowThisFuncLeave();
12859}
12860
12861// util::Lockable interface
12862////////////////////////////////////////////////////////////////////////////////
12863
12864/**
12865 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12866 * with the primary Machine instance (mPeer).
12867 */
12868RWLockHandle *SessionMachine::lockHandle() const
12869{
12870 AssertReturn(mPeer != NULL, NULL);
12871 return mPeer->lockHandle();
12872}
12873
12874// IInternalMachineControl methods
12875////////////////////////////////////////////////////////////////////////////////
12876
12877/**
12878 * Passes collected guest statistics to performance collector object
12879 */
12880HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12881 ULONG aCpuKernel, ULONG aCpuIdle,
12882 ULONG aMemTotal, ULONG aMemFree,
12883 ULONG aMemBalloon, ULONG aMemShared,
12884 ULONG aMemCache, ULONG aPageTotal,
12885 ULONG aAllocVMM, ULONG aFreeVMM,
12886 ULONG aBalloonedVMM, ULONG aSharedVMM,
12887 ULONG aVmNetRx, ULONG aVmNetTx)
12888{
12889#ifdef VBOX_WITH_RESOURCE_USAGE_API
12890 if (mCollectorGuest)
12891 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12892 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12893 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12894 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12895
12896 return S_OK;
12897#else
12898 NOREF(aValidStats);
12899 NOREF(aCpuUser);
12900 NOREF(aCpuKernel);
12901 NOREF(aCpuIdle);
12902 NOREF(aMemTotal);
12903 NOREF(aMemFree);
12904 NOREF(aMemBalloon);
12905 NOREF(aMemShared);
12906 NOREF(aMemCache);
12907 NOREF(aPageTotal);
12908 NOREF(aAllocVMM);
12909 NOREF(aFreeVMM);
12910 NOREF(aBalloonedVMM);
12911 NOREF(aSharedVMM);
12912 NOREF(aVmNetRx);
12913 NOREF(aVmNetTx);
12914 return E_NOTIMPL;
12915#endif
12916}
12917
12918////////////////////////////////////////////////////////////////////////////////
12919//
12920// SessionMachine task records
12921//
12922////////////////////////////////////////////////////////////////////////////////
12923
12924/**
12925 * Task record for saving the machine state.
12926 */
12927class SessionMachine::SaveStateTask
12928 : public Machine::Task
12929{
12930public:
12931 SaveStateTask(SessionMachine *m,
12932 Progress *p,
12933 const Utf8Str &t,
12934 Reason_T enmReason,
12935 const Utf8Str &strStateFilePath)
12936 : Task(m, p, t),
12937 m_enmReason(enmReason),
12938 m_strStateFilePath(strStateFilePath)
12939 {}
12940
12941private:
12942 void handler()
12943 {
12944 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12945 }
12946
12947 Reason_T m_enmReason;
12948 Utf8Str m_strStateFilePath;
12949
12950 friend class SessionMachine;
12951};
12952
12953/**
12954 * Task thread implementation for SessionMachine::SaveState(), called from
12955 * SessionMachine::taskHandler().
12956 *
12957 * @note Locks this object for writing.
12958 *
12959 * @param task
12960 * @return
12961 */
12962void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12963{
12964 LogFlowThisFuncEnter();
12965
12966 AutoCaller autoCaller(this);
12967 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12968 if (FAILED(autoCaller.rc()))
12969 {
12970 /* we might have been uninitialized because the session was accidentally
12971 * closed by the client, so don't assert */
12972 HRESULT rc = setError(E_FAIL,
12973 tr("The session has been accidentally closed"));
12974 task.m_pProgress->i_notifyComplete(rc);
12975 LogFlowThisFuncLeave();
12976 return;
12977 }
12978
12979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12980
12981 HRESULT rc = S_OK;
12982
12983 try
12984 {
12985 ComPtr<IInternalSessionControl> directControl;
12986 if (mData->mSession.mLockType == LockType_VM)
12987 directControl = mData->mSession.mDirectControl;
12988 if (directControl.isNull())
12989 throw setError(VBOX_E_INVALID_VM_STATE,
12990 tr("Trying to save state without a running VM"));
12991 alock.release();
12992 BOOL fSuspendedBySave;
12993 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12994 Assert(!fSuspendedBySave);
12995 alock.acquire();
12996
12997 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12998 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12999 throw E_FAIL);
13000
13001 if (SUCCEEDED(rc))
13002 {
13003 mSSData->strStateFilePath = task.m_strStateFilePath;
13004
13005 /* save all VM settings */
13006 rc = i_saveSettings(NULL, alock);
13007 // no need to check whether VirtualBox.xml needs saving also since
13008 // we can't have a name change pending at this point
13009 }
13010 else
13011 {
13012 // On failure, set the state to the state we had at the beginning.
13013 i_setMachineState(task.m_machineStateBackup);
13014 i_updateMachineStateOnClient();
13015
13016 // Delete the saved state file (might have been already created).
13017 // No need to check whether this is shared with a snapshot here
13018 // because we certainly created a fresh saved state file here.
13019 RTFileDelete(task.m_strStateFilePath.c_str());
13020 }
13021 }
13022 catch (HRESULT aRC) { rc = aRC; }
13023
13024 task.m_pProgress->i_notifyComplete(rc);
13025
13026 LogFlowThisFuncLeave();
13027}
13028
13029/**
13030 * @note Locks this object for writing.
13031 */
13032HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13033{
13034 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13035}
13036
13037HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13038{
13039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13040
13041 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13042 if (FAILED(rc)) return rc;
13043
13044 if ( mData->mMachineState != MachineState_Running
13045 && mData->mMachineState != MachineState_Paused
13046 )
13047 return setError(VBOX_E_INVALID_VM_STATE,
13048 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13049 Global::stringifyMachineState(mData->mMachineState));
13050
13051 ComObjPtr<Progress> pProgress;
13052 pProgress.createObject();
13053 rc = pProgress->init(i_getVirtualBox(),
13054 static_cast<IMachine *>(this) /* aInitiator */,
13055 tr("Saving the execution state of the virtual machine"),
13056 FALSE /* aCancelable */);
13057 if (FAILED(rc))
13058 return rc;
13059
13060 Utf8Str strStateFilePath;
13061 i_composeSavedStateFilename(strStateFilePath);
13062
13063 /* create and start the task on a separate thread (note that it will not
13064 * start working until we release alock) */
13065 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13066 rc = pTask->createThread();
13067 if (FAILED(rc))
13068 return rc;
13069
13070 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13071 i_setMachineState(MachineState_Saving);
13072 i_updateMachineStateOnClient();
13073
13074 pProgress.queryInterfaceTo(aProgress.asOutParam());
13075
13076 return S_OK;
13077}
13078
13079/**
13080 * @note Locks this object for writing.
13081 */
13082HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13083{
13084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13085
13086 HRESULT rc = i_checkStateDependency(MutableStateDep);
13087 if (FAILED(rc)) return rc;
13088
13089 if ( mData->mMachineState != MachineState_PoweredOff
13090 && mData->mMachineState != MachineState_Teleported
13091 && mData->mMachineState != MachineState_Aborted
13092 )
13093 return setError(VBOX_E_INVALID_VM_STATE,
13094 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13095 Global::stringifyMachineState(mData->mMachineState));
13096
13097 com::Utf8Str stateFilePathFull;
13098 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13099 if (RT_FAILURE(vrc))
13100 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13101 tr("Invalid saved state file path '%s' (%Rrc)"),
13102 aSavedStateFile.c_str(),
13103 vrc);
13104
13105 mSSData->strStateFilePath = stateFilePathFull;
13106
13107 /* The below i_setMachineState() will detect the state transition and will
13108 * update the settings file */
13109
13110 return i_setMachineState(MachineState_Saved);
13111}
13112
13113/**
13114 * @note Locks this object for writing.
13115 */
13116HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13117{
13118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13119
13120 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13121 if (FAILED(rc)) return rc;
13122
13123 if ( mData->mMachineState != MachineState_Saved
13124 && mData->mMachineState != MachineState_AbortedSaved)
13125 return setError(VBOX_E_INVALID_VM_STATE,
13126 tr("Cannot discard the saved state as the machine is not in the Saved or Aborted-Saved state (machine state: %s)"),
13127 Global::stringifyMachineState(mData->mMachineState));
13128
13129 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13130
13131 /*
13132 * Saved -> PoweredOff transition will be detected in the SessionMachine
13133 * and properly handled.
13134 */
13135 rc = i_setMachineState(MachineState_PoweredOff);
13136 return rc;
13137}
13138
13139
13140/**
13141 * @note Locks the same as #i_setMachineState() does.
13142 */
13143HRESULT SessionMachine::updateState(MachineState_T aState)
13144{
13145 return i_setMachineState(aState);
13146}
13147
13148/**
13149 * @note Locks this object for writing.
13150 */
13151HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13152{
13153 IProgress *pProgress(aProgress);
13154
13155 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13156
13157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13158
13159 if (mData->mSession.mState != SessionState_Locked)
13160 return VBOX_E_INVALID_OBJECT_STATE;
13161
13162 if (!mData->mSession.mProgress.isNull())
13163 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13164
13165 /* If we didn't reference the NAT network service yet, add a reference to
13166 * force a start */
13167 if (miNATNetworksStarted < 1)
13168 {
13169 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13170 {
13171 BOOL enabled;
13172 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13173 if ( FAILED(hrc)
13174 || !enabled)
13175 continue;
13176
13177 NetworkAttachmentType_T type;
13178 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13179 if ( SUCCEEDED(hrc)
13180 && type == NetworkAttachmentType_NATNetwork)
13181 {
13182 Bstr name;
13183 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13184 if (SUCCEEDED(hrc))
13185 {
13186 Utf8Str strName(name);
13187 LogRel(("VM '%s' starts using NAT network '%s'\n",
13188 mUserData->s.strName.c_str(), strName.c_str()));
13189 mPeer->lockHandle()->unlockWrite();
13190 mParent->i_natNetworkRefInc(strName);
13191#ifdef RT_LOCK_STRICT
13192 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13193#else
13194 mPeer->lockHandle()->lockWrite();
13195#endif
13196 }
13197 }
13198 }
13199 miNATNetworksStarted++;
13200 }
13201
13202 LogFlowThisFunc(("returns S_OK.\n"));
13203 return S_OK;
13204}
13205
13206/**
13207 * @note Locks this object for writing.
13208 */
13209HRESULT SessionMachine::endPowerUp(LONG aResult)
13210{
13211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13212
13213 if (mData->mSession.mState != SessionState_Locked)
13214 return VBOX_E_INVALID_OBJECT_STATE;
13215
13216 /* Finalize the LaunchVMProcess progress object. */
13217 if (mData->mSession.mProgress)
13218 {
13219 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13220 mData->mSession.mProgress.setNull();
13221 }
13222
13223 if (SUCCEEDED((HRESULT)aResult))
13224 {
13225#ifdef VBOX_WITH_RESOURCE_USAGE_API
13226 /* The VM has been powered up successfully, so it makes sense
13227 * now to offer the performance metrics for a running machine
13228 * object. Doing it earlier wouldn't be safe. */
13229 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13230 mData->mSession.mPID);
13231#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13232 }
13233
13234 return S_OK;
13235}
13236
13237/**
13238 * @note Locks this object for writing.
13239 */
13240HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13241{
13242 LogFlowThisFuncEnter();
13243
13244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13245
13246 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13247 E_FAIL);
13248
13249 /* create a progress object to track operation completion */
13250 ComObjPtr<Progress> pProgress;
13251 pProgress.createObject();
13252 pProgress->init(i_getVirtualBox(),
13253 static_cast<IMachine *>(this) /* aInitiator */,
13254 tr("Stopping the virtual machine"),
13255 FALSE /* aCancelable */);
13256
13257 /* fill in the console task data */
13258 mConsoleTaskData.mLastState = mData->mMachineState;
13259 mConsoleTaskData.mProgress = pProgress;
13260
13261 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13262 i_setMachineState(MachineState_Stopping);
13263
13264 pProgress.queryInterfaceTo(aProgress.asOutParam());
13265
13266 return S_OK;
13267}
13268
13269/**
13270 * @note Locks this object for writing.
13271 */
13272HRESULT SessionMachine::endPoweringDown(LONG aResult,
13273 const com::Utf8Str &aErrMsg)
13274{
13275 HRESULT const hrcResult = (HRESULT)aResult;
13276 LogFlowThisFuncEnter();
13277
13278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13279
13280 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13281 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13282 && mConsoleTaskData.mLastState != MachineState_Null,
13283 E_FAIL);
13284
13285 /*
13286 * On failure, set the state to the state we had when BeginPoweringDown()
13287 * was called (this is expected by Console::PowerDown() and the associated
13288 * task). On success the VM process already changed the state to
13289 * MachineState_PoweredOff, so no need to do anything.
13290 */
13291 if (FAILED(hrcResult))
13292 i_setMachineState(mConsoleTaskData.mLastState);
13293
13294 /* notify the progress object about operation completion */
13295 Assert(mConsoleTaskData.mProgress);
13296 if (SUCCEEDED(hrcResult))
13297 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13298 else
13299 {
13300 if (aErrMsg.length())
13301 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13302 COM_IIDOF(ISession),
13303 getComponentName(),
13304 aErrMsg.c_str());
13305 else
13306 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13307 }
13308
13309 /* clear out the temporary saved state data */
13310 mConsoleTaskData.mLastState = MachineState_Null;
13311 mConsoleTaskData.mProgress.setNull();
13312
13313 LogFlowThisFuncLeave();
13314 return S_OK;
13315}
13316
13317
13318/**
13319 * Goes through the USB filters of the given machine to see if the given
13320 * device matches any filter or not.
13321 *
13322 * @note Locks the same as USBController::hasMatchingFilter() does.
13323 */
13324HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13325 BOOL *aMatched,
13326 ULONG *aMaskedInterfaces)
13327{
13328 LogFlowThisFunc(("\n"));
13329
13330#ifdef VBOX_WITH_USB
13331 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13332#else
13333 NOREF(aDevice);
13334 NOREF(aMaskedInterfaces);
13335 *aMatched = FALSE;
13336#endif
13337
13338 return S_OK;
13339}
13340
13341/**
13342 * @note Locks the same as Host::captureUSBDevice() does.
13343 */
13344HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13345{
13346 LogFlowThisFunc(("\n"));
13347
13348#ifdef VBOX_WITH_USB
13349 /* if captureDeviceForVM() fails, it must have set extended error info */
13350 clearError();
13351 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13352 if (FAILED(rc) || SUCCEEDED_WARNING(rc))
13353 return rc;
13354
13355 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13356 AssertReturn(service, E_FAIL);
13357 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13358#else
13359 RT_NOREF(aId, aCaptureFilename);
13360 return E_NOTIMPL;
13361#endif
13362}
13363
13364/**
13365 * @note Locks the same as Host::detachUSBDevice() does.
13366 */
13367HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13368 BOOL aDone)
13369{
13370 LogFlowThisFunc(("\n"));
13371
13372#ifdef VBOX_WITH_USB
13373 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13374 AssertReturn(service, E_FAIL);
13375 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13376#else
13377 NOREF(aId);
13378 NOREF(aDone);
13379 return E_NOTIMPL;
13380#endif
13381}
13382
13383/**
13384 * Inserts all machine filters to the USB proxy service and then calls
13385 * Host::autoCaptureUSBDevices().
13386 *
13387 * Called by Console from the VM process upon VM startup.
13388 *
13389 * @note Locks what called methods lock.
13390 */
13391HRESULT SessionMachine::autoCaptureUSBDevices()
13392{
13393 LogFlowThisFunc(("\n"));
13394
13395#ifdef VBOX_WITH_USB
13396 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13397 AssertComRC(rc);
13398 NOREF(rc);
13399
13400 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13401 AssertReturn(service, E_FAIL);
13402 return service->autoCaptureDevicesForVM(this);
13403#else
13404 return S_OK;
13405#endif
13406}
13407
13408/**
13409 * Removes all machine filters from the USB proxy service and then calls
13410 * Host::detachAllUSBDevices().
13411 *
13412 * Called by Console from the VM process upon normal VM termination or by
13413 * SessionMachine::uninit() upon abnormal VM termination (from under the
13414 * Machine/SessionMachine lock).
13415 *
13416 * @note Locks what called methods lock.
13417 */
13418HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13419{
13420 LogFlowThisFunc(("\n"));
13421
13422#ifdef VBOX_WITH_USB
13423 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13424 AssertComRC(rc);
13425 NOREF(rc);
13426
13427 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13428 AssertReturn(service, E_FAIL);
13429 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13430#else
13431 NOREF(aDone);
13432 return S_OK;
13433#endif
13434}
13435
13436/**
13437 * @note Locks this object for writing.
13438 */
13439HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13440 ComPtr<IProgress> &aProgress)
13441{
13442 LogFlowThisFuncEnter();
13443
13444 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13445 /*
13446 * We don't assert below because it might happen that a non-direct session
13447 * informs us it is closed right after we've been uninitialized -- it's ok.
13448 */
13449
13450 /* get IInternalSessionControl interface */
13451 ComPtr<IInternalSessionControl> control(aSession);
13452
13453 ComAssertRet(!control.isNull(), E_INVALIDARG);
13454
13455 /* Creating a Progress object requires the VirtualBox lock, and
13456 * thus locking it here is required by the lock order rules. */
13457 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13458
13459 if (control == mData->mSession.mDirectControl)
13460 {
13461 /* The direct session is being normally closed by the client process
13462 * ----------------------------------------------------------------- */
13463
13464 /* go to the closing state (essential for all open*Session() calls and
13465 * for #i_checkForDeath()) */
13466 Assert(mData->mSession.mState == SessionState_Locked);
13467 mData->mSession.mState = SessionState_Unlocking;
13468
13469 /* set direct control to NULL to release the remote instance */
13470 mData->mSession.mDirectControl.setNull();
13471 LogFlowThisFunc(("Direct control is set to NULL\n"));
13472
13473 if (mData->mSession.mProgress)
13474 {
13475 /* finalize the progress, someone might wait if a frontend
13476 * closes the session before powering on the VM. */
13477 mData->mSession.mProgress->notifyComplete(E_FAIL,
13478 COM_IIDOF(ISession),
13479 getComponentName(),
13480 tr("The VM session was closed before any attempt to power it on"));
13481 mData->mSession.mProgress.setNull();
13482 }
13483
13484 /* Create the progress object the client will use to wait until
13485 * #i_checkForDeath() is called to uninitialize this session object after
13486 * it releases the IPC semaphore.
13487 * Note! Because we're "reusing" mProgress here, this must be a proxy
13488 * object just like for LaunchVMProcess. */
13489 Assert(mData->mSession.mProgress.isNull());
13490 ComObjPtr<ProgressProxy> progress;
13491 progress.createObject();
13492 ComPtr<IUnknown> pPeer(mPeer);
13493 progress->init(mParent, pPeer,
13494 Bstr(tr("Closing session")).raw(),
13495 FALSE /* aCancelable */);
13496 progress.queryInterfaceTo(aProgress.asOutParam());
13497 mData->mSession.mProgress = progress;
13498 }
13499 else
13500 {
13501 /* the remote session is being normally closed */
13502 bool found = false;
13503 for (Data::Session::RemoteControlList::iterator
13504 it = mData->mSession.mRemoteControls.begin();
13505 it != mData->mSession.mRemoteControls.end();
13506 ++it)
13507 {
13508 if (control == *it)
13509 {
13510 found = true;
13511 // This MUST be erase(it), not remove(*it) as the latter
13512 // triggers a very nasty use after free due to the place where
13513 // the value "lives".
13514 mData->mSession.mRemoteControls.erase(it);
13515 break;
13516 }
13517 }
13518 ComAssertMsgRet(found, (tr("The session is not found in the session list!")),
13519 E_INVALIDARG);
13520 }
13521
13522 /* signal the client watcher thread, because the client is going away */
13523 mParent->i_updateClientWatcher();
13524
13525 LogFlowThisFuncLeave();
13526 return S_OK;
13527}
13528
13529HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13530 std::vector<com::Utf8Str> &aValues,
13531 std::vector<LONG64> &aTimestamps,
13532 std::vector<com::Utf8Str> &aFlags)
13533{
13534 LogFlowThisFunc(("\n"));
13535
13536#ifdef VBOX_WITH_GUEST_PROPS
13537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13538
13539 size_t cEntries = mHWData->mGuestProperties.size();
13540 aNames.resize(cEntries);
13541 aValues.resize(cEntries);
13542 aTimestamps.resize(cEntries);
13543 aFlags.resize(cEntries);
13544
13545 size_t i = 0;
13546 for (HWData::GuestPropertyMap::const_iterator
13547 it = mHWData->mGuestProperties.begin();
13548 it != mHWData->mGuestProperties.end();
13549 ++it, ++i)
13550 {
13551 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13552 aNames[i] = it->first;
13553 aValues[i] = it->second.strValue;
13554 aTimestamps[i] = it->second.mTimestamp;
13555
13556 /* If it is NULL, keep it NULL. */
13557 if (it->second.mFlags)
13558 {
13559 GuestPropWriteFlags(it->second.mFlags, szFlags);
13560 aFlags[i] = szFlags;
13561 }
13562 else
13563 aFlags[i] = "";
13564 }
13565 return S_OK;
13566#else
13567 ReturnComNotImplemented();
13568#endif
13569}
13570
13571HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13572 const com::Utf8Str &aValue,
13573 LONG64 aTimestamp,
13574 const com::Utf8Str &aFlags)
13575{
13576 LogFlowThisFunc(("\n"));
13577
13578#ifdef VBOX_WITH_GUEST_PROPS
13579 try
13580 {
13581 /*
13582 * Convert input up front.
13583 */
13584 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13585 if (aFlags.length())
13586 {
13587 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13588 AssertRCReturn(vrc, E_INVALIDARG);
13589 }
13590
13591 /*
13592 * Now grab the object lock, validate the state and do the update.
13593 */
13594
13595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13596
13597 if (!Global::IsOnline(mData->mMachineState))
13598 {
13599 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13600 VBOX_E_INVALID_VM_STATE);
13601 }
13602
13603 i_setModified(IsModified_MachineData);
13604 mHWData.backup();
13605
13606 bool fDelete = !aValue.length();
13607 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13608 if (it != mHWData->mGuestProperties.end())
13609 {
13610 if (!fDelete)
13611 {
13612 it->second.strValue = aValue;
13613 it->second.mTimestamp = aTimestamp;
13614 it->second.mFlags = fFlags;
13615 }
13616 else
13617 mHWData->mGuestProperties.erase(it);
13618
13619 mData->mGuestPropertiesModified = TRUE;
13620 }
13621 else if (!fDelete)
13622 {
13623 HWData::GuestProperty prop;
13624 prop.strValue = aValue;
13625 prop.mTimestamp = aTimestamp;
13626 prop.mFlags = fFlags;
13627
13628 mHWData->mGuestProperties[aName] = prop;
13629 mData->mGuestPropertiesModified = TRUE;
13630 }
13631
13632 alock.release();
13633
13634 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13635 }
13636 catch (...)
13637 {
13638 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13639 }
13640 return S_OK;
13641#else
13642 ReturnComNotImplemented();
13643#endif
13644}
13645
13646
13647HRESULT SessionMachine::lockMedia()
13648{
13649 AutoMultiWriteLock2 alock(this->lockHandle(),
13650 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13651
13652 AssertReturn( mData->mMachineState == MachineState_Starting
13653 || mData->mMachineState == MachineState_Restoring
13654 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13655
13656 clearError();
13657 alock.release();
13658 return i_lockMedia();
13659}
13660
13661HRESULT SessionMachine::unlockMedia()
13662{
13663 HRESULT hrc = i_unlockMedia();
13664 return hrc;
13665}
13666
13667HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13668 ComPtr<IMediumAttachment> &aNewAttachment)
13669{
13670 // request the host lock first, since might be calling Host methods for getting host drives;
13671 // next, protect the media tree all the while we're in here, as well as our member variables
13672 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13673 this->lockHandle(),
13674 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13675
13676 IMediumAttachment *iAttach = aAttachment;
13677 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13678
13679 Utf8Str ctrlName;
13680 LONG lPort;
13681 LONG lDevice;
13682 bool fTempEject;
13683 {
13684 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13685
13686 /* Need to query the details first, as the IMediumAttachment reference
13687 * might be to the original settings, which we are going to change. */
13688 ctrlName = pAttach->i_getControllerName();
13689 lPort = pAttach->i_getPort();
13690 lDevice = pAttach->i_getDevice();
13691 fTempEject = pAttach->i_getTempEject();
13692 }
13693
13694 if (!fTempEject)
13695 {
13696 /* Remember previously mounted medium. The medium before taking the
13697 * backup is not necessarily the same thing. */
13698 ComObjPtr<Medium> oldmedium;
13699 oldmedium = pAttach->i_getMedium();
13700
13701 i_setModified(IsModified_Storage);
13702 mMediumAttachments.backup();
13703
13704 // The backup operation makes the pAttach reference point to the
13705 // old settings. Re-get the correct reference.
13706 pAttach = i_findAttachment(*mMediumAttachments.data(),
13707 ctrlName,
13708 lPort,
13709 lDevice);
13710
13711 {
13712 AutoCaller autoAttachCaller(this);
13713 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13714
13715 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13716 if (!oldmedium.isNull())
13717 oldmedium->i_removeBackReference(mData->mUuid);
13718
13719 pAttach->i_updateMedium(NULL);
13720 pAttach->i_updateEjected();
13721 }
13722
13723 i_setModified(IsModified_Storage);
13724 }
13725 else
13726 {
13727 {
13728 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13729 pAttach->i_updateEjected();
13730 }
13731 }
13732
13733 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13734
13735 return S_OK;
13736}
13737
13738HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13739 com::Utf8Str &aResult)
13740{
13741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13742
13743 HRESULT hr = S_OK;
13744
13745 if (!mAuthLibCtx.hAuthLibrary)
13746 {
13747 /* Load the external authentication library. */
13748 Bstr authLibrary;
13749 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13750
13751 Utf8Str filename = authLibrary;
13752
13753 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13754 if (RT_FAILURE(vrc))
13755 hr = setErrorBoth(E_FAIL, vrc,
13756 tr("Could not load the external authentication library '%s' (%Rrc)"),
13757 filename.c_str(), vrc);
13758 }
13759
13760 /* The auth library might need the machine lock. */
13761 alock.release();
13762
13763 if (FAILED(hr))
13764 return hr;
13765
13766 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13767 {
13768 enum VRDEAuthParams
13769 {
13770 parmUuid = 1,
13771 parmGuestJudgement,
13772 parmUser,
13773 parmPassword,
13774 parmDomain,
13775 parmClientId
13776 };
13777
13778 AuthResult result = AuthResultAccessDenied;
13779
13780 Guid uuid(aAuthParams[parmUuid]);
13781 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13782 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13783
13784 result = AuthLibAuthenticate(&mAuthLibCtx,
13785 uuid.raw(), guestJudgement,
13786 aAuthParams[parmUser].c_str(),
13787 aAuthParams[parmPassword].c_str(),
13788 aAuthParams[parmDomain].c_str(),
13789 u32ClientId);
13790
13791 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13792 size_t cbPassword = aAuthParams[parmPassword].length();
13793 if (cbPassword)
13794 {
13795 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13796 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13797 }
13798
13799 if (result == AuthResultAccessGranted)
13800 aResult = "granted";
13801 else
13802 aResult = "denied";
13803
13804 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13805 aAuthParams[parmUser].c_str(), aResult.c_str()));
13806 }
13807 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13808 {
13809 enum VRDEAuthDisconnectParams
13810 {
13811 parmUuid = 1,
13812 parmClientId
13813 };
13814
13815 Guid uuid(aAuthParams[parmUuid]);
13816 uint32_t u32ClientId = 0;
13817 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13818 }
13819 else
13820 {
13821 hr = E_INVALIDARG;
13822 }
13823
13824 return hr;
13825}
13826
13827// public methods only for internal purposes
13828/////////////////////////////////////////////////////////////////////////////
13829
13830#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13831/**
13832 * Called from the client watcher thread to check for expected or unexpected
13833 * death of the client process that has a direct session to this machine.
13834 *
13835 * On Win32 and on OS/2, this method is called only when we've got the
13836 * mutex (i.e. the client has either died or terminated normally) so it always
13837 * returns @c true (the client is terminated, the session machine is
13838 * uninitialized).
13839 *
13840 * On other platforms, the method returns @c true if the client process has
13841 * terminated normally or abnormally and the session machine was uninitialized,
13842 * and @c false if the client process is still alive.
13843 *
13844 * @note Locks this object for writing.
13845 */
13846bool SessionMachine::i_checkForDeath()
13847{
13848 Uninit::Reason reason;
13849 bool terminated = false;
13850
13851 /* Enclose autoCaller with a block because calling uninit() from under it
13852 * will deadlock. */
13853 {
13854 AutoCaller autoCaller(this);
13855 if (!autoCaller.isOk())
13856 {
13857 /* return true if not ready, to cause the client watcher to exclude
13858 * the corresponding session from watching */
13859 LogFlowThisFunc(("Already uninitialized!\n"));
13860 return true;
13861 }
13862
13863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13864
13865 /* Determine the reason of death: if the session state is Closing here,
13866 * everything is fine. Otherwise it means that the client did not call
13867 * OnSessionEnd() before it released the IPC semaphore. This may happen
13868 * either because the client process has abnormally terminated, or
13869 * because it simply forgot to call ISession::Close() before exiting. We
13870 * threat the latter also as an abnormal termination (see
13871 * Session::uninit() for details). */
13872 reason = mData->mSession.mState == SessionState_Unlocking ?
13873 Uninit::Normal :
13874 Uninit::Abnormal;
13875
13876 if (mClientToken)
13877 terminated = mClientToken->release();
13878 } /* AutoCaller block */
13879
13880 if (terminated)
13881 uninit(reason);
13882
13883 return terminated;
13884}
13885
13886void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13887{
13888 LogFlowThisFunc(("\n"));
13889
13890 strTokenId.setNull();
13891
13892 AutoCaller autoCaller(this);
13893 AssertComRCReturnVoid(autoCaller.rc());
13894
13895 Assert(mClientToken);
13896 if (mClientToken)
13897 mClientToken->getId(strTokenId);
13898}
13899#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13900IToken *SessionMachine::i_getToken()
13901{
13902 LogFlowThisFunc(("\n"));
13903
13904 AutoCaller autoCaller(this);
13905 AssertComRCReturn(autoCaller.rc(), NULL);
13906
13907 Assert(mClientToken);
13908 if (mClientToken)
13909 return mClientToken->getToken();
13910 else
13911 return NULL;
13912}
13913#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13914
13915Machine::ClientToken *SessionMachine::i_getClientToken()
13916{
13917 LogFlowThisFunc(("\n"));
13918
13919 AutoCaller autoCaller(this);
13920 AssertComRCReturn(autoCaller.rc(), NULL);
13921
13922 return mClientToken;
13923}
13924
13925
13926/**
13927 * @note Locks this object for reading.
13928 */
13929HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13930{
13931 LogFlowThisFunc(("\n"));
13932
13933 AutoCaller autoCaller(this);
13934 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13935
13936 ComPtr<IInternalSessionControl> directControl;
13937 {
13938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13939 if (mData->mSession.mLockType == LockType_VM)
13940 directControl = mData->mSession.mDirectControl;
13941 }
13942
13943 /* ignore notifications sent after #OnSessionEnd() is called */
13944 if (!directControl)
13945 return S_OK;
13946
13947 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13948}
13949
13950/**
13951 * @note Locks this object for reading.
13952 */
13953HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13954 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13955 const Utf8Str &aGuestIp, LONG aGuestPort)
13956{
13957 LogFlowThisFunc(("\n"));
13958
13959 AutoCaller autoCaller(this);
13960 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13961
13962 ComPtr<IInternalSessionControl> directControl;
13963 {
13964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13965 if (mData->mSession.mLockType == LockType_VM)
13966 directControl = mData->mSession.mDirectControl;
13967 }
13968
13969 /* ignore notifications sent after #OnSessionEnd() is called */
13970 if (!directControl)
13971 return S_OK;
13972 /*
13973 * instead acting like callback we ask IVirtualBox deliver corresponding event
13974 */
13975
13976 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13977 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13978 return S_OK;
13979}
13980
13981/**
13982 * @note Locks this object for reading.
13983 */
13984HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13985{
13986 LogFlowThisFunc(("\n"));
13987
13988 AutoCaller autoCaller(this);
13989 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13990
13991 ComPtr<IInternalSessionControl> directControl;
13992 {
13993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13994 if (mData->mSession.mLockType == LockType_VM)
13995 directControl = mData->mSession.mDirectControl;
13996 }
13997
13998 /* ignore notifications sent after #OnSessionEnd() is called */
13999 if (!directControl)
14000 return S_OK;
14001
14002 return directControl->OnAudioAdapterChange(audioAdapter);
14003}
14004
14005/**
14006 * @note Locks this object for reading.
14007 */
14008HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14009{
14010 LogFlowThisFunc(("\n"));
14011
14012 AutoCaller autoCaller(this);
14013 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14014
14015 ComPtr<IInternalSessionControl> directControl;
14016 {
14017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14018 if (mData->mSession.mLockType == LockType_VM)
14019 directControl = mData->mSession.mDirectControl;
14020 }
14021
14022 /* ignore notifications sent after #OnSessionEnd() is called */
14023 if (!directControl)
14024 return S_OK;
14025
14026 return directControl->OnSerialPortChange(serialPort);
14027}
14028
14029/**
14030 * @note Locks this object for reading.
14031 */
14032HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14033{
14034 LogFlowThisFunc(("\n"));
14035
14036 AutoCaller autoCaller(this);
14037 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14038
14039 ComPtr<IInternalSessionControl> directControl;
14040 {
14041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14042 if (mData->mSession.mLockType == LockType_VM)
14043 directControl = mData->mSession.mDirectControl;
14044 }
14045
14046 /* ignore notifications sent after #OnSessionEnd() is called */
14047 if (!directControl)
14048 return S_OK;
14049
14050 return directControl->OnParallelPortChange(parallelPort);
14051}
14052
14053/**
14054 * @note Locks this object for reading.
14055 */
14056HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14057{
14058 LogFlowThisFunc(("\n"));
14059
14060 AutoCaller autoCaller(this);
14061 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14062
14063 ComPtr<IInternalSessionControl> directControl;
14064 {
14065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14066 if (mData->mSession.mLockType == LockType_VM)
14067 directControl = mData->mSession.mDirectControl;
14068 }
14069
14070 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14071
14072 /* ignore notifications sent after #OnSessionEnd() is called */
14073 if (!directControl)
14074 return S_OK;
14075
14076 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14077}
14078
14079/**
14080 * @note Locks this object for reading.
14081 */
14082HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14083{
14084 LogFlowThisFunc(("\n"));
14085
14086 AutoCaller autoCaller(this);
14087 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14088
14089 ComPtr<IInternalSessionControl> directControl;
14090 {
14091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14092 if (mData->mSession.mLockType == LockType_VM)
14093 directControl = mData->mSession.mDirectControl;
14094 }
14095
14096 mParent->i_onMediumChanged(aAttachment);
14097
14098 /* ignore notifications sent after #OnSessionEnd() is called */
14099 if (!directControl)
14100 return S_OK;
14101
14102 return directControl->OnMediumChange(aAttachment, aForce);
14103}
14104
14105HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14106{
14107 LogFlowThisFunc(("\n"));
14108
14109 AutoCaller autoCaller(this);
14110 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14111
14112 ComPtr<IInternalSessionControl> directControl;
14113 {
14114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14115 if (mData->mSession.mLockType == LockType_VM)
14116 directControl = mData->mSession.mDirectControl;
14117 }
14118
14119 /* ignore notifications sent after #OnSessionEnd() is called */
14120 if (!directControl)
14121 return S_OK;
14122
14123 return directControl->OnVMProcessPriorityChange(aPriority);
14124}
14125
14126/**
14127 * @note Locks this object for reading.
14128 */
14129HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14130{
14131 LogFlowThisFunc(("\n"));
14132
14133 AutoCaller autoCaller(this);
14134 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14135
14136 ComPtr<IInternalSessionControl> directControl;
14137 {
14138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14139 if (mData->mSession.mLockType == LockType_VM)
14140 directControl = mData->mSession.mDirectControl;
14141 }
14142
14143 /* ignore notifications sent after #OnSessionEnd() is called */
14144 if (!directControl)
14145 return S_OK;
14146
14147 return directControl->OnCPUChange(aCPU, aRemove);
14148}
14149
14150HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14151{
14152 LogFlowThisFunc(("\n"));
14153
14154 AutoCaller autoCaller(this);
14155 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14156
14157 ComPtr<IInternalSessionControl> directControl;
14158 {
14159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14160 if (mData->mSession.mLockType == LockType_VM)
14161 directControl = mData->mSession.mDirectControl;
14162 }
14163
14164 /* ignore notifications sent after #OnSessionEnd() is called */
14165 if (!directControl)
14166 return S_OK;
14167
14168 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14169}
14170
14171/**
14172 * @note Locks this object for reading.
14173 */
14174HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14175{
14176 LogFlowThisFunc(("\n"));
14177
14178 AutoCaller autoCaller(this);
14179 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14180
14181 ComPtr<IInternalSessionControl> directControl;
14182 {
14183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14184 if (mData->mSession.mLockType == LockType_VM)
14185 directControl = mData->mSession.mDirectControl;
14186 }
14187
14188 /* ignore notifications sent after #OnSessionEnd() is called */
14189 if (!directControl)
14190 return S_OK;
14191
14192 return directControl->OnVRDEServerChange(aRestart);
14193}
14194
14195/**
14196 * @note Locks this object for reading.
14197 */
14198HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14199{
14200 LogFlowThisFunc(("\n"));
14201
14202 AutoCaller autoCaller(this);
14203 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14204
14205 ComPtr<IInternalSessionControl> directControl;
14206 {
14207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14208 if (mData->mSession.mLockType == LockType_VM)
14209 directControl = mData->mSession.mDirectControl;
14210 }
14211
14212 /* ignore notifications sent after #OnSessionEnd() is called */
14213 if (!directControl)
14214 return S_OK;
14215
14216 return directControl->OnRecordingChange(aEnable);
14217}
14218
14219/**
14220 * @note Locks this object for reading.
14221 */
14222HRESULT SessionMachine::i_onUSBControllerChange()
14223{
14224 LogFlowThisFunc(("\n"));
14225
14226 AutoCaller autoCaller(this);
14227 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14228
14229 ComPtr<IInternalSessionControl> directControl;
14230 {
14231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14232 if (mData->mSession.mLockType == LockType_VM)
14233 directControl = mData->mSession.mDirectControl;
14234 }
14235
14236 /* ignore notifications sent after #OnSessionEnd() is called */
14237 if (!directControl)
14238 return S_OK;
14239
14240 return directControl->OnUSBControllerChange();
14241}
14242
14243/**
14244 * @note Locks this object for reading.
14245 */
14246HRESULT SessionMachine::i_onSharedFolderChange()
14247{
14248 LogFlowThisFunc(("\n"));
14249
14250 AutoCaller autoCaller(this);
14251 AssertComRCReturnRC(autoCaller.rc());
14252
14253 ComPtr<IInternalSessionControl> directControl;
14254 {
14255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14256 if (mData->mSession.mLockType == LockType_VM)
14257 directControl = mData->mSession.mDirectControl;
14258 }
14259
14260 /* ignore notifications sent after #OnSessionEnd() is called */
14261 if (!directControl)
14262 return S_OK;
14263
14264 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14265}
14266
14267/**
14268 * @note Locks this object for reading.
14269 */
14270HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14271{
14272 LogFlowThisFunc(("\n"));
14273
14274 AutoCaller autoCaller(this);
14275 AssertComRCReturnRC(autoCaller.rc());
14276
14277 ComPtr<IInternalSessionControl> directControl;
14278 {
14279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14280 if (mData->mSession.mLockType == LockType_VM)
14281 directControl = mData->mSession.mDirectControl;
14282 }
14283
14284 /* ignore notifications sent after #OnSessionEnd() is called */
14285 if (!directControl)
14286 return S_OK;
14287
14288 return directControl->OnClipboardModeChange(aClipboardMode);
14289}
14290
14291/**
14292 * @note Locks this object for reading.
14293 */
14294HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14295{
14296 LogFlowThisFunc(("\n"));
14297
14298 AutoCaller autoCaller(this);
14299 AssertComRCReturnRC(autoCaller.rc());
14300
14301 ComPtr<IInternalSessionControl> directControl;
14302 {
14303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14304 if (mData->mSession.mLockType == LockType_VM)
14305 directControl = mData->mSession.mDirectControl;
14306 }
14307
14308 /* ignore notifications sent after #OnSessionEnd() is called */
14309 if (!directControl)
14310 return S_OK;
14311
14312 return directControl->OnClipboardFileTransferModeChange(aEnable);
14313}
14314
14315/**
14316 * @note Locks this object for reading.
14317 */
14318HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14319{
14320 LogFlowThisFunc(("\n"));
14321
14322 AutoCaller autoCaller(this);
14323 AssertComRCReturnRC(autoCaller.rc());
14324
14325 ComPtr<IInternalSessionControl> directControl;
14326 {
14327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14328 if (mData->mSession.mLockType == LockType_VM)
14329 directControl = mData->mSession.mDirectControl;
14330 }
14331
14332 /* ignore notifications sent after #OnSessionEnd() is called */
14333 if (!directControl)
14334 return S_OK;
14335
14336 return directControl->OnDnDModeChange(aDnDMode);
14337}
14338
14339/**
14340 * @note Locks this object for reading.
14341 */
14342HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14343{
14344 LogFlowThisFunc(("\n"));
14345
14346 AutoCaller autoCaller(this);
14347 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14348
14349 ComPtr<IInternalSessionControl> directControl;
14350 {
14351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14352 if (mData->mSession.mLockType == LockType_VM)
14353 directControl = mData->mSession.mDirectControl;
14354 }
14355
14356 /* ignore notifications sent after #OnSessionEnd() is called */
14357 if (!directControl)
14358 return S_OK;
14359
14360 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14361}
14362
14363/**
14364 * @note Locks this object for reading.
14365 */
14366HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14367{
14368 LogFlowThisFunc(("\n"));
14369
14370 AutoCaller autoCaller(this);
14371 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14372
14373 ComPtr<IInternalSessionControl> directControl;
14374 {
14375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14376 if (mData->mSession.mLockType == LockType_VM)
14377 directControl = mData->mSession.mDirectControl;
14378 }
14379
14380 /* ignore notifications sent after #OnSessionEnd() is called */
14381 if (!directControl)
14382 return S_OK;
14383
14384 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14385}
14386
14387/**
14388 * Returns @c true if this machine's USB controller reports it has a matching
14389 * filter for the given USB device and @c false otherwise.
14390 *
14391 * @note locks this object for reading.
14392 */
14393bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14394{
14395 AutoCaller autoCaller(this);
14396 /* silently return if not ready -- this method may be called after the
14397 * direct machine session has been called */
14398 if (!autoCaller.isOk())
14399 return false;
14400
14401#ifdef VBOX_WITH_USB
14402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14403
14404 switch (mData->mMachineState)
14405 {
14406 case MachineState_Starting:
14407 case MachineState_Restoring:
14408 case MachineState_TeleportingIn:
14409 case MachineState_Paused:
14410 case MachineState_Running:
14411 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14412 * elsewhere... */
14413 alock.release();
14414 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14415 default: break;
14416 }
14417#else
14418 NOREF(aDevice);
14419 NOREF(aMaskedIfs);
14420#endif
14421 return false;
14422}
14423
14424/**
14425 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14426 */
14427HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14428 IVirtualBoxErrorInfo *aError,
14429 ULONG aMaskedIfs,
14430 const com::Utf8Str &aCaptureFilename)
14431{
14432 LogFlowThisFunc(("\n"));
14433
14434 AutoCaller autoCaller(this);
14435
14436 /* This notification may happen after the machine object has been
14437 * uninitialized (the session was closed), so don't assert. */
14438 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14439
14440 ComPtr<IInternalSessionControl> directControl;
14441 {
14442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14443 if (mData->mSession.mLockType == LockType_VM)
14444 directControl = mData->mSession.mDirectControl;
14445 }
14446
14447 /* fail on notifications sent after #OnSessionEnd() is called, it is
14448 * expected by the caller */
14449 if (!directControl)
14450 return E_FAIL;
14451
14452 /* No locks should be held at this point. */
14453 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14454 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14455
14456 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14457}
14458
14459/**
14460 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14461 */
14462HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14463 IVirtualBoxErrorInfo *aError)
14464{
14465 LogFlowThisFunc(("\n"));
14466
14467 AutoCaller autoCaller(this);
14468
14469 /* This notification may happen after the machine object has been
14470 * uninitialized (the session was closed), so don't assert. */
14471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14472
14473 ComPtr<IInternalSessionControl> directControl;
14474 {
14475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14476 if (mData->mSession.mLockType == LockType_VM)
14477 directControl = mData->mSession.mDirectControl;
14478 }
14479
14480 /* fail on notifications sent after #OnSessionEnd() is called, it is
14481 * expected by the caller */
14482 if (!directControl)
14483 return E_FAIL;
14484
14485 /* No locks should be held at this point. */
14486 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14487 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14488
14489 return directControl->OnUSBDeviceDetach(aId, aError);
14490}
14491
14492// protected methods
14493/////////////////////////////////////////////////////////////////////////////
14494
14495/**
14496 * Deletes the given file if it is no longer in use by either the current machine state
14497 * (if the machine is "saved") or any of the machine's snapshots.
14498 *
14499 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14500 * but is different for each SnapshotMachine. When calling this, the order of calling this
14501 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14502 * is therefore critical. I know, it's all rather messy.
14503 *
14504 * @param strStateFile
14505 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14506 * the test for whether the saved state file is in use.
14507 */
14508void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14509 Snapshot *pSnapshotToIgnore)
14510{
14511 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14512 if ( (strStateFile.isNotEmpty())
14513 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14514 )
14515 // ... and it must also not be shared with other snapshots
14516 if ( !mData->mFirstSnapshot
14517 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14518 // this checks the SnapshotMachine's state file paths
14519 )
14520 RTFileDelete(strStateFile.c_str());
14521}
14522
14523/**
14524 * Locks the attached media.
14525 *
14526 * All attached hard disks are locked for writing and DVD/floppy are locked for
14527 * reading. Parents of attached hard disks (if any) are locked for reading.
14528 *
14529 * This method also performs accessibility check of all media it locks: if some
14530 * media is inaccessible, the method will return a failure and a bunch of
14531 * extended error info objects per each inaccessible medium.
14532 *
14533 * Note that this method is atomic: if it returns a success, all media are
14534 * locked as described above; on failure no media is locked at all (all
14535 * succeeded individual locks will be undone).
14536 *
14537 * The caller is responsible for doing the necessary state sanity checks.
14538 *
14539 * The locks made by this method must be undone by calling #unlockMedia() when
14540 * no more needed.
14541 */
14542HRESULT SessionMachine::i_lockMedia()
14543{
14544 AutoCaller autoCaller(this);
14545 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14546
14547 AutoMultiWriteLock2 alock(this->lockHandle(),
14548 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14549
14550 /* bail out if trying to lock things with already set up locking */
14551 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14552
14553 MultiResult mrc(S_OK);
14554
14555 /* Collect locking information for all medium objects attached to the VM. */
14556 for (MediumAttachmentList::const_iterator
14557 it = mMediumAttachments->begin();
14558 it != mMediumAttachments->end();
14559 ++it)
14560 {
14561 MediumAttachment *pAtt = *it;
14562 DeviceType_T devType = pAtt->i_getType();
14563 Medium *pMedium = pAtt->i_getMedium();
14564
14565 MediumLockList *pMediumLockList(new MediumLockList());
14566 // There can be attachments without a medium (floppy/dvd), and thus
14567 // it's impossible to create a medium lock list. It still makes sense
14568 // to have the empty medium lock list in the map in case a medium is
14569 // attached later.
14570 if (pMedium != NULL)
14571 {
14572 MediumType_T mediumType = pMedium->i_getType();
14573 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14574 || mediumType == MediumType_Shareable;
14575 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14576
14577 alock.release();
14578 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14579 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14580 false /* fMediumLockWriteAll */,
14581 NULL,
14582 *pMediumLockList);
14583 alock.acquire();
14584 if (FAILED(mrc))
14585 {
14586 delete pMediumLockList;
14587 mData->mSession.mLockedMedia.Clear();
14588 break;
14589 }
14590 }
14591
14592 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14593 if (FAILED(rc))
14594 {
14595 mData->mSession.mLockedMedia.Clear();
14596 mrc = setError(rc,
14597 tr("Collecting locking information for all attached media failed"));
14598 break;
14599 }
14600 }
14601
14602 if (SUCCEEDED(mrc))
14603 {
14604 /* Now lock all media. If this fails, nothing is locked. */
14605 alock.release();
14606 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14607 alock.acquire();
14608 if (FAILED(rc))
14609 {
14610 mrc = setError(rc,
14611 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14612 }
14613 }
14614
14615 return mrc;
14616}
14617
14618/**
14619 * Undoes the locks made by by #lockMedia().
14620 */
14621HRESULT SessionMachine::i_unlockMedia()
14622{
14623 AutoCaller autoCaller(this);
14624 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14625
14626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14627
14628 /* we may be holding important error info on the current thread;
14629 * preserve it */
14630 ErrorInfoKeeper eik;
14631
14632 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14633 AssertComRC(rc);
14634 return rc;
14635}
14636
14637/**
14638 * Helper to change the machine state (reimplementation).
14639 *
14640 * @note Locks this object for writing.
14641 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14642 * it can cause crashes in random places due to unexpectedly committing
14643 * the current settings. The caller is responsible for that. The call
14644 * to saveStateSettings is fine, because this method does not commit.
14645 */
14646HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14647{
14648 LogFlowThisFuncEnter();
14649
14650 AutoCaller autoCaller(this);
14651 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14652
14653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14654
14655 MachineState_T oldMachineState = mData->mMachineState;
14656
14657 AssertMsgReturn(oldMachineState != aMachineState,
14658 ("oldMachineState=%s, aMachineState=%s\n",
14659 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14660 E_FAIL);
14661
14662 HRESULT rc = S_OK;
14663
14664 int stsFlags = 0;
14665 bool deleteSavedState = false;
14666
14667 /* detect some state transitions */
14668
14669 if ( ( ( oldMachineState == MachineState_Saved
14670 || oldMachineState == MachineState_AbortedSaved
14671 )
14672 && aMachineState == MachineState_Restoring
14673 )
14674 || ( ( oldMachineState == MachineState_PoweredOff
14675 || oldMachineState == MachineState_Teleported
14676 || oldMachineState == MachineState_Aborted
14677 )
14678 && ( aMachineState == MachineState_TeleportingIn
14679 || aMachineState == MachineState_Starting
14680 )
14681 )
14682 )
14683 {
14684 /* The EMT thread is about to start */
14685
14686 /* Nothing to do here for now... */
14687
14688 /// @todo NEWMEDIA don't let mDVDDrive and other children
14689 /// change anything when in the Starting/Restoring state
14690 }
14691 else if ( ( oldMachineState == MachineState_Running
14692 || oldMachineState == MachineState_Paused
14693 || oldMachineState == MachineState_Teleporting
14694 || oldMachineState == MachineState_OnlineSnapshotting
14695 || oldMachineState == MachineState_LiveSnapshotting
14696 || oldMachineState == MachineState_Stuck
14697 || oldMachineState == MachineState_Starting
14698 || oldMachineState == MachineState_Stopping
14699 || oldMachineState == MachineState_Saving
14700 || oldMachineState == MachineState_Restoring
14701 || oldMachineState == MachineState_TeleportingPausedVM
14702 || oldMachineState == MachineState_TeleportingIn
14703 )
14704 && ( aMachineState == MachineState_PoweredOff
14705 || aMachineState == MachineState_Saved
14706 || aMachineState == MachineState_Teleported
14707 || aMachineState == MachineState_Aborted
14708 || aMachineState == MachineState_AbortedSaved
14709 )
14710 )
14711 {
14712 /* The EMT thread has just stopped, unlock attached media. Note that as
14713 * opposed to locking that is done from Console, we do unlocking here
14714 * because the VM process may have aborted before having a chance to
14715 * properly unlock all media it locked. */
14716
14717 unlockMedia();
14718 }
14719
14720 if (oldMachineState == MachineState_Restoring)
14721 {
14722 if (aMachineState != MachineState_Saved && aMachineState != MachineState_AbortedSaved)
14723 {
14724 /*
14725 * delete the saved state file once the machine has finished
14726 * restoring from it (note that Console sets the state from
14727 * Restoring to AbortedSaved if the VM couldn't restore successfully,
14728 * to give the user an ability to fix an error and retry --
14729 * we keep the saved state file in this case)
14730 */
14731 deleteSavedState = true;
14732 }
14733 }
14734 else if ( oldMachineState == MachineState_Saved
14735 && ( aMachineState == MachineState_PoweredOff
14736 || aMachineState == MachineState_Teleported
14737 )
14738 )
14739 {
14740 /* delete the saved state after SessionMachine::ForgetSavedState() is called */
14741 deleteSavedState = true;
14742 mData->mCurrentStateModified = TRUE;
14743 stsFlags |= SaveSTS_CurStateModified;
14744 }
14745 /* failure to reach the restoring state should always go to MachineState_AbortedSaved */
14746 Assert(!(oldMachineState == MachineState_Saved && aMachineState == MachineState_Aborted));
14747
14748 if ( aMachineState == MachineState_Starting
14749 || aMachineState == MachineState_Restoring
14750 || aMachineState == MachineState_TeleportingIn
14751 )
14752 {
14753 /* set the current state modified flag to indicate that the current
14754 * state is no more identical to the state in the
14755 * current snapshot */
14756 if (!mData->mCurrentSnapshot.isNull())
14757 {
14758 mData->mCurrentStateModified = TRUE;
14759 stsFlags |= SaveSTS_CurStateModified;
14760 }
14761 }
14762
14763 if (deleteSavedState)
14764 {
14765 if (mRemoveSavedState)
14766 {
14767 Assert(!mSSData->strStateFilePath.isEmpty());
14768
14769 // it is safe to delete the saved state file if ...
14770 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14771 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14772 // ... none of the snapshots share the saved state file
14773 )
14774 RTFileDelete(mSSData->strStateFilePath.c_str());
14775 }
14776
14777 mSSData->strStateFilePath.setNull();
14778 stsFlags |= SaveSTS_StateFilePath;
14779 }
14780
14781 /* redirect to the underlying peer machine */
14782 mPeer->i_setMachineState(aMachineState);
14783
14784 if ( oldMachineState != MachineState_RestoringSnapshot
14785 && ( aMachineState == MachineState_PoweredOff
14786 || aMachineState == MachineState_Teleported
14787 || aMachineState == MachineState_Aborted
14788 || aMachineState == MachineState_AbortedSaved
14789 || aMachineState == MachineState_Saved))
14790 {
14791 /* the machine has stopped execution
14792 * (or the saved state file was adopted) */
14793 stsFlags |= SaveSTS_StateTimeStamp;
14794 }
14795
14796 if ( ( oldMachineState == MachineState_PoweredOff
14797 || oldMachineState == MachineState_Aborted
14798 || oldMachineState == MachineState_Teleported
14799 )
14800 && aMachineState == MachineState_Saved)
14801 {
14802 /* the saved state file was adopted */
14803 Assert(!mSSData->strStateFilePath.isEmpty());
14804 stsFlags |= SaveSTS_StateFilePath;
14805 }
14806
14807#ifdef VBOX_WITH_GUEST_PROPS
14808 if ( aMachineState == MachineState_PoweredOff
14809 || aMachineState == MachineState_Aborted
14810 || aMachineState == MachineState_Teleported)
14811 {
14812 /* Make sure any transient guest properties get removed from the
14813 * property store on shutdown. */
14814 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14815
14816 /* remove it from the settings representation */
14817 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14818 for (settings::GuestPropertiesList::iterator
14819 it = llGuestProperties.begin();
14820 it != llGuestProperties.end();
14821 /*nothing*/)
14822 {
14823 const settings::GuestProperty &prop = *it;
14824 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14825 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14826 {
14827 it = llGuestProperties.erase(it);
14828 fNeedsSaving = true;
14829 }
14830 else
14831 {
14832 ++it;
14833 }
14834 }
14835
14836 /* Additionally remove it from the HWData representation. Required to
14837 * keep everything in sync, as this is what the API keeps using. */
14838 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14839 for (HWData::GuestPropertyMap::iterator
14840 it = llHWGuestProperties.begin();
14841 it != llHWGuestProperties.end();
14842 /*nothing*/)
14843 {
14844 uint32_t fFlags = it->second.mFlags;
14845 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14846 {
14847 /* iterator where we need to continue after the erase call
14848 * (C++03 is a fact still, and it doesn't return the iterator
14849 * which would allow continuing) */
14850 HWData::GuestPropertyMap::iterator it2 = it;
14851 ++it2;
14852 llHWGuestProperties.erase(it);
14853 it = it2;
14854 fNeedsSaving = true;
14855 }
14856 else
14857 {
14858 ++it;
14859 }
14860 }
14861
14862 if (fNeedsSaving)
14863 {
14864 mData->mCurrentStateModified = TRUE;
14865 stsFlags |= SaveSTS_CurStateModified;
14866 }
14867 }
14868#endif /* VBOX_WITH_GUEST_PROPS */
14869
14870 rc = i_saveStateSettings(stsFlags);
14871
14872 if ( ( oldMachineState != MachineState_PoweredOff
14873 && oldMachineState != MachineState_Aborted
14874 && oldMachineState != MachineState_Teleported
14875 )
14876 && ( aMachineState == MachineState_PoweredOff
14877 || aMachineState == MachineState_Aborted
14878 || aMachineState == MachineState_Teleported
14879 )
14880 )
14881 {
14882 /* we've been shut down for any reason */
14883 /* no special action so far */
14884 }
14885
14886 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14887 LogFlowThisFuncLeave();
14888 return rc;
14889}
14890
14891/**
14892 * Sends the current machine state value to the VM process.
14893 *
14894 * @note Locks this object for reading, then calls a client process.
14895 */
14896HRESULT SessionMachine::i_updateMachineStateOnClient()
14897{
14898 AutoCaller autoCaller(this);
14899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14900
14901 ComPtr<IInternalSessionControl> directControl;
14902 {
14903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14904 AssertReturn(!!mData, E_FAIL);
14905 if (mData->mSession.mLockType == LockType_VM)
14906 directControl = mData->mSession.mDirectControl;
14907
14908 /* directControl may be already set to NULL here in #OnSessionEnd()
14909 * called too early by the direct session process while there is still
14910 * some operation (like deleting the snapshot) in progress. The client
14911 * process in this case is waiting inside Session::close() for the
14912 * "end session" process object to complete, while #uninit() called by
14913 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14914 * operation to complete. For now, we accept this inconsistent behavior
14915 * and simply do nothing here. */
14916
14917 if (mData->mSession.mState == SessionState_Unlocking)
14918 return S_OK;
14919 }
14920
14921 /* ignore notifications sent after #OnSessionEnd() is called */
14922 if (!directControl)
14923 return S_OK;
14924
14925 return directControl->UpdateMachineState(mData->mMachineState);
14926}
14927
14928
14929/*static*/
14930HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14931{
14932 va_list args;
14933 va_start(args, pcszMsg);
14934 HRESULT rc = setErrorInternalV(aResultCode,
14935 getStaticClassIID(),
14936 getStaticComponentName(),
14937 pcszMsg, args,
14938 false /* aWarning */,
14939 true /* aLogIt */);
14940 va_end(args);
14941 return rc;
14942}
14943
14944
14945HRESULT Machine::updateState(MachineState_T aState)
14946{
14947 NOREF(aState);
14948 ReturnComNotImplemented();
14949}
14950
14951HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14952{
14953 NOREF(aProgress);
14954 ReturnComNotImplemented();
14955}
14956
14957HRESULT Machine::endPowerUp(LONG aResult)
14958{
14959 NOREF(aResult);
14960 ReturnComNotImplemented();
14961}
14962
14963HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14964{
14965 NOREF(aProgress);
14966 ReturnComNotImplemented();
14967}
14968
14969HRESULT Machine::endPoweringDown(LONG aResult,
14970 const com::Utf8Str &aErrMsg)
14971{
14972 NOREF(aResult);
14973 NOREF(aErrMsg);
14974 ReturnComNotImplemented();
14975}
14976
14977HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14978 BOOL *aMatched,
14979 ULONG *aMaskedInterfaces)
14980{
14981 NOREF(aDevice);
14982 NOREF(aMatched);
14983 NOREF(aMaskedInterfaces);
14984 ReturnComNotImplemented();
14985
14986}
14987
14988HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14989{
14990 NOREF(aId); NOREF(aCaptureFilename);
14991 ReturnComNotImplemented();
14992}
14993
14994HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14995 BOOL aDone)
14996{
14997 NOREF(aId);
14998 NOREF(aDone);
14999 ReturnComNotImplemented();
15000}
15001
15002HRESULT Machine::autoCaptureUSBDevices()
15003{
15004 ReturnComNotImplemented();
15005}
15006
15007HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15008{
15009 NOREF(aDone);
15010 ReturnComNotImplemented();
15011}
15012
15013HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15014 ComPtr<IProgress> &aProgress)
15015{
15016 NOREF(aSession);
15017 NOREF(aProgress);
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::finishOnlineMergeMedium()
15022{
15023 ReturnComNotImplemented();
15024}
15025
15026HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15027 std::vector<com::Utf8Str> &aValues,
15028 std::vector<LONG64> &aTimestamps,
15029 std::vector<com::Utf8Str> &aFlags)
15030{
15031 NOREF(aNames);
15032 NOREF(aValues);
15033 NOREF(aTimestamps);
15034 NOREF(aFlags);
15035 ReturnComNotImplemented();
15036}
15037
15038HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15039 const com::Utf8Str &aValue,
15040 LONG64 aTimestamp,
15041 const com::Utf8Str &aFlags)
15042{
15043 NOREF(aName);
15044 NOREF(aValue);
15045 NOREF(aTimestamp);
15046 NOREF(aFlags);
15047 ReturnComNotImplemented();
15048}
15049
15050HRESULT Machine::lockMedia()
15051{
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::unlockMedia()
15056{
15057 ReturnComNotImplemented();
15058}
15059
15060HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15061 ComPtr<IMediumAttachment> &aNewAttachment)
15062{
15063 NOREF(aAttachment);
15064 NOREF(aNewAttachment);
15065 ReturnComNotImplemented();
15066}
15067
15068HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15069 ULONG aCpuUser,
15070 ULONG aCpuKernel,
15071 ULONG aCpuIdle,
15072 ULONG aMemTotal,
15073 ULONG aMemFree,
15074 ULONG aMemBalloon,
15075 ULONG aMemShared,
15076 ULONG aMemCache,
15077 ULONG aPagedTotal,
15078 ULONG aMemAllocTotal,
15079 ULONG aMemFreeTotal,
15080 ULONG aMemBalloonTotal,
15081 ULONG aMemSharedTotal,
15082 ULONG aVmNetRx,
15083 ULONG aVmNetTx)
15084{
15085 NOREF(aValidStats);
15086 NOREF(aCpuUser);
15087 NOREF(aCpuKernel);
15088 NOREF(aCpuIdle);
15089 NOREF(aMemTotal);
15090 NOREF(aMemFree);
15091 NOREF(aMemBalloon);
15092 NOREF(aMemShared);
15093 NOREF(aMemCache);
15094 NOREF(aPagedTotal);
15095 NOREF(aMemAllocTotal);
15096 NOREF(aMemFreeTotal);
15097 NOREF(aMemBalloonTotal);
15098 NOREF(aMemSharedTotal);
15099 NOREF(aVmNetRx);
15100 NOREF(aVmNetTx);
15101 ReturnComNotImplemented();
15102}
15103
15104HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15105 com::Utf8Str &aResult)
15106{
15107 NOREF(aAuthParams);
15108 NOREF(aResult);
15109 ReturnComNotImplemented();
15110}
15111
15112com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15113{
15114 com::Utf8Str strControllerName = "Unknown";
15115 switch (aBusType)
15116 {
15117 case StorageBus_IDE:
15118 {
15119 strControllerName = "IDE";
15120 break;
15121 }
15122 case StorageBus_SATA:
15123 {
15124 strControllerName = "SATA";
15125 break;
15126 }
15127 case StorageBus_SCSI:
15128 {
15129 strControllerName = "SCSI";
15130 break;
15131 }
15132 case StorageBus_Floppy:
15133 {
15134 strControllerName = "Floppy";
15135 break;
15136 }
15137 case StorageBus_SAS:
15138 {
15139 strControllerName = "SAS";
15140 break;
15141 }
15142 case StorageBus_USB:
15143 {
15144 strControllerName = "USB";
15145 break;
15146 }
15147 default:
15148 break;
15149 }
15150 return strControllerName;
15151}
15152
15153HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15154{
15155 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15156
15157 AutoCaller autoCaller(this);
15158 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15159
15160 HRESULT rc = S_OK;
15161
15162 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15163 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15164 rc = getUSBDeviceFilters(usbDeviceFilters);
15165 if (FAILED(rc)) return rc;
15166
15167 NOREF(aFlags);
15168 com::Utf8Str osTypeId;
15169 ComObjPtr<GuestOSType> osType = NULL;
15170
15171 /* Get the guest os type as a string from the VB. */
15172 rc = getOSTypeId(osTypeId);
15173 if (FAILED(rc)) return rc;
15174
15175 /* Get the os type obj that coresponds, can be used to get
15176 * the defaults for this guest OS. */
15177 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15178 if (FAILED(rc)) return rc;
15179
15180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15181
15182 /* Let the OS type select 64-bit ness. */
15183 mHWData->mLongMode = osType->i_is64Bit()
15184 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15185
15186 /* Let the OS type enable the X2APIC */
15187 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15188
15189 /* This one covers IOAPICEnabled. */
15190 mBIOSSettings->i_applyDefaults(osType);
15191
15192 /* Initialize default record settings. */
15193 mRecordingSettings->i_applyDefaults();
15194
15195 /* Initialize default BIOS settings here */
15196 /* Hardware virtualization must be ON by default */
15197 mHWData->mAPIC = true;
15198 mHWData->mHWVirtExEnabled = true;
15199
15200 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15201 if (FAILED(rc)) return rc;
15202
15203 rc = osType->COMGETTER(RecommendedCPUCount)(&mHWData->mCPUCount);
15204 if (FAILED(rc)) return rc;
15205
15206 /* Graphics stuff. */
15207 GraphicsControllerType_T graphicsController;
15208 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15209 if (FAILED(rc)) return rc;
15210
15211 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15212 if (FAILED(rc)) return rc;
15213
15214 ULONG vramSize;
15215 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15216 if (FAILED(rc)) return rc;
15217
15218 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15219 if (FAILED(rc)) return rc;
15220
15221 BOOL fAccelerate2DVideoEnabled;
15222 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15223 if (FAILED(rc)) return rc;
15224
15225 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15226 if (FAILED(rc)) return rc;
15227
15228 BOOL fAccelerate3DEnabled;
15229 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15230 if (FAILED(rc)) return rc;
15231
15232 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15233 if (FAILED(rc)) return rc;
15234
15235 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15236 if (FAILED(rc)) return rc;
15237
15238 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15239 if (FAILED(rc)) return rc;
15240
15241 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15242 if (FAILED(rc)) return rc;
15243
15244 BOOL mRTCUseUTC;
15245 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15246 if (FAILED(rc)) return rc;
15247
15248 setRTCUseUTC(mRTCUseUTC);
15249 if (FAILED(rc)) return rc;
15250
15251 /* the setter does more than just the assignment, so use it */
15252 ChipsetType_T enmChipsetType;
15253 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15254 if (FAILED(rc)) return rc;
15255
15256 rc = COMSETTER(ChipsetType)(enmChipsetType);
15257 if (FAILED(rc)) return rc;
15258
15259 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15260 if (FAILED(rc)) return rc;
15261
15262 /* Apply IOMMU defaults. */
15263 IommuType_T enmIommuType;
15264 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15265 if (FAILED(rc)) return rc;
15266
15267 rc = COMSETTER(IommuType)(enmIommuType);
15268 if (FAILED(rc)) return rc;
15269
15270 /* Apply network adapters defaults */
15271 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15272 mNetworkAdapters[slot]->i_applyDefaults(osType);
15273
15274 /* Apply serial port defaults */
15275 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15276 mSerialPorts[slot]->i_applyDefaults(osType);
15277
15278 /* Apply parallel port defaults - not OS dependent*/
15279 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15280 mParallelPorts[slot]->i_applyDefaults();
15281
15282 /* This one covers the TPM type. */
15283 mTrustedPlatformModule->i_applyDefaults(osType);
15284
15285 /* This one covers secure boot. */
15286 rc = mNvramStore->i_applyDefaults(osType);
15287 if (FAILED(rc)) return rc;
15288
15289 /* Audio stuff. */
15290 AudioControllerType_T audioController;
15291 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15292 if (FAILED(rc)) return rc;
15293
15294 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15295 if (FAILED(rc)) return rc;
15296
15297 AudioCodecType_T audioCodec;
15298 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15299 if (FAILED(rc)) return rc;
15300
15301 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15302 if (FAILED(rc)) return rc;
15303
15304 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15305 if (FAILED(rc)) return rc;
15306
15307 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15308 if (FAILED(rc)) return rc;
15309
15310 /* Storage Controllers */
15311 StorageControllerType_T hdStorageControllerType;
15312 StorageBus_T hdStorageBusType;
15313 StorageControllerType_T dvdStorageControllerType;
15314 StorageBus_T dvdStorageBusType;
15315 BOOL recommendedFloppy;
15316 ComPtr<IStorageController> floppyController;
15317 ComPtr<IStorageController> hdController;
15318 ComPtr<IStorageController> dvdController;
15319 Utf8Str strFloppyName, strDVDName, strHDName;
15320
15321 /* GUI auto generates controller names using bus type. Do the same*/
15322 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15323
15324 /* Floppy recommended? add one. */
15325 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15326 if (FAILED(rc)) return rc;
15327 if (recommendedFloppy)
15328 {
15329 rc = addStorageController(strFloppyName,
15330 StorageBus_Floppy,
15331 floppyController);
15332 if (FAILED(rc)) return rc;
15333 }
15334
15335 /* Setup one DVD storage controller. */
15336 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15337 if (FAILED(rc)) return rc;
15338
15339 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15340 if (FAILED(rc)) return rc;
15341
15342 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15343
15344 rc = addStorageController(strDVDName,
15345 dvdStorageBusType,
15346 dvdController);
15347 if (FAILED(rc)) return rc;
15348
15349 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15350 if (FAILED(rc)) return rc;
15351
15352 /* Setup one HDD storage controller. */
15353 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15354 if (FAILED(rc)) return rc;
15355
15356 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15357 if (FAILED(rc)) return rc;
15358
15359 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15360
15361 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15362 {
15363 rc = addStorageController(strHDName,
15364 hdStorageBusType,
15365 hdController);
15366 if (FAILED(rc)) return rc;
15367
15368 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15369 if (FAILED(rc)) return rc;
15370 }
15371 else
15372 {
15373 /* The HD controller is the same as DVD: */
15374 hdController = dvdController;
15375 }
15376
15377 /* Limit the AHCI port count if it's used because windows has trouble with
15378 * too many ports and other guest (OS X in particular) may take extra long
15379 * boot: */
15380
15381 // pParent = static_cast<Medium*>(aP)
15382 IStorageController *temp = hdController;
15383 ComObjPtr<StorageController> storageController;
15384 storageController = static_cast<StorageController *>(temp);
15385
15386 // tempHDController = aHDController;
15387 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15388 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15389 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15390 storageController->COMSETTER(PortCount)(1);
15391
15392 /* USB stuff */
15393
15394 bool ohciEnabled = false;
15395
15396 ComPtr<IUSBController> usbController;
15397 BOOL recommendedUSB3;
15398 BOOL recommendedUSB;
15399 BOOL usbProxyAvailable;
15400
15401 getUSBProxyAvailable(&usbProxyAvailable);
15402 if (FAILED(rc)) return rc;
15403
15404 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15405 if (FAILED(rc)) return rc;
15406 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15407 if (FAILED(rc)) return rc;
15408
15409 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15410 {
15411#ifdef VBOX_WITH_EXTPACK
15412 /* USB 3.0 is only available if the proper ExtPack is installed. */
15413 ExtPackManager *aManager = mParent->i_getExtPackManager();
15414 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15415 {
15416 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15417 if (FAILED(rc)) return rc;
15418
15419 /* xHci includes OHCI */
15420 ohciEnabled = true;
15421 }
15422#endif
15423 }
15424 if ( !ohciEnabled
15425 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15426 {
15427 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15428 if (FAILED(rc)) return rc;
15429 ohciEnabled = true;
15430
15431#ifdef VBOX_WITH_EXTPACK
15432 /* USB 2.0 is only available if the proper ExtPack is installed.
15433 * Note. Configuring EHCI here and providing messages about
15434 * the missing extpack isn't exactly clean, but it is a
15435 * necessary evil to patch over legacy compatability issues
15436 * introduced by the new distribution model. */
15437 ExtPackManager *manager = mParent->i_getExtPackManager();
15438 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15439 {
15440 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15441 if (FAILED(rc)) return rc;
15442 }
15443#endif
15444 }
15445
15446 /* Set recommended human interface device types: */
15447 BOOL recommendedUSBHID;
15448 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15449 if (FAILED(rc)) return rc;
15450
15451 if (recommendedUSBHID)
15452 {
15453 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15454 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15455 if (!ohciEnabled && !usbDeviceFilters.isNull())
15456 {
15457 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15458 if (FAILED(rc)) return rc;
15459 }
15460 }
15461
15462 BOOL recommendedUSBTablet;
15463 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15464 if (FAILED(rc)) return rc;
15465
15466 if (recommendedUSBTablet)
15467 {
15468 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15469 if (!ohciEnabled && !usbDeviceFilters.isNull())
15470 {
15471 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15472 if (FAILED(rc)) return rc;
15473 }
15474 }
15475 return S_OK;
15476}
15477
15478/* This isn't handled entirely by the wrapper generator yet. */
15479#ifdef VBOX_WITH_XPCOM
15480NS_DECL_CLASSINFO(SessionMachine)
15481NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15482
15483NS_DECL_CLASSINFO(SnapshotMachine)
15484NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15485#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