VirtualBox

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

Last change on this file since 86697 was 86498, checked in by vboxsync, 4 years ago

VBoxSVC/MachineImpl.cpp: Fixed use-after-free problem in Machine::i_prepareSaveSettings where a Utf8Str::printf was fed its old value as one of the format argument. bugref:9841

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 537.5 KB
Line 
1/* $Id: MachineImpl.cpp 86498 2020-10-08 17:00:21Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#ifdef VBOX_WITH_SHARED_CLIPBOARD
91# include <VBox/HostServices/VBoxClipboardSvc.h>
92#endif
93
94#include "VBox/com/MultiResult.h"
95
96#include <algorithm>
97
98#ifdef VBOX_WITH_DTRACE_R3_MAIN
99# include "dtrace/VBoxAPI.h"
100#endif
101
102#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
103# define HOSTSUFF_EXE ".exe"
104#else /* !RT_OS_WINDOWS */
105# define HOSTSUFF_EXE ""
106#endif /* !RT_OS_WINDOWS */
107
108// defines / prototypes
109/////////////////////////////////////////////////////////////////////////////
110
111/////////////////////////////////////////////////////////////////////////////
112// Machine::Data structure
113/////////////////////////////////////////////////////////////////////////////
114
115Machine::Data::Data()
116{
117 mRegistered = FALSE;
118 pMachineConfigFile = NULL;
119 /* Contains hints on what has changed when the user is using the VM (config
120 * changes, running the VM, ...). This is used to decide if a config needs
121 * to be written to disk. */
122 flModifications = 0;
123 /* VM modification usually also trigger setting the current state to
124 * "Modified". Although this is not always the case. An e.g. is the VM
125 * initialization phase or when snapshot related data is changed. The
126 * actually behavior is controlled by the following flag. */
127 m_fAllowStateModification = false;
128 mAccessible = FALSE;
129 /* mUuid is initialized in Machine::init() */
130
131 mMachineState = MachineState_PoweredOff;
132 RTTimeNow(&mLastStateChange);
133
134 mMachineStateDeps = 0;
135 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
136 mMachineStateChangePending = 0;
137
138 mCurrentStateModified = TRUE;
139 mGuestPropertiesModified = FALSE;
140
141 mSession.mPID = NIL_RTPROCESS;
142 mSession.mLockType = LockType_Null;
143 mSession.mState = SessionState_Unlocked;
144}
145
146Machine::Data::~Data()
147{
148 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
149 {
150 RTSemEventMultiDestroy(mMachineStateDepsSem);
151 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
152 }
153 if (pMachineConfigFile)
154 {
155 delete pMachineConfigFile;
156 pMachineConfigFile = NULL;
157 }
158}
159
160/////////////////////////////////////////////////////////////////////////////
161// Machine::HWData structure
162/////////////////////////////////////////////////////////////////////////////
163
164Machine::HWData::HWData()
165{
166 /* default values for a newly created machine */
167 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
168 mMemorySize = 128;
169 mCPUCount = 1;
170 mCPUHotPlugEnabled = false;
171 mMemoryBalloonSize = 0;
172 mPageFusionEnabled = false;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185 mHWVirtExVirtVmsaveVmload = true;
186#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
187 mPAEEnabled = true;
188#else
189 mPAEEnabled = false;
190#endif
191 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
192 mTripleFaultReset = false;
193 mAPIC = true;
194 mX2APIC = false;
195 mIBPBOnVMExit = false;
196 mIBPBOnVMEntry = false;
197 mSpecCtrl = false;
198 mSpecCtrlByHost = false;
199 mL1DFlushOnSched = true;
200 mL1DFlushOnVMEntry = false;
201 mMDSClearOnSched = true;
202 mMDSClearOnVMEntry = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mClipboardFileTransfersEnabled = FALSE;
218
219 mDnDMode = DnDMode_Disabled;
220
221 mFirmwareType = FirmwareType_BIOS;
222 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
223 mPointingHIDType = PointingHIDType_PS2Mouse;
224 mChipsetType = ChipsetType_PIIX3;
225 mParavirtProvider = ParavirtProvider_Default;
226 mEmulatedUSBCardReaderEnabled = FALSE;
227
228 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
229 mCPUAttached[i] = false;
230
231 mIOCacheEnabled = true;
232 mIOCacheSize = 5; /* 5MB */
233}
234
235Machine::HWData::~HWData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param strOsType OS Type string (stored as is if aOsType is NULL).
283 * @param aOsType OS Type of this machine or NULL.
284 * @param aId UUID for the new machine.
285 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
286 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
287 * scheme (includes the UUID).
288 *
289 * @return Success indicator. if not S_OK, the machine object is invalid
290 */
291HRESULT Machine::init(VirtualBox *aParent,
292 const Utf8Str &strConfigFile,
293 const Utf8Str &strName,
294 const StringsList &llGroups,
295 const Utf8Str &strOsType,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 if (llGroups.size())
332 mUserData->s.llGroups = llGroups;
333
334 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
335 // the "name sync" flag determines whether the machine directory gets renamed along
336 // with the machine file; say so if the settings file name is the same as the
337 // settings file parent directory (machine directory)
338 mUserData->s.fNameSync = i_isInOwnDir();
339
340 // initialize the default snapshots folder
341 rc = COMSETTER(SnapshotFolder)(NULL);
342 AssertComRC(rc);
343
344 if (aOsType)
345 {
346 /* Store OS type */
347 mUserData->s.strOsType = aOsType->i_id();
348
349 /* Let the OS type select 64-bit ness. */
350 mHWData->mLongMode = aOsType->i_is64Bit()
351 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
352
353 /* Let the OS type enable the X2APIC */
354 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
355 }
356 else if (!strOsType.isEmpty())
357 {
358 /* Store OS type */
359 mUserData->s.strOsType = strOsType;
360
361 /* No guest OS type object. Pick some plausible defaults which the
362 * host can handle. There's no way to know or validate anything. */
363 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
364 mHWData->mX2APIC = false;
365 }
366
367 /* Apply BIOS defaults. */
368 mBIOSSettings->i_applyDefaults(aOsType);
369
370 /* Apply record defaults. */
371 mRecordingSettings->i_applyDefaults();
372
373 /* Apply network adapters defaults */
374 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
375 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
376
377 /* Apply serial port defaults */
378 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
379 mSerialPorts[slot]->i_applyDefaults(aOsType);
380
381 /* Apply parallel port defaults */
382 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
383 mParallelPorts[slot]->i_applyDefaults();
384
385 /* At this point the changing of the current state modification
386 * flag is allowed. */
387 i_allowStateModification();
388
389 /* commit all changes made during the initialization */
390 i_commit();
391 }
392
393 /* Confirm a successful initialization when it's the case */
394 if (SUCCEEDED(rc))
395 {
396 if (mData->mAccessible)
397 autoInitSpan.setSucceeded();
398 else
399 autoInitSpan.setLimited();
400 }
401
402 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
403 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
404 mData->mRegistered,
405 mData->mAccessible,
406 rc));
407
408 LogFlowThisFuncLeave();
409
410 return rc;
411}
412
413/**
414 * Initializes a new instance with data from machine XML (formerly Init_Registered).
415 * Gets called in two modes:
416 *
417 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
418 * UUID is specified and we mark the machine as "registered";
419 *
420 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
421 * and the machine remains unregistered until RegisterMachine() is called.
422 *
423 * @param aParent Associated parent object
424 * @param strConfigFile Local file system path to the VM settings file (can
425 * be relative to the VirtualBox config directory).
426 * @param aId UUID of the machine or NULL (see above).
427 *
428 * @return Success indicator. if not S_OK, the machine object is invalid
429 */
430HRESULT Machine::initFromSettings(VirtualBox *aParent,
431 const Utf8Str &strConfigFile,
432 const Guid *aId)
433{
434 LogFlowThisFuncEnter();
435 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
436
437 /* Enclose the state transition NotReady->InInit->Ready */
438 AutoInitSpan autoInitSpan(this);
439 AssertReturn(autoInitSpan.isOk(), E_FAIL);
440
441 HRESULT rc = initImpl(aParent, strConfigFile);
442 if (FAILED(rc)) return rc;
443
444 if (aId)
445 {
446 // loading a registered VM:
447 unconst(mData->mUuid) = *aId;
448 mData->mRegistered = TRUE;
449 // now load the settings from XML:
450 rc = i_registeredInit();
451 // this calls initDataAndChildObjects() and loadSettings()
452 }
453 else
454 {
455 // opening an unregistered VM (VirtualBox::OpenMachine()):
456 rc = initDataAndChildObjects();
457
458 if (SUCCEEDED(rc))
459 {
460 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
461 mData->mAccessible = TRUE;
462
463 try
464 {
465 // load and parse machine XML; this will throw on XML or logic errors
466 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
467
468 // reject VM UUID duplicates, they can happen if someone
469 // tries to register an already known VM config again
470 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
471 true /* fPermitInaccessible */,
472 false /* aDoSetError */,
473 NULL) != VBOX_E_OBJECT_NOT_FOUND)
474 {
475 throw setError(E_FAIL,
476 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
477 mData->m_strConfigFile.c_str());
478 }
479
480 // use UUID from machine config
481 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
482
483 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
484 NULL /* puuidRegistry */);
485 if (FAILED(rc)) throw rc;
486
487 /* At this point the changing of the current state modification
488 * flag is allowed. */
489 i_allowStateModification();
490
491 i_commit();
492 }
493 catch (HRESULT err)
494 {
495 /* we assume that error info is set by the thrower */
496 rc = err;
497 }
498 catch (...)
499 {
500 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
501 }
502 }
503 }
504
505 /* Confirm a successful initialization when it's the case */
506 if (SUCCEEDED(rc))
507 {
508 if (mData->mAccessible)
509 autoInitSpan.setSucceeded();
510 else
511 {
512 autoInitSpan.setLimited();
513
514 // uninit media from this machine's media registry, or else
515 // reloading the settings will fail
516 mParent->i_unregisterMachineMedia(i_getId());
517 }
518 }
519
520 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
521 "rc=%08X\n",
522 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
523 mData->mRegistered, mData->mAccessible, rc));
524
525 LogFlowThisFuncLeave();
526
527 return rc;
528}
529
530/**
531 * Initializes a new instance from a machine config that is already in memory
532 * (import OVF case). Since we are importing, the UUID in the machine
533 * config is ignored and we always generate a fresh one.
534 *
535 * @param aParent Associated parent object.
536 * @param strName Name for the new machine; this overrides what is specified in config.
537 * @param strSettingsFilename File name of .vbox file.
538 * @param config Machine configuration loaded and parsed from XML.
539 *
540 * @return Success indicator. if not S_OK, the machine object is invalid
541 */
542HRESULT Machine::init(VirtualBox *aParent,
543 const Utf8Str &strName,
544 const Utf8Str &strSettingsFilename,
545 const settings::MachineConfigFile &config)
546{
547 LogFlowThisFuncEnter();
548
549 /* Enclose the state transition NotReady->InInit->Ready */
550 AutoInitSpan autoInitSpan(this);
551 AssertReturn(autoInitSpan.isOk(), E_FAIL);
552
553 HRESULT rc = initImpl(aParent, strSettingsFilename);
554 if (FAILED(rc)) return rc;
555
556 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
557 if (FAILED(rc)) return rc;
558
559 rc = initDataAndChildObjects();
560
561 if (SUCCEEDED(rc))
562 {
563 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
564 mData->mAccessible = TRUE;
565
566 // create empty machine config for instance data
567 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
568
569 // generate fresh UUID, ignore machine config
570 unconst(mData->mUuid).create();
571
572 rc = i_loadMachineDataFromSettings(config,
573 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
574
575 // override VM name as well, it may be different
576 mUserData->s.strName = strName;
577
578 if (SUCCEEDED(rc))
579 {
580 /* At this point the changing of the current state modification
581 * flag is allowed. */
582 i_allowStateModification();
583
584 /* commit all changes made during the initialization */
585 i_commit();
586 }
587 }
588
589 /* Confirm a successful initialization when it's the case */
590 if (SUCCEEDED(rc))
591 {
592 if (mData->mAccessible)
593 autoInitSpan.setSucceeded();
594 else
595 {
596 /* Ignore all errors from unregistering, they would destroy
597- * the more interesting error information we already have,
598- * pinpointing the issue with the VM config. */
599 ErrorInfoKeeper eik;
600
601 autoInitSpan.setLimited();
602
603 // uninit media from this machine's media registry, or else
604 // reloading the settings will fail
605 mParent->i_unregisterMachineMedia(i_getId());
606 }
607 }
608
609 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
610 "rc=%08X\n",
611 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
612 mData->mRegistered, mData->mAccessible, rc));
613
614 LogFlowThisFuncLeave();
615
616 return rc;
617}
618
619/**
620 * Shared code between the various init() implementations.
621 * @param aParent The VirtualBox object.
622 * @param strConfigFile Settings file.
623 * @return
624 */
625HRESULT Machine::initImpl(VirtualBox *aParent,
626 const Utf8Str &strConfigFile)
627{
628 LogFlowThisFuncEnter();
629
630 AssertReturn(aParent, E_INVALIDARG);
631 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
632
633 HRESULT rc = S_OK;
634
635 /* share the parent weakly */
636 unconst(mParent) = aParent;
637
638 /* allocate the essential machine data structure (the rest will be
639 * allocated later by initDataAndChildObjects() */
640 mData.allocate();
641
642 /* memorize the config file name (as provided) */
643 mData->m_strConfigFile = strConfigFile;
644
645 /* get the full file name */
646 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
647 if (RT_FAILURE(vrc1))
648 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
649 tr("Invalid machine settings file name '%s' (%Rrc)"),
650 strConfigFile.c_str(),
651 vrc1);
652
653 LogFlowThisFuncLeave();
654
655 return rc;
656}
657
658/**
659 * Tries to create a machine settings file in the path stored in the machine
660 * instance data. Used when a new machine is created to fail gracefully if
661 * the settings file could not be written (e.g. because machine dir is read-only).
662 * @return
663 */
664HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
665{
666 HRESULT rc = S_OK;
667
668 // when we create a new machine, we must be able to create the settings file
669 RTFILE f = NIL_RTFILE;
670 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
671 if ( RT_SUCCESS(vrc)
672 || vrc == VERR_SHARING_VIOLATION
673 )
674 {
675 if (RT_SUCCESS(vrc))
676 RTFileClose(f);
677 if (!fForceOverwrite)
678 rc = setError(VBOX_E_FILE_ERROR,
679 tr("Machine settings file '%s' already exists"),
680 mData->m_strConfigFileFull.c_str());
681 else
682 {
683 /* try to delete the config file, as otherwise the creation
684 * of a new settings file will fail. */
685 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
686 if (RT_FAILURE(vrc2))
687 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
688 tr("Could not delete the existing settings file '%s' (%Rrc)"),
689 mData->m_strConfigFileFull.c_str(), vrc2);
690 }
691 }
692 else if ( vrc != VERR_FILE_NOT_FOUND
693 && vrc != VERR_PATH_NOT_FOUND
694 )
695 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
696 tr("Invalid machine settings file name '%s' (%Rrc)"),
697 mData->m_strConfigFileFull.c_str(),
698 vrc);
699 return rc;
700}
701
702/**
703 * Initializes the registered machine by loading the settings file.
704 * This method is separated from #init() in order to make it possible to
705 * retry the operation after VirtualBox startup instead of refusing to
706 * startup the whole VirtualBox server in case if the settings file of some
707 * registered VM is invalid or inaccessible.
708 *
709 * @note Must be always called from this object's write lock
710 * (unless called from #init() that doesn't need any locking).
711 * @note Locks the mUSBController method for writing.
712 * @note Subclasses must not call this method.
713 */
714HRESULT Machine::i_registeredInit()
715{
716 AssertReturn(!i_isSessionMachine(), E_FAIL);
717 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
718 AssertReturn(mData->mUuid.isValid(), E_FAIL);
719 AssertReturn(!mData->mAccessible, E_FAIL);
720
721 HRESULT rc = initDataAndChildObjects();
722
723 if (SUCCEEDED(rc))
724 {
725 /* Temporarily reset the registered flag in order to let setters
726 * potentially called from loadSettings() succeed (isMutable() used in
727 * all setters will return FALSE for a Machine instance if mRegistered
728 * is TRUE). */
729 mData->mRegistered = FALSE;
730
731 try
732 {
733 // load and parse machine XML; this will throw on XML or logic errors
734 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
735
736 if (mData->mUuid != mData->pMachineConfigFile->uuid)
737 throw setError(E_FAIL,
738 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
739 mData->pMachineConfigFile->uuid.raw(),
740 mData->m_strConfigFileFull.c_str(),
741 mData->mUuid.toString().c_str(),
742 mParent->i_settingsFilePath().c_str());
743
744 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
745 NULL /* const Guid *puuidRegistry */);
746 if (FAILED(rc)) throw rc;
747 }
748 catch (HRESULT err)
749 {
750 /* we assume that error info is set by the thrower */
751 rc = err;
752 }
753 catch (...)
754 {
755 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
756 }
757
758 /* Restore the registered flag (even on failure) */
759 mData->mRegistered = TRUE;
760 }
761
762 if (SUCCEEDED(rc))
763 {
764 /* Set mAccessible to TRUE only if we successfully locked and loaded
765 * the settings file */
766 mData->mAccessible = TRUE;
767
768 /* commit all changes made during loading the settings file */
769 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
770 /// @todo r=klaus for some reason the settings loading logic backs up
771 // the settings, and therefore a commit is needed. Should probably be changed.
772 }
773 else
774 {
775 /* If the machine is registered, then, instead of returning a
776 * failure, we mark it as inaccessible and set the result to
777 * success to give it a try later */
778
779 /* fetch the current error info */
780 mData->mAccessError = com::ErrorInfo();
781 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
782
783 /* rollback all changes */
784 i_rollback(false /* aNotify */);
785
786 // uninit media from this machine's media registry, or else
787 // reloading the settings will fail
788 mParent->i_unregisterMachineMedia(i_getId());
789
790 /* uninitialize the common part to make sure all data is reset to
791 * default (null) values */
792 uninitDataAndChildObjects();
793
794 rc = S_OK;
795 }
796
797 return rc;
798}
799
800/**
801 * Uninitializes the instance.
802 * Called either from FinalRelease() or by the parent when it gets destroyed.
803 *
804 * @note The caller of this method must make sure that this object
805 * a) doesn't have active callers on the current thread and b) is not locked
806 * by the current thread; otherwise uninit() will hang either a) due to
807 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
808 * a dead-lock caused by this thread waiting for all callers on the other
809 * threads are done but preventing them from doing so by holding a lock.
810 */
811void Machine::uninit()
812{
813 LogFlowThisFuncEnter();
814
815 Assert(!isWriteLockOnCurrentThread());
816
817 Assert(!uRegistryNeedsSaving);
818 if (uRegistryNeedsSaving)
819 {
820 AutoCaller autoCaller(this);
821 if (SUCCEEDED(autoCaller.rc()))
822 {
823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
824 i_saveSettings(NULL, Machine::SaveS_Force);
825 }
826 }
827
828 /* Enclose the state transition Ready->InUninit->NotReady */
829 AutoUninitSpan autoUninitSpan(this);
830 if (autoUninitSpan.uninitDone())
831 return;
832
833 Assert(!i_isSnapshotMachine());
834 Assert(!i_isSessionMachine());
835 Assert(!!mData);
836
837 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
838 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
839
840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
841
842 if (!mData->mSession.mMachine.isNull())
843 {
844 /* Theoretically, this can only happen if the VirtualBox server has been
845 * terminated while there were clients running that owned open direct
846 * sessions. Since in this case we are definitely called by
847 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
848 * won't happen on the client watcher thread (because it has a
849 * VirtualBox caller for the duration of the
850 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
851 * cannot happen until the VirtualBox caller is released). This is
852 * important, because SessionMachine::uninit() cannot correctly operate
853 * after we return from this method (it expects the Machine instance is
854 * still valid). We'll call it ourselves below.
855 */
856 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
857 (SessionMachine*)mData->mSession.mMachine));
858
859 if (Global::IsOnlineOrTransient(mData->mMachineState))
860 {
861 Log1WarningThisFunc(("Setting state to Aborted!\n"));
862 /* set machine state using SessionMachine reimplementation */
863 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
864 }
865
866 /*
867 * Uninitialize SessionMachine using public uninit() to indicate
868 * an unexpected uninitialization.
869 */
870 mData->mSession.mMachine->uninit();
871 /* SessionMachine::uninit() must set mSession.mMachine to null */
872 Assert(mData->mSession.mMachine.isNull());
873 }
874
875 // uninit media from this machine's media registry, if they're still there
876 Guid uuidMachine(i_getId());
877
878 /* the lock is no more necessary (SessionMachine is uninitialized) */
879 alock.release();
880
881 /* XXX This will fail with
882 * "cannot be closed because it is still attached to 1 virtual machines"
883 * because at this point we did not call uninitDataAndChildObjects() yet
884 * and therefore also removeBackReference() for all these mediums was not called! */
885
886 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
887 mParent->i_unregisterMachineMedia(uuidMachine);
888
889 // has machine been modified?
890 if (mData->flModifications)
891 {
892 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
893 i_rollback(false /* aNotify */);
894 }
895
896 if (mData->mAccessible)
897 uninitDataAndChildObjects();
898
899 /* free the essential data structure last */
900 mData.free();
901
902 LogFlowThisFuncLeave();
903}
904
905// Wrapped IMachine properties
906/////////////////////////////////////////////////////////////////////////////
907HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
908{
909 /* mParent is constant during life time, no need to lock */
910 ComObjPtr<VirtualBox> pVirtualBox(mParent);
911 aParent = pVirtualBox;
912
913 return S_OK;
914}
915
916
917HRESULT Machine::getAccessible(BOOL *aAccessible)
918{
919 /* In some cases (medium registry related), it is necessary to be able to
920 * go through the list of all machines. Happens when an inaccessible VM
921 * has a sensible medium registry. */
922 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
924
925 HRESULT rc = S_OK;
926
927 if (!mData->mAccessible)
928 {
929 /* try to initialize the VM once more if not accessible */
930
931 AutoReinitSpan autoReinitSpan(this);
932 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
933
934#ifdef DEBUG
935 LogFlowThisFunc(("Dumping media backreferences\n"));
936 mParent->i_dumpAllBackRefs();
937#endif
938
939 if (mData->pMachineConfigFile)
940 {
941 // reset the XML file to force loadSettings() (called from i_registeredInit())
942 // to parse it again; the file might have changed
943 delete mData->pMachineConfigFile;
944 mData->pMachineConfigFile = NULL;
945 }
946
947 rc = i_registeredInit();
948
949 if (SUCCEEDED(rc) && mData->mAccessible)
950 {
951 autoReinitSpan.setSucceeded();
952
953 /* make sure interesting parties will notice the accessibility
954 * state change */
955 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
956 mParent->i_onMachineDataChanged(mData->mUuid);
957 }
958 }
959
960 if (SUCCEEDED(rc))
961 *aAccessible = mData->mAccessible;
962
963 LogFlowThisFuncLeave();
964
965 return rc;
966}
967
968HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
969{
970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
971
972 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
973 {
974 /* return shortly */
975 aAccessError = NULL;
976 return S_OK;
977 }
978
979 HRESULT rc = S_OK;
980
981 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
982 rc = errorInfo.createObject();
983 if (SUCCEEDED(rc))
984 {
985 errorInfo->init(mData->mAccessError.getResultCode(),
986 mData->mAccessError.getInterfaceID().ref(),
987 Utf8Str(mData->mAccessError.getComponent()).c_str(),
988 Utf8Str(mData->mAccessError.getText()));
989 aAccessError = errorInfo;
990 }
991
992 return rc;
993}
994
995HRESULT Machine::getName(com::Utf8Str &aName)
996{
997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
998
999 aName = mUserData->s.strName;
1000
1001 return S_OK;
1002}
1003
1004HRESULT Machine::setName(const com::Utf8Str &aName)
1005{
1006 // prohibit setting a UUID only as the machine name, or else it can
1007 // never be found by findMachine()
1008 Guid test(aName);
1009
1010 if (test.isValid())
1011 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1012
1013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1016 if (FAILED(rc)) return rc;
1017
1018 i_setModified(IsModified_MachineData);
1019 mUserData.backup();
1020 mUserData->s.strName = aName;
1021
1022 return S_OK;
1023}
1024
1025HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1026{
1027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1028
1029 aDescription = mUserData->s.strDescription;
1030
1031 return S_OK;
1032}
1033
1034HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1035{
1036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1037
1038 // this can be done in principle in any state as it doesn't affect the VM
1039 // significantly, but play safe by not messing around while complex
1040 // activities are going on
1041 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1042 if (FAILED(rc)) return rc;
1043
1044 i_setModified(IsModified_MachineData);
1045 mUserData.backup();
1046 mUserData->s.strDescription = aDescription;
1047
1048 return S_OK;
1049}
1050
1051HRESULT Machine::getId(com::Guid &aId)
1052{
1053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1054
1055 aId = mData->mUuid;
1056
1057 return S_OK;
1058}
1059
1060HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1061{
1062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1063 aGroups.resize(mUserData->s.llGroups.size());
1064 size_t i = 0;
1065 for (StringsList::const_iterator
1066 it = mUserData->s.llGroups.begin();
1067 it != mUserData->s.llGroups.end();
1068 ++it, ++i)
1069 aGroups[i] = (*it);
1070
1071 return S_OK;
1072}
1073
1074HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1075{
1076 StringsList llGroups;
1077 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1078 if (FAILED(rc))
1079 return rc;
1080
1081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1082
1083 rc = i_checkStateDependency(MutableOrSavedStateDep);
1084 if (FAILED(rc)) return rc;
1085
1086 i_setModified(IsModified_MachineData);
1087 mUserData.backup();
1088 mUserData->s.llGroups = llGroups;
1089
1090 return S_OK;
1091}
1092
1093HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1094{
1095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1096
1097 aOSTypeId = mUserData->s.strOsType;
1098
1099 return S_OK;
1100}
1101
1102HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1103{
1104 /* look up the object by Id to check it is valid */
1105 ComObjPtr<GuestOSType> pGuestOSType;
1106 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1107
1108 /* when setting, always use the "etalon" value for consistency -- lookup
1109 * by ID is case-insensitive and the input value may have different case */
1110 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1111
1112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 HRESULT rc = i_checkStateDependency(MutableStateDep);
1115 if (FAILED(rc)) return rc;
1116
1117 i_setModified(IsModified_MachineData);
1118 mUserData.backup();
1119 mUserData->s.strOsType = osTypeId;
1120
1121 return S_OK;
1122}
1123
1124HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1125{
1126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 *aFirmwareType = mHWData->mFirmwareType;
1129
1130 return S_OK;
1131}
1132
1133HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1134{
1135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1136
1137 HRESULT rc = i_checkStateDependency(MutableStateDep);
1138 if (FAILED(rc)) return rc;
1139
1140 i_setModified(IsModified_MachineData);
1141 mHWData.backup();
1142 mHWData->mFirmwareType = aFirmwareType;
1143 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1144 alock.release();
1145
1146 mBIOSSettings->i_updateNonVolatileStorageFile(strNVRAM);
1147
1148 return S_OK;
1149}
1150
1151HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1152{
1153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1154
1155 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1156
1157 return S_OK;
1158}
1159
1160HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1161{
1162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 HRESULT rc = i_checkStateDependency(MutableStateDep);
1165 if (FAILED(rc)) return rc;
1166
1167 i_setModified(IsModified_MachineData);
1168 mHWData.backup();
1169 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1170
1171 return S_OK;
1172}
1173
1174HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1175{
1176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 *aPointingHIDType = mHWData->mPointingHIDType;
1179
1180 return S_OK;
1181}
1182
1183HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1184{
1185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 HRESULT rc = i_checkStateDependency(MutableStateDep);
1188 if (FAILED(rc)) return rc;
1189
1190 i_setModified(IsModified_MachineData);
1191 mHWData.backup();
1192 mHWData->mPointingHIDType = aPointingHIDType;
1193
1194 return S_OK;
1195}
1196
1197HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1198{
1199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1200
1201 *aChipsetType = mHWData->mChipsetType;
1202
1203 return S_OK;
1204}
1205
1206HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1207{
1208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1209
1210 HRESULT rc = i_checkStateDependency(MutableStateDep);
1211 if (FAILED(rc)) return rc;
1212
1213 if (aChipsetType != mHWData->mChipsetType)
1214 {
1215 i_setModified(IsModified_MachineData);
1216 mHWData.backup();
1217 mHWData->mChipsetType = aChipsetType;
1218
1219 // Resize network adapter array, to be finalized on commit/rollback.
1220 // We must not throw away entries yet, otherwise settings are lost
1221 // without a way to roll back.
1222 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1223 size_t oldCount = mNetworkAdapters.size();
1224 if (newCount > oldCount)
1225 {
1226 mNetworkAdapters.resize(newCount);
1227 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1228 {
1229 unconst(mNetworkAdapters[slot]).createObject();
1230 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1231 }
1232 }
1233 }
1234
1235 return S_OK;
1236}
1237
1238HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1239{
1240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1241
1242 aParavirtDebug = mHWData->mParavirtDebug;
1243 return S_OK;
1244}
1245
1246HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1247{
1248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 HRESULT rc = i_checkStateDependency(MutableStateDep);
1251 if (FAILED(rc)) return rc;
1252
1253 /** @todo Parse/validate options? */
1254 if (aParavirtDebug != mHWData->mParavirtDebug)
1255 {
1256 i_setModified(IsModified_MachineData);
1257 mHWData.backup();
1258 mHWData->mParavirtDebug = aParavirtDebug;
1259 }
1260
1261 return S_OK;
1262}
1263
1264HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1265{
1266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 *aParavirtProvider = mHWData->mParavirtProvider;
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1274{
1275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 HRESULT rc = i_checkStateDependency(MutableStateDep);
1278 if (FAILED(rc)) return rc;
1279
1280 if (aParavirtProvider != mHWData->mParavirtProvider)
1281 {
1282 i_setModified(IsModified_MachineData);
1283 mHWData.backup();
1284 mHWData->mParavirtProvider = aParavirtProvider;
1285 }
1286
1287 return S_OK;
1288}
1289
1290HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1291{
1292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1293
1294 *aParavirtProvider = mHWData->mParavirtProvider;
1295 switch (mHWData->mParavirtProvider)
1296 {
1297 case ParavirtProvider_None:
1298 case ParavirtProvider_HyperV:
1299 case ParavirtProvider_KVM:
1300 case ParavirtProvider_Minimal:
1301 break;
1302
1303 /* Resolve dynamic provider types to the effective types. */
1304 default:
1305 {
1306 ComObjPtr<GuestOSType> pGuestOSType;
1307 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1308 pGuestOSType);
1309 if (FAILED(hrc2) || pGuestOSType.isNull())
1310 {
1311 *aParavirtProvider = ParavirtProvider_None;
1312 break;
1313 }
1314
1315 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1316 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1317
1318 switch (mHWData->mParavirtProvider)
1319 {
1320 case ParavirtProvider_Legacy:
1321 {
1322 if (fOsXGuest)
1323 *aParavirtProvider = ParavirtProvider_Minimal;
1324 else
1325 *aParavirtProvider = ParavirtProvider_None;
1326 break;
1327 }
1328
1329 case ParavirtProvider_Default:
1330 {
1331 if (fOsXGuest)
1332 *aParavirtProvider = ParavirtProvider_Minimal;
1333 else if ( mUserData->s.strOsType == "Windows10"
1334 || mUserData->s.strOsType == "Windows10_64"
1335 || mUserData->s.strOsType == "Windows81"
1336 || mUserData->s.strOsType == "Windows81_64"
1337 || mUserData->s.strOsType == "Windows8"
1338 || mUserData->s.strOsType == "Windows8_64"
1339 || mUserData->s.strOsType == "Windows7"
1340 || mUserData->s.strOsType == "Windows7_64"
1341 || mUserData->s.strOsType == "WindowsVista"
1342 || mUserData->s.strOsType == "WindowsVista_64"
1343 || mUserData->s.strOsType == "Windows2012"
1344 || mUserData->s.strOsType == "Windows2012_64"
1345 || mUserData->s.strOsType == "Windows2008"
1346 || mUserData->s.strOsType == "Windows2008_64")
1347 {
1348 *aParavirtProvider = ParavirtProvider_HyperV;
1349 }
1350 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1351 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1352 || mUserData->s.strOsType == "Linux"
1353 || mUserData->s.strOsType == "Linux_64"
1354 || mUserData->s.strOsType == "ArchLinux"
1355 || mUserData->s.strOsType == "ArchLinux_64"
1356 || mUserData->s.strOsType == "Debian"
1357 || mUserData->s.strOsType == "Debian_64"
1358 || mUserData->s.strOsType == "Fedora"
1359 || mUserData->s.strOsType == "Fedora_64"
1360 || mUserData->s.strOsType == "Gentoo"
1361 || mUserData->s.strOsType == "Gentoo_64"
1362 || mUserData->s.strOsType == "Mandriva"
1363 || mUserData->s.strOsType == "Mandriva_64"
1364 || mUserData->s.strOsType == "OpenSUSE"
1365 || mUserData->s.strOsType == "OpenSUSE_64"
1366 || mUserData->s.strOsType == "Oracle"
1367 || mUserData->s.strOsType == "Oracle_64"
1368 || mUserData->s.strOsType == "RedHat"
1369 || mUserData->s.strOsType == "RedHat_64"
1370 || mUserData->s.strOsType == "Turbolinux"
1371 || mUserData->s.strOsType == "Turbolinux_64"
1372 || mUserData->s.strOsType == "Ubuntu"
1373 || mUserData->s.strOsType == "Ubuntu_64"
1374 || mUserData->s.strOsType == "Xandros"
1375 || mUserData->s.strOsType == "Xandros_64")
1376 {
1377 *aParavirtProvider = ParavirtProvider_KVM;
1378 }
1379 else
1380 *aParavirtProvider = ParavirtProvider_None;
1381 break;
1382 }
1383
1384 default: AssertFailedBreak(); /* Shut up MSC. */
1385 }
1386 break;
1387 }
1388 }
1389
1390 Assert( *aParavirtProvider == ParavirtProvider_None
1391 || *aParavirtProvider == ParavirtProvider_Minimal
1392 || *aParavirtProvider == ParavirtProvider_HyperV
1393 || *aParavirtProvider == ParavirtProvider_KVM);
1394 return S_OK;
1395}
1396
1397HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1398{
1399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1400
1401 aHardwareVersion = mHWData->mHWVersion;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1407{
1408 /* check known version */
1409 Utf8Str hwVersion = aHardwareVersion;
1410 if ( hwVersion.compare("1") != 0
1411 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1412 return setError(E_INVALIDARG,
1413 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1414
1415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1416
1417 HRESULT rc = i_checkStateDependency(MutableStateDep);
1418 if (FAILED(rc)) return rc;
1419
1420 i_setModified(IsModified_MachineData);
1421 mHWData.backup();
1422 mHWData->mHWVersion = aHardwareVersion;
1423
1424 return S_OK;
1425}
1426
1427HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1428{
1429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1430
1431 if (!mHWData->mHardwareUUID.isZero())
1432 aHardwareUUID = mHWData->mHardwareUUID;
1433 else
1434 aHardwareUUID = mData->mUuid;
1435
1436 return S_OK;
1437}
1438
1439HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1440{
1441 if (!aHardwareUUID.isValid())
1442 return E_INVALIDARG;
1443
1444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 HRESULT rc = i_checkStateDependency(MutableStateDep);
1447 if (FAILED(rc)) return rc;
1448
1449 i_setModified(IsModified_MachineData);
1450 mHWData.backup();
1451 if (aHardwareUUID == mData->mUuid)
1452 mHWData->mHardwareUUID.clear();
1453 else
1454 mHWData->mHardwareUUID = aHardwareUUID;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1460{
1461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 *aMemorySize = mHWData->mMemorySize;
1464
1465 return S_OK;
1466}
1467
1468HRESULT Machine::setMemorySize(ULONG aMemorySize)
1469{
1470 /* check RAM limits */
1471 if ( aMemorySize < MM_RAM_MIN_IN_MB
1472 || aMemorySize > MM_RAM_MAX_IN_MB
1473 )
1474 return setError(E_INVALIDARG,
1475 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1476 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1477
1478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 HRESULT rc = i_checkStateDependency(MutableStateDep);
1481 if (FAILED(rc)) return rc;
1482
1483 i_setModified(IsModified_MachineData);
1484 mHWData.backup();
1485 mHWData->mMemorySize = aMemorySize;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1491{
1492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 *aCPUCount = mHWData->mCPUCount;
1495
1496 return S_OK;
1497}
1498
1499HRESULT Machine::setCPUCount(ULONG aCPUCount)
1500{
1501 /* check CPU limits */
1502 if ( aCPUCount < SchemaDefs::MinCPUCount
1503 || aCPUCount > SchemaDefs::MaxCPUCount
1504 )
1505 return setError(E_INVALIDARG,
1506 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1507 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1508
1509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1510
1511 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1512 if (mHWData->mCPUHotPlugEnabled)
1513 {
1514 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1515 {
1516 if (mHWData->mCPUAttached[idx])
1517 return setError(E_INVALIDARG,
1518 tr("There is still a CPU attached to socket %lu."
1519 "Detach the CPU before removing the socket"),
1520 aCPUCount, idx+1);
1521 }
1522 }
1523
1524 HRESULT rc = i_checkStateDependency(MutableStateDep);
1525 if (FAILED(rc)) return rc;
1526
1527 i_setModified(IsModified_MachineData);
1528 mHWData.backup();
1529 mHWData->mCPUCount = aCPUCount;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1535{
1536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1539
1540 return S_OK;
1541}
1542
1543HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1544{
1545 HRESULT rc = S_OK;
1546
1547 /* check throttle limits */
1548 if ( aCPUExecutionCap < 1
1549 || aCPUExecutionCap > 100
1550 )
1551 return setError(E_INVALIDARG,
1552 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1553 aCPUExecutionCap, 1, 100);
1554
1555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1556
1557 alock.release();
1558 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1559 alock.acquire();
1560 if (FAILED(rc)) return rc;
1561
1562 i_setModified(IsModified_MachineData);
1563 mHWData.backup();
1564 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1565
1566 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1567 if (Global::IsOnline(mData->mMachineState))
1568 i_saveSettings(NULL);
1569
1570 return S_OK;
1571}
1572
1573HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1574{
1575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1576
1577 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1578
1579 return S_OK;
1580}
1581
1582HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1583{
1584 HRESULT rc = S_OK;
1585
1586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 rc = i_checkStateDependency(MutableStateDep);
1589 if (FAILED(rc)) return rc;
1590
1591 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1592 {
1593 if (aCPUHotPlugEnabled)
1594 {
1595 i_setModified(IsModified_MachineData);
1596 mHWData.backup();
1597
1598 /* Add the amount of CPUs currently attached */
1599 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1600 mHWData->mCPUAttached[i] = true;
1601 }
1602 else
1603 {
1604 /*
1605 * We can disable hotplug only if the amount of maximum CPUs is equal
1606 * to the amount of attached CPUs
1607 */
1608 unsigned cCpusAttached = 0;
1609 unsigned iHighestId = 0;
1610
1611 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1612 {
1613 if (mHWData->mCPUAttached[i])
1614 {
1615 cCpusAttached++;
1616 iHighestId = i;
1617 }
1618 }
1619
1620 if ( (cCpusAttached != mHWData->mCPUCount)
1621 || (iHighestId >= mHWData->mCPUCount))
1622 return setError(E_INVALIDARG,
1623 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1624
1625 i_setModified(IsModified_MachineData);
1626 mHWData.backup();
1627 }
1628 }
1629
1630 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1631
1632 return rc;
1633}
1634
1635HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1636{
1637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1638
1639 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1640
1641 return S_OK;
1642}
1643
1644HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1645{
1646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1647
1648 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1649 if (SUCCEEDED(hrc))
1650 {
1651 i_setModified(IsModified_MachineData);
1652 mHWData.backup();
1653 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1654 }
1655 return hrc;
1656}
1657
1658HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1659{
1660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1661 aCPUProfile = mHWData->mCpuProfile;
1662 return S_OK;
1663}
1664
1665HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1666{
1667 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1668 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1669 if (SUCCEEDED(hrc))
1670 {
1671 i_setModified(IsModified_MachineData);
1672 mHWData.backup();
1673 /* Empty equals 'host'. */
1674 if (aCPUProfile.isNotEmpty())
1675 mHWData->mCpuProfile = aCPUProfile;
1676 else
1677 mHWData->mCpuProfile = "host";
1678 }
1679 return hrc;
1680}
1681
1682HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1683{
1684#ifdef VBOX_WITH_USB_CARDREADER
1685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1688
1689 return S_OK;
1690#else
1691 NOREF(aEmulatedUSBCardReaderEnabled);
1692 return E_NOTIMPL;
1693#endif
1694}
1695
1696HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1697{
1698#ifdef VBOX_WITH_USB_CARDREADER
1699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1702 if (FAILED(rc)) return rc;
1703
1704 i_setModified(IsModified_MachineData);
1705 mHWData.backup();
1706 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1707
1708 return S_OK;
1709#else
1710 NOREF(aEmulatedUSBCardReaderEnabled);
1711 return E_NOTIMPL;
1712#endif
1713}
1714
1715HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1716{
1717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1718
1719 *aHPETEnabled = mHWData->mHPETEnabled;
1720
1721 return S_OK;
1722}
1723
1724HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1725{
1726 HRESULT rc = S_OK;
1727
1728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1729
1730 rc = i_checkStateDependency(MutableStateDep);
1731 if (FAILED(rc)) return rc;
1732
1733 i_setModified(IsModified_MachineData);
1734 mHWData.backup();
1735
1736 mHWData->mHPETEnabled = aHPETEnabled;
1737
1738 return rc;
1739}
1740
1741/** @todo this method should not be public */
1742HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1743{
1744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1747
1748 return S_OK;
1749}
1750
1751/**
1752 * Set the memory balloon size.
1753 *
1754 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1755 * we have to make sure that we never call IGuest from here.
1756 */
1757HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1758{
1759 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1760#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1761 /* check limits */
1762 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1763 return setError(E_INVALIDARG,
1764 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1765 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1766
1767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1768
1769 i_setModified(IsModified_MachineData);
1770 mHWData.backup();
1771 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1772
1773 return S_OK;
1774#else
1775 NOREF(aMemoryBalloonSize);
1776 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1777#endif
1778}
1779
1780HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1781{
1782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1785 return S_OK;
1786}
1787
1788HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1789{
1790#ifdef VBOX_WITH_PAGE_SHARING
1791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1792
1793 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1794 i_setModified(IsModified_MachineData);
1795 mHWData.backup();
1796 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1797 return S_OK;
1798#else
1799 NOREF(aPageFusionEnabled);
1800 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1801#endif
1802}
1803
1804HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1805{
1806 /* mBIOSSettings is constant during life time, no need to lock */
1807 aBIOSSettings = mBIOSSettings;
1808
1809 return S_OK;
1810}
1811
1812HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1813{
1814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1815
1816 aRecordingSettings = mRecordingSettings;
1817
1818 return S_OK;
1819}
1820
1821HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1822{
1823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1824
1825 aGraphicsAdapter = mGraphicsAdapter;
1826
1827 return S_OK;
1828}
1829
1830HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1831{
1832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1833
1834 switch (aProperty)
1835 {
1836 case CPUPropertyType_PAE:
1837 *aValue = mHWData->mPAEEnabled;
1838 break;
1839
1840 case CPUPropertyType_LongMode:
1841 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1842 *aValue = TRUE;
1843 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1844 *aValue = FALSE;
1845#if HC_ARCH_BITS == 64
1846 else
1847 *aValue = TRUE;
1848#else
1849 else
1850 {
1851 *aValue = FALSE;
1852
1853 ComObjPtr<GuestOSType> pGuestOSType;
1854 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1855 pGuestOSType);
1856 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1857 {
1858 if (pGuestOSType->i_is64Bit())
1859 {
1860 ComObjPtr<Host> pHost = mParent->i_host();
1861 alock.release();
1862
1863 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1864 if (FAILED(hrc2))
1865 *aValue = FALSE;
1866 }
1867 }
1868 }
1869#endif
1870 break;
1871
1872 case CPUPropertyType_TripleFaultReset:
1873 *aValue = mHWData->mTripleFaultReset;
1874 break;
1875
1876 case CPUPropertyType_APIC:
1877 *aValue = mHWData->mAPIC;
1878 break;
1879
1880 case CPUPropertyType_X2APIC:
1881 *aValue = mHWData->mX2APIC;
1882 break;
1883
1884 case CPUPropertyType_IBPBOnVMExit:
1885 *aValue = mHWData->mIBPBOnVMExit;
1886 break;
1887
1888 case CPUPropertyType_IBPBOnVMEntry:
1889 *aValue = mHWData->mIBPBOnVMEntry;
1890 break;
1891
1892 case CPUPropertyType_SpecCtrl:
1893 *aValue = mHWData->mSpecCtrl;
1894 break;
1895
1896 case CPUPropertyType_SpecCtrlByHost:
1897 *aValue = mHWData->mSpecCtrlByHost;
1898 break;
1899
1900 case CPUPropertyType_HWVirt:
1901 *aValue = mHWData->mNestedHWVirt;
1902 break;
1903
1904 case CPUPropertyType_L1DFlushOnEMTScheduling:
1905 *aValue = mHWData->mL1DFlushOnSched;
1906 break;
1907
1908 case CPUPropertyType_L1DFlushOnVMEntry:
1909 *aValue = mHWData->mL1DFlushOnVMEntry;
1910 break;
1911
1912 case CPUPropertyType_MDSClearOnEMTScheduling:
1913 *aValue = mHWData->mMDSClearOnSched;
1914 break;
1915
1916 case CPUPropertyType_MDSClearOnVMEntry:
1917 *aValue = mHWData->mMDSClearOnVMEntry;
1918 break;
1919
1920 default:
1921 return E_INVALIDARG;
1922 }
1923 return S_OK;
1924}
1925
1926HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1927{
1928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1929
1930 HRESULT rc = i_checkStateDependency(MutableStateDep);
1931 if (FAILED(rc)) return rc;
1932
1933 switch (aProperty)
1934 {
1935 case CPUPropertyType_PAE:
1936 i_setModified(IsModified_MachineData);
1937 mHWData.backup();
1938 mHWData->mPAEEnabled = !!aValue;
1939 break;
1940
1941 case CPUPropertyType_LongMode:
1942 i_setModified(IsModified_MachineData);
1943 mHWData.backup();
1944 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1945 break;
1946
1947 case CPUPropertyType_TripleFaultReset:
1948 i_setModified(IsModified_MachineData);
1949 mHWData.backup();
1950 mHWData->mTripleFaultReset = !!aValue;
1951 break;
1952
1953 case CPUPropertyType_APIC:
1954 if (mHWData->mX2APIC)
1955 aValue = TRUE;
1956 i_setModified(IsModified_MachineData);
1957 mHWData.backup();
1958 mHWData->mAPIC = !!aValue;
1959 break;
1960
1961 case CPUPropertyType_X2APIC:
1962 i_setModified(IsModified_MachineData);
1963 mHWData.backup();
1964 mHWData->mX2APIC = !!aValue;
1965 if (aValue)
1966 mHWData->mAPIC = !!aValue;
1967 break;
1968
1969 case CPUPropertyType_IBPBOnVMExit:
1970 i_setModified(IsModified_MachineData);
1971 mHWData.backup();
1972 mHWData->mIBPBOnVMExit = !!aValue;
1973 break;
1974
1975 case CPUPropertyType_IBPBOnVMEntry:
1976 i_setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mIBPBOnVMEntry = !!aValue;
1979 break;
1980
1981 case CPUPropertyType_SpecCtrl:
1982 i_setModified(IsModified_MachineData);
1983 mHWData.backup();
1984 mHWData->mSpecCtrl = !!aValue;
1985 break;
1986
1987 case CPUPropertyType_SpecCtrlByHost:
1988 i_setModified(IsModified_MachineData);
1989 mHWData.backup();
1990 mHWData->mSpecCtrlByHost = !!aValue;
1991 break;
1992
1993 case CPUPropertyType_HWVirt:
1994 i_setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mNestedHWVirt = !!aValue;
1997 break;
1998
1999 case CPUPropertyType_L1DFlushOnEMTScheduling:
2000 i_setModified(IsModified_MachineData);
2001 mHWData.backup();
2002 mHWData->mL1DFlushOnSched = !!aValue;
2003 break;
2004
2005 case CPUPropertyType_L1DFlushOnVMEntry:
2006 i_setModified(IsModified_MachineData);
2007 mHWData.backup();
2008 mHWData->mL1DFlushOnVMEntry = !!aValue;
2009 break;
2010
2011 case CPUPropertyType_MDSClearOnEMTScheduling:
2012 i_setModified(IsModified_MachineData);
2013 mHWData.backup();
2014 mHWData->mMDSClearOnSched = !!aValue;
2015 break;
2016
2017 case CPUPropertyType_MDSClearOnVMEntry:
2018 i_setModified(IsModified_MachineData);
2019 mHWData.backup();
2020 mHWData->mMDSClearOnVMEntry = !!aValue;
2021 break;
2022
2023 default:
2024 return E_INVALIDARG;
2025 }
2026 return S_OK;
2027}
2028
2029HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2030 ULONG *aValEcx, ULONG *aValEdx)
2031{
2032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2033 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2034 {
2035 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2036 it != mHWData->mCpuIdLeafList.end();
2037 ++it)
2038 {
2039 if (aOrdinal == 0)
2040 {
2041 const settings::CpuIdLeaf &rLeaf= *it;
2042 *aIdx = rLeaf.idx;
2043 *aSubIdx = rLeaf.idxSub;
2044 *aValEax = rLeaf.uEax;
2045 *aValEbx = rLeaf.uEbx;
2046 *aValEcx = rLeaf.uEcx;
2047 *aValEdx = rLeaf.uEdx;
2048 return S_OK;
2049 }
2050 aOrdinal--;
2051 }
2052 }
2053 return E_INVALIDARG;
2054}
2055
2056HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2057{
2058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2059
2060 /*
2061 * Search the list.
2062 */
2063 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2064 {
2065 const settings::CpuIdLeaf &rLeaf= *it;
2066 if ( rLeaf.idx == aIdx
2067 && ( aSubIdx == UINT32_MAX
2068 || rLeaf.idxSub == aSubIdx) )
2069 {
2070 *aValEax = rLeaf.uEax;
2071 *aValEbx = rLeaf.uEbx;
2072 *aValEcx = rLeaf.uEcx;
2073 *aValEdx = rLeaf.uEdx;
2074 return S_OK;
2075 }
2076 }
2077
2078 return E_INVALIDARG;
2079}
2080
2081
2082HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2083{
2084 /*
2085 * Validate input before taking locks and checking state.
2086 */
2087 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2088 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2089 if ( aIdx >= UINT32_C(0x20)
2090 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2091 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2092 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2093
2094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2095 HRESULT rc = i_checkStateDependency(MutableStateDep);
2096 if (FAILED(rc)) return rc;
2097
2098 /*
2099 * Impose a maximum number of leaves.
2100 */
2101 if (mHWData->mCpuIdLeafList.size() > 256)
2102 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2103
2104 /*
2105 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2106 */
2107 i_setModified(IsModified_MachineData);
2108 mHWData.backup();
2109
2110 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2111 {
2112 settings::CpuIdLeaf &rLeaf= *it;
2113 if ( rLeaf.idx == aIdx
2114 && ( aSubIdx == UINT32_MAX
2115 || rLeaf.idxSub == aSubIdx) )
2116 it = mHWData->mCpuIdLeafList.erase(it);
2117 else
2118 ++it;
2119 }
2120
2121 settings::CpuIdLeaf NewLeaf;
2122 NewLeaf.idx = aIdx;
2123 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2124 NewLeaf.uEax = aValEax;
2125 NewLeaf.uEbx = aValEbx;
2126 NewLeaf.uEcx = aValEcx;
2127 NewLeaf.uEdx = aValEdx;
2128 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2129 return S_OK;
2130}
2131
2132HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2133{
2134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 HRESULT rc = i_checkStateDependency(MutableStateDep);
2137 if (FAILED(rc)) return rc;
2138
2139 /*
2140 * Do the removal.
2141 */
2142 bool fModified = mHWData.isBackedUp();
2143 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2144 {
2145 settings::CpuIdLeaf &rLeaf= *it;
2146 if ( rLeaf.idx == aIdx
2147 && ( aSubIdx == UINT32_MAX
2148 || rLeaf.idxSub == aSubIdx) )
2149 {
2150 if (!fModified)
2151 {
2152 fModified = true;
2153 i_setModified(IsModified_MachineData);
2154 mHWData.backup();
2155 // Start from the beginning, since mHWData.backup() creates
2156 // a new list, causing iterator mixup. This makes sure that
2157 // the settings are not unnecessarily marked as modified,
2158 // at the price of extra list walking.
2159 it = mHWData->mCpuIdLeafList.begin();
2160 }
2161 else
2162 it = mHWData->mCpuIdLeafList.erase(it);
2163 }
2164 else
2165 ++it;
2166 }
2167
2168 return S_OK;
2169}
2170
2171HRESULT Machine::removeAllCPUIDLeaves()
2172{
2173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2174
2175 HRESULT rc = i_checkStateDependency(MutableStateDep);
2176 if (FAILED(rc)) return rc;
2177
2178 if (mHWData->mCpuIdLeafList.size() > 0)
2179 {
2180 i_setModified(IsModified_MachineData);
2181 mHWData.backup();
2182
2183 mHWData->mCpuIdLeafList.clear();
2184 }
2185
2186 return S_OK;
2187}
2188HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2189{
2190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2191
2192 switch(aProperty)
2193 {
2194 case HWVirtExPropertyType_Enabled:
2195 *aValue = mHWData->mHWVirtExEnabled;
2196 break;
2197
2198 case HWVirtExPropertyType_VPID:
2199 *aValue = mHWData->mHWVirtExVPIDEnabled;
2200 break;
2201
2202 case HWVirtExPropertyType_NestedPaging:
2203 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2204 break;
2205
2206 case HWVirtExPropertyType_UnrestrictedExecution:
2207 *aValue = mHWData->mHWVirtExUXEnabled;
2208 break;
2209
2210 case HWVirtExPropertyType_LargePages:
2211 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2212#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2213 *aValue = FALSE;
2214#endif
2215 break;
2216
2217 case HWVirtExPropertyType_Force:
2218 *aValue = mHWData->mHWVirtExForceEnabled;
2219 break;
2220
2221 case HWVirtExPropertyType_UseNativeApi:
2222 *aValue = mHWData->mHWVirtExUseNativeApi;
2223 break;
2224
2225 case HWVirtExPropertyType_VirtVmsaveVmload:
2226 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2227 break;
2228
2229 default:
2230 return E_INVALIDARG;
2231 }
2232 return S_OK;
2233}
2234
2235HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2236{
2237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2238
2239 HRESULT rc = i_checkStateDependency(MutableStateDep);
2240 if (FAILED(rc)) return rc;
2241
2242 switch (aProperty)
2243 {
2244 case HWVirtExPropertyType_Enabled:
2245 i_setModified(IsModified_MachineData);
2246 mHWData.backup();
2247 mHWData->mHWVirtExEnabled = !!aValue;
2248 break;
2249
2250 case HWVirtExPropertyType_VPID:
2251 i_setModified(IsModified_MachineData);
2252 mHWData.backup();
2253 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2254 break;
2255
2256 case HWVirtExPropertyType_NestedPaging:
2257 i_setModified(IsModified_MachineData);
2258 mHWData.backup();
2259 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2260 break;
2261
2262 case HWVirtExPropertyType_UnrestrictedExecution:
2263 i_setModified(IsModified_MachineData);
2264 mHWData.backup();
2265 mHWData->mHWVirtExUXEnabled = !!aValue;
2266 break;
2267
2268 case HWVirtExPropertyType_LargePages:
2269 i_setModified(IsModified_MachineData);
2270 mHWData.backup();
2271 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2272 break;
2273
2274 case HWVirtExPropertyType_Force:
2275 i_setModified(IsModified_MachineData);
2276 mHWData.backup();
2277 mHWData->mHWVirtExForceEnabled = !!aValue;
2278 break;
2279
2280 case HWVirtExPropertyType_UseNativeApi:
2281 i_setModified(IsModified_MachineData);
2282 mHWData.backup();
2283 mHWData->mHWVirtExUseNativeApi = !!aValue;
2284 break;
2285
2286 case HWVirtExPropertyType_VirtVmsaveVmload:
2287 i_setModified(IsModified_MachineData);
2288 mHWData.backup();
2289 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2290 break;
2291
2292 default:
2293 return E_INVALIDARG;
2294 }
2295
2296 return S_OK;
2297}
2298
2299HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2300{
2301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2302
2303 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2304
2305 return S_OK;
2306}
2307
2308HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2309{
2310 /** @todo (r=dmik):
2311 * 1. Allow to change the name of the snapshot folder containing snapshots
2312 * 2. Rename the folder on disk instead of just changing the property
2313 * value (to be smart and not to leave garbage). Note that it cannot be
2314 * done here because the change may be rolled back. Thus, the right
2315 * place is #saveSettings().
2316 */
2317
2318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2319
2320 HRESULT rc = i_checkStateDependency(MutableStateDep);
2321 if (FAILED(rc)) return rc;
2322
2323 if (!mData->mCurrentSnapshot.isNull())
2324 return setError(E_FAIL,
2325 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2326
2327 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2328
2329 if (strSnapshotFolder.isEmpty())
2330 strSnapshotFolder = "Snapshots";
2331 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2332 if (RT_FAILURE(vrc))
2333 return setErrorBoth(E_FAIL, vrc,
2334 tr("Invalid snapshot folder '%s' (%Rrc)"),
2335 strSnapshotFolder.c_str(), vrc);
2336
2337 i_setModified(IsModified_MachineData);
2338 mUserData.backup();
2339
2340 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2341
2342 return S_OK;
2343}
2344
2345HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2346{
2347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2348
2349 aMediumAttachments.resize(mMediumAttachments->size());
2350 size_t i = 0;
2351 for (MediumAttachmentList::const_iterator
2352 it = mMediumAttachments->begin();
2353 it != mMediumAttachments->end();
2354 ++it, ++i)
2355 aMediumAttachments[i] = *it;
2356
2357 return S_OK;
2358}
2359
2360HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2361{
2362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 Assert(!!mVRDEServer);
2365
2366 aVRDEServer = mVRDEServer;
2367
2368 return S_OK;
2369}
2370
2371HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2372{
2373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2374
2375 aAudioAdapter = mAudioAdapter;
2376
2377 return S_OK;
2378}
2379
2380HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2381{
2382#ifdef VBOX_WITH_VUSB
2383 clearError();
2384 MultiResult rc(S_OK);
2385
2386# ifdef VBOX_WITH_USB
2387 rc = mParent->i_host()->i_checkUSBProxyService();
2388 if (FAILED(rc)) return rc;
2389# endif
2390
2391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2392
2393 aUSBControllers.resize(mUSBControllers->size());
2394 size_t i = 0;
2395 for (USBControllerList::const_iterator
2396 it = mUSBControllers->begin();
2397 it != mUSBControllers->end();
2398 ++it, ++i)
2399 aUSBControllers[i] = *it;
2400
2401 return S_OK;
2402#else
2403 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2404 * extended error info to indicate that USB is simply not available
2405 * (w/o treating it as a failure), for example, as in OSE */
2406 NOREF(aUSBControllers);
2407 ReturnComNotImplemented();
2408#endif /* VBOX_WITH_VUSB */
2409}
2410
2411HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2412{
2413#ifdef VBOX_WITH_VUSB
2414 clearError();
2415 MultiResult rc(S_OK);
2416
2417# ifdef VBOX_WITH_USB
2418 rc = mParent->i_host()->i_checkUSBProxyService();
2419 if (FAILED(rc)) return rc;
2420# endif
2421
2422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2423
2424 aUSBDeviceFilters = mUSBDeviceFilters;
2425 return rc;
2426#else
2427 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2428 * extended error info to indicate that USB is simply not available
2429 * (w/o treating it as a failure), for example, as in OSE */
2430 NOREF(aUSBDeviceFilters);
2431 ReturnComNotImplemented();
2432#endif /* VBOX_WITH_VUSB */
2433}
2434
2435HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2436{
2437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2438
2439 aSettingsFilePath = mData->m_strConfigFileFull;
2440
2441 return S_OK;
2442}
2443
2444HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2445{
2446 RT_NOREF(aSettingsFilePath);
2447 ReturnComNotImplemented();
2448}
2449
2450HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2451{
2452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2455 if (FAILED(rc)) return rc;
2456
2457 if (!mData->pMachineConfigFile->fileExists())
2458 // this is a new machine, and no config file exists yet:
2459 *aSettingsModified = TRUE;
2460 else
2461 *aSettingsModified = (mData->flModifications != 0);
2462
2463 return S_OK;
2464}
2465
2466HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2467{
2468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2469
2470 *aSessionState = mData->mSession.mState;
2471
2472 return S_OK;
2473}
2474
2475HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2476{
2477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 aSessionName = mData->mSession.mName;
2480
2481 return S_OK;
2482}
2483
2484HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2485{
2486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2487
2488 *aSessionPID = mData->mSession.mPID;
2489
2490 return S_OK;
2491}
2492
2493HRESULT Machine::getState(MachineState_T *aState)
2494{
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 *aState = mData->mMachineState;
2498 Assert(mData->mMachineState != MachineState_Null);
2499
2500 return S_OK;
2501}
2502
2503HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2504{
2505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2506
2507 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2508
2509 return S_OK;
2510}
2511
2512HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2513{
2514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2515
2516 aStateFilePath = mSSData->strStateFilePath;
2517
2518 return S_OK;
2519}
2520
2521HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2522{
2523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 i_getLogFolder(aLogFolder);
2526
2527 return S_OK;
2528}
2529
2530HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2531{
2532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 aCurrentSnapshot = mData->mCurrentSnapshot;
2535
2536 return S_OK;
2537}
2538
2539HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2540{
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2544 ? 0
2545 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2546
2547 return S_OK;
2548}
2549
2550HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2551{
2552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2553
2554 /* Note: for machines with no snapshots, we always return FALSE
2555 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2556 * reasons :) */
2557
2558 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2559 ? FALSE
2560 : mData->mCurrentStateModified;
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 aSharedFolders.resize(mHWData->mSharedFolders.size());
2570 size_t i = 0;
2571 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2572 it = mHWData->mSharedFolders.begin();
2573 it != mHWData->mSharedFolders.end();
2574 ++it, ++i)
2575 aSharedFolders[i] = *it;
2576
2577 return S_OK;
2578}
2579
2580HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2581{
2582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2583
2584 *aClipboardMode = mHWData->mClipboardMode;
2585
2586 return S_OK;
2587}
2588
2589HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2590{
2591 HRESULT rc = S_OK;
2592
2593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2594
2595 alock.release();
2596 rc = i_onClipboardModeChange(aClipboardMode);
2597 alock.acquire();
2598 if (FAILED(rc)) return rc;
2599
2600 i_setModified(IsModified_MachineData);
2601 mHWData.backup();
2602 mHWData->mClipboardMode = aClipboardMode;
2603
2604 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2605 if (Global::IsOnline(mData->mMachineState))
2606 i_saveSettings(NULL);
2607
2608 return S_OK;
2609}
2610
2611HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2612{
2613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2616
2617 return S_OK;
2618}
2619
2620HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2621{
2622 HRESULT rc = S_OK;
2623
2624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2625
2626 alock.release();
2627 rc = i_onClipboardFileTransferModeChange(aEnabled);
2628 alock.acquire();
2629 if (FAILED(rc)) return rc;
2630
2631 i_setModified(IsModified_MachineData);
2632 mHWData.backup();
2633 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2634
2635 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2636 if (Global::IsOnline(mData->mMachineState))
2637 i_saveSettings(NULL);
2638
2639 return S_OK;
2640}
2641
2642HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2643{
2644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 *aDnDMode = mHWData->mDnDMode;
2647
2648 return S_OK;
2649}
2650
2651HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2652{
2653 HRESULT rc = S_OK;
2654
2655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2656
2657 alock.release();
2658 rc = i_onDnDModeChange(aDnDMode);
2659
2660 alock.acquire();
2661 if (FAILED(rc)) return rc;
2662
2663 i_setModified(IsModified_MachineData);
2664 mHWData.backup();
2665 mHWData->mDnDMode = aDnDMode;
2666
2667 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2668 if (Global::IsOnline(mData->mMachineState))
2669 i_saveSettings(NULL);
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2675{
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 aStorageControllers.resize(mStorageControllers->size());
2679 size_t i = 0;
2680 for (StorageControllerList::const_iterator
2681 it = mStorageControllers->begin();
2682 it != mStorageControllers->end();
2683 ++it, ++i)
2684 aStorageControllers[i] = *it;
2685
2686 return S_OK;
2687}
2688
2689HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2690{
2691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2692
2693 *aEnabled = mUserData->s.fTeleporterEnabled;
2694
2695 return S_OK;
2696}
2697
2698HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2699{
2700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2701
2702 /* Only allow it to be set to true when PoweredOff or Aborted.
2703 (Clearing it is always permitted.) */
2704 if ( aTeleporterEnabled
2705 && mData->mRegistered
2706 && ( !i_isSessionMachine()
2707 || ( mData->mMachineState != MachineState_PoweredOff
2708 && mData->mMachineState != MachineState_Teleported
2709 && mData->mMachineState != MachineState_Aborted
2710 )
2711 )
2712 )
2713 return setError(VBOX_E_INVALID_VM_STATE,
2714 tr("The machine is not powered off (state is %s)"),
2715 Global::stringifyMachineState(mData->mMachineState));
2716
2717 i_setModified(IsModified_MachineData);
2718 mUserData.backup();
2719 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2720
2721 return S_OK;
2722}
2723
2724HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2725{
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2729
2730 return S_OK;
2731}
2732
2733HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2734{
2735 if (aTeleporterPort >= _64K)
2736 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2737
2738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2741 if (FAILED(rc)) return rc;
2742
2743 i_setModified(IsModified_MachineData);
2744 mUserData.backup();
2745 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2746
2747 return S_OK;
2748}
2749
2750HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2751{
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2760{
2761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2764 if (FAILED(rc)) return rc;
2765
2766 i_setModified(IsModified_MachineData);
2767 mUserData.backup();
2768 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2769
2770 return S_OK;
2771}
2772
2773HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2774{
2775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2776 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2777
2778 return S_OK;
2779}
2780
2781HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2782{
2783 /*
2784 * Hash the password first.
2785 */
2786 com::Utf8Str aT = aTeleporterPassword;
2787
2788 if (!aT.isEmpty())
2789 {
2790 if (VBoxIsPasswordHashed(&aT))
2791 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2792 VBoxHashPassword(&aT);
2793 }
2794
2795 /*
2796 * Do the update.
2797 */
2798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2799 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2800 if (SUCCEEDED(hrc))
2801 {
2802 i_setModified(IsModified_MachineData);
2803 mUserData.backup();
2804 mUserData->s.strTeleporterPassword = aT;
2805 }
2806
2807 return hrc;
2808}
2809
2810HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2811{
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2815
2816 return S_OK;
2817}
2818
2819HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2820{
2821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2822
2823 /* Only allow it to be set to true when PoweredOff or Aborted.
2824 (Clearing it is always permitted.) */
2825 if ( aRTCUseUTC
2826 && mData->mRegistered
2827 && ( !i_isSessionMachine()
2828 || ( mData->mMachineState != MachineState_PoweredOff
2829 && mData->mMachineState != MachineState_Teleported
2830 && mData->mMachineState != MachineState_Aborted
2831 )
2832 )
2833 )
2834 return setError(VBOX_E_INVALID_VM_STATE,
2835 tr("The machine is not powered off (state is %s)"),
2836 Global::stringifyMachineState(mData->mMachineState));
2837
2838 i_setModified(IsModified_MachineData);
2839 mUserData.backup();
2840 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2841
2842 return S_OK;
2843}
2844
2845HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2846{
2847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2855{
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 HRESULT rc = i_checkStateDependency(MutableStateDep);
2859 if (FAILED(rc)) return rc;
2860
2861 i_setModified(IsModified_MachineData);
2862 mHWData.backup();
2863 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2864
2865 return S_OK;
2866}
2867
2868HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2869{
2870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2871
2872 *aIOCacheSize = mHWData->mIOCacheSize;
2873
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 HRESULT rc = i_checkStateDependency(MutableStateDep);
2882 if (FAILED(rc)) return rc;
2883
2884 i_setModified(IsModified_MachineData);
2885 mHWData.backup();
2886 mHWData->mIOCacheSize = aIOCacheSize;
2887
2888 return S_OK;
2889}
2890
2891
2892/**
2893 * @note Locks objects!
2894 */
2895HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2896 LockType_T aLockType)
2897{
2898 /* check the session state */
2899 SessionState_T state;
2900 HRESULT rc = aSession->COMGETTER(State)(&state);
2901 if (FAILED(rc)) return rc;
2902
2903 if (state != SessionState_Unlocked)
2904 return setError(VBOX_E_INVALID_OBJECT_STATE,
2905 tr("The given session is busy"));
2906
2907 // get the client's IInternalSessionControl interface
2908 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2909 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2910 E_INVALIDARG);
2911
2912 // session name (only used in some code paths)
2913 Utf8Str strSessionName;
2914
2915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 if (!mData->mRegistered)
2918 return setError(E_UNEXPECTED,
2919 tr("The machine '%s' is not registered"),
2920 mUserData->s.strName.c_str());
2921
2922 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2923
2924 SessionState_T oldState = mData->mSession.mState;
2925 /* Hack: in case the session is closing and there is a progress object
2926 * which allows waiting for the session to be closed, take the opportunity
2927 * and do a limited wait (max. 1 second). This helps a lot when the system
2928 * is busy and thus session closing can take a little while. */
2929 if ( mData->mSession.mState == SessionState_Unlocking
2930 && mData->mSession.mProgress)
2931 {
2932 alock.release();
2933 mData->mSession.mProgress->WaitForCompletion(1000);
2934 alock.acquire();
2935 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2936 }
2937
2938 // try again now
2939 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2940 // (i.e. session machine exists)
2941 && (aLockType == LockType_Shared) // caller wants a shared link to the
2942 // existing session that holds the write lock:
2943 )
2944 {
2945 // OK, share the session... we are now dealing with three processes:
2946 // 1) VBoxSVC (where this code runs);
2947 // 2) process C: the caller's client process (who wants a shared session);
2948 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2949
2950 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2951 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2952 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2953 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2954 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2955
2956 /*
2957 * Release the lock before calling the client process. It's safe here
2958 * since the only thing to do after we get the lock again is to add
2959 * the remote control to the list (which doesn't directly influence
2960 * anything).
2961 */
2962 alock.release();
2963
2964 // get the console of the session holding the write lock (this is a remote call)
2965 ComPtr<IConsole> pConsoleW;
2966 if (mData->mSession.mLockType == LockType_VM)
2967 {
2968 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2969 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2970 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2971 if (FAILED(rc))
2972 // the failure may occur w/o any error info (from RPC), so provide one
2973 return setError(VBOX_E_VM_ERROR,
2974 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
2975 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2976 }
2977
2978 // share the session machine and W's console with the caller's session
2979 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2980 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2981 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
2982
2983 if (FAILED(rc))
2984 // the failure may occur w/o any error info (from RPC), so provide one
2985 return setError(VBOX_E_VM_ERROR,
2986 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
2987 alock.acquire();
2988
2989 // need to revalidate the state after acquiring the lock again
2990 if (mData->mSession.mState != SessionState_Locked)
2991 {
2992 pSessionControl->Uninitialize();
2993 return setError(VBOX_E_INVALID_SESSION_STATE,
2994 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2995 mUserData->s.strName.c_str());
2996 }
2997
2998 // add the caller's session to the list
2999 mData->mSession.mRemoteControls.push_back(pSessionControl);
3000 }
3001 else if ( mData->mSession.mState == SessionState_Locked
3002 || mData->mSession.mState == SessionState_Unlocking
3003 )
3004 {
3005 // sharing not permitted, or machine still unlocking:
3006 return setError(VBOX_E_INVALID_OBJECT_STATE,
3007 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3008 mUserData->s.strName.c_str());
3009 }
3010 else
3011 {
3012 // machine is not locked: then write-lock the machine (create the session machine)
3013
3014 // must not be busy
3015 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3016
3017 // get the caller's session PID
3018 RTPROCESS pid = NIL_RTPROCESS;
3019 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3020 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3021 Assert(pid != NIL_RTPROCESS);
3022
3023 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3024
3025 if (fLaunchingVMProcess)
3026 {
3027 if (mData->mSession.mPID == NIL_RTPROCESS)
3028 {
3029 // two or more clients racing for a lock, the one which set the
3030 // session state to Spawning will win, the others will get an
3031 // error as we can't decide here if waiting a little would help
3032 // (only for shared locks this would avoid an error)
3033 return setError(VBOX_E_INVALID_OBJECT_STATE,
3034 tr("The machine '%s' already has a lock request pending"),
3035 mUserData->s.strName.c_str());
3036 }
3037
3038 // this machine is awaiting for a spawning session to be opened:
3039 // then the calling process must be the one that got started by
3040 // LaunchVMProcess()
3041
3042 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3043 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3044
3045#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3046 /* Hardened windows builds spawns three processes when a VM is
3047 launched, the 3rd one is the one that will end up here. */
3048 RTPROCESS pidParent;
3049 int vrc = RTProcQueryParent(pid, &pidParent);
3050 if (RT_SUCCESS(vrc))
3051 vrc = RTProcQueryParent(pidParent, &pidParent);
3052 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3053 || vrc == VERR_ACCESS_DENIED)
3054 {
3055 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3056 mData->mSession.mPID = pid;
3057 }
3058#endif
3059
3060 if (mData->mSession.mPID != pid)
3061 return setError(E_ACCESSDENIED,
3062 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3063 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3064 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3065 }
3066
3067 // create the mutable SessionMachine from the current machine
3068 ComObjPtr<SessionMachine> sessionMachine;
3069 sessionMachine.createObject();
3070 rc = sessionMachine->init(this);
3071 AssertComRC(rc);
3072
3073 /* NOTE: doing return from this function after this point but
3074 * before the end is forbidden since it may call SessionMachine::uninit()
3075 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3076 * lock while still holding the Machine lock in alock so that a deadlock
3077 * is possible due to the wrong lock order. */
3078
3079 if (SUCCEEDED(rc))
3080 {
3081 /*
3082 * Set the session state to Spawning to protect against subsequent
3083 * attempts to open a session and to unregister the machine after
3084 * we release the lock.
3085 */
3086 SessionState_T origState = mData->mSession.mState;
3087 mData->mSession.mState = SessionState_Spawning;
3088
3089#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3090 /* Get the client token ID to be passed to the client process */
3091 Utf8Str strTokenId;
3092 sessionMachine->i_getTokenId(strTokenId);
3093 Assert(!strTokenId.isEmpty());
3094#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3095 /* Get the client token to be passed to the client process */
3096 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3097 /* The token is now "owned" by pToken, fix refcount */
3098 if (!pToken.isNull())
3099 pToken->Release();
3100#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3101
3102 /*
3103 * Release the lock before calling the client process -- it will call
3104 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3105 * because the state is Spawning, so that LaunchVMProcess() and
3106 * LockMachine() calls will fail. This method, called before we
3107 * acquire the lock again, will fail because of the wrong PID.
3108 *
3109 * Note that mData->mSession.mRemoteControls accessed outside
3110 * the lock may not be modified when state is Spawning, so it's safe.
3111 */
3112 alock.release();
3113
3114 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3115#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3116 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3117#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3118 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3119 /* Now the token is owned by the client process. */
3120 pToken.setNull();
3121#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3122 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3123
3124 /* The failure may occur w/o any error info (from RPC), so provide one */
3125 if (FAILED(rc))
3126 setError(VBOX_E_VM_ERROR,
3127 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3128
3129 // get session name, either to remember or to compare against
3130 // the already known session name.
3131 {
3132 Bstr bstrSessionName;
3133 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3134 if (SUCCEEDED(rc2))
3135 strSessionName = bstrSessionName;
3136 }
3137
3138 if ( SUCCEEDED(rc)
3139 && fLaunchingVMProcess
3140 )
3141 {
3142 /* complete the remote session initialization */
3143
3144 /* get the console from the direct session */
3145 ComPtr<IConsole> console;
3146 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3147 ComAssertComRC(rc);
3148
3149 if (SUCCEEDED(rc) && !console)
3150 {
3151 ComAssert(!!console);
3152 rc = E_FAIL;
3153 }
3154
3155 /* assign machine & console to the remote session */
3156 if (SUCCEEDED(rc))
3157 {
3158 /*
3159 * after LaunchVMProcess(), the first and the only
3160 * entry in remoteControls is that remote session
3161 */
3162 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3163 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3164 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3165
3166 /* The failure may occur w/o any error info (from RPC), so provide one */
3167 if (FAILED(rc))
3168 setError(VBOX_E_VM_ERROR,
3169 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3170 }
3171
3172 if (FAILED(rc))
3173 pSessionControl->Uninitialize();
3174 }
3175
3176 /* acquire the lock again */
3177 alock.acquire();
3178
3179 /* Restore the session state */
3180 mData->mSession.mState = origState;
3181 }
3182
3183 // finalize spawning anyway (this is why we don't return on errors above)
3184 if (fLaunchingVMProcess)
3185 {
3186 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3187 /* Note that the progress object is finalized later */
3188 /** @todo Consider checking mData->mSession.mProgress for cancellation
3189 * around here. */
3190
3191 /* We don't reset mSession.mPID here because it is necessary for
3192 * SessionMachine::uninit() to reap the child process later. */
3193
3194 if (FAILED(rc))
3195 {
3196 /* Close the remote session, remove the remote control from the list
3197 * and reset session state to Closed (@note keep the code in sync
3198 * with the relevant part in checkForSpawnFailure()). */
3199
3200 Assert(mData->mSession.mRemoteControls.size() == 1);
3201 if (mData->mSession.mRemoteControls.size() == 1)
3202 {
3203 ErrorInfoKeeper eik;
3204 mData->mSession.mRemoteControls.front()->Uninitialize();
3205 }
3206
3207 mData->mSession.mRemoteControls.clear();
3208 mData->mSession.mState = SessionState_Unlocked;
3209 }
3210 }
3211 else
3212 {
3213 /* memorize PID of the directly opened session */
3214 if (SUCCEEDED(rc))
3215 mData->mSession.mPID = pid;
3216 }
3217
3218 if (SUCCEEDED(rc))
3219 {
3220 mData->mSession.mLockType = aLockType;
3221 /* memorize the direct session control and cache IUnknown for it */
3222 mData->mSession.mDirectControl = pSessionControl;
3223 mData->mSession.mState = SessionState_Locked;
3224 if (!fLaunchingVMProcess)
3225 mData->mSession.mName = strSessionName;
3226 /* associate the SessionMachine with this Machine */
3227 mData->mSession.mMachine = sessionMachine;
3228
3229 /* request an IUnknown pointer early from the remote party for later
3230 * identity checks (it will be internally cached within mDirectControl
3231 * at least on XPCOM) */
3232 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3233 NOREF(unk);
3234 }
3235
3236 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3237 * would break the lock order */
3238 alock.release();
3239
3240 /* uninitialize the created session machine on failure */
3241 if (FAILED(rc))
3242 sessionMachine->uninit();
3243 }
3244
3245 if (SUCCEEDED(rc))
3246 {
3247 /*
3248 * tell the client watcher thread to update the set of
3249 * machines that have open sessions
3250 */
3251 mParent->i_updateClientWatcher();
3252
3253 if (oldState != SessionState_Locked)
3254 /* fire an event */
3255 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3256 }
3257
3258 return rc;
3259}
3260
3261/**
3262 * @note Locks objects!
3263 */
3264HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3265 const com::Utf8Str &aName,
3266 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3267 ComPtr<IProgress> &aProgress)
3268{
3269 Utf8Str strFrontend(aName);
3270 /* "emergencystop" doesn't need the session, so skip the checks/interface
3271 * retrieval. This code doesn't quite fit in here, but introducing a
3272 * special API method would be even more effort, and would require explicit
3273 * support by every API client. It's better to hide the feature a bit. */
3274 if (strFrontend != "emergencystop")
3275 CheckComArgNotNull(aSession);
3276
3277 HRESULT rc = S_OK;
3278 if (strFrontend.isEmpty())
3279 {
3280 Bstr bstrFrontend;
3281 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3282 if (FAILED(rc))
3283 return rc;
3284 strFrontend = bstrFrontend;
3285 if (strFrontend.isEmpty())
3286 {
3287 ComPtr<ISystemProperties> systemProperties;
3288 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3289 if (FAILED(rc))
3290 return rc;
3291 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3292 if (FAILED(rc))
3293 return rc;
3294 strFrontend = bstrFrontend;
3295 }
3296 /* paranoia - emergencystop is not a valid default */
3297 if (strFrontend == "emergencystop")
3298 strFrontend = Utf8Str::Empty;
3299 }
3300 /* default frontend: Qt GUI */
3301 if (strFrontend.isEmpty())
3302 strFrontend = "GUI/Qt";
3303
3304 if (strFrontend != "emergencystop")
3305 {
3306 /* check the session state */
3307 SessionState_T state;
3308 rc = aSession->COMGETTER(State)(&state);
3309 if (FAILED(rc))
3310 return rc;
3311
3312 if (state != SessionState_Unlocked)
3313 return setError(VBOX_E_INVALID_OBJECT_STATE,
3314 tr("The given session is busy"));
3315
3316 /* get the IInternalSessionControl interface */
3317 ComPtr<IInternalSessionControl> control(aSession);
3318 ComAssertMsgRet(!control.isNull(),
3319 ("No IInternalSessionControl interface"),
3320 E_INVALIDARG);
3321
3322 /* get the teleporter enable state for the progress object init. */
3323 BOOL fTeleporterEnabled;
3324 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3325 if (FAILED(rc))
3326 return rc;
3327
3328 /* create a progress object */
3329 ComObjPtr<ProgressProxy> progress;
3330 progress.createObject();
3331 rc = progress->init(mParent,
3332 static_cast<IMachine*>(this),
3333 Bstr(tr("Starting VM")).raw(),
3334 TRUE /* aCancelable */,
3335 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3336 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3337 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3338 2 /* uFirstOperationWeight */,
3339 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3340
3341 if (SUCCEEDED(rc))
3342 {
3343 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3344 if (SUCCEEDED(rc))
3345 {
3346 aProgress = progress;
3347
3348 /* signal the client watcher thread */
3349 mParent->i_updateClientWatcher();
3350
3351 /* fire an event */
3352 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3353 }
3354 }
3355 }
3356 else
3357 {
3358 /* no progress object - either instant success or failure */
3359 aProgress = NULL;
3360
3361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3362
3363 if (mData->mSession.mState != SessionState_Locked)
3364 return setError(VBOX_E_INVALID_OBJECT_STATE,
3365 tr("The machine '%s' is not locked by a session"),
3366 mUserData->s.strName.c_str());
3367
3368 /* must have a VM process associated - do not kill normal API clients
3369 * with an open session */
3370 if (!Global::IsOnline(mData->mMachineState))
3371 return setError(VBOX_E_INVALID_OBJECT_STATE,
3372 tr("The machine '%s' does not have a VM process"),
3373 mUserData->s.strName.c_str());
3374
3375 /* forcibly terminate the VM process */
3376 if (mData->mSession.mPID != NIL_RTPROCESS)
3377 RTProcTerminate(mData->mSession.mPID);
3378
3379 /* signal the client watcher thread, as most likely the client has
3380 * been terminated */
3381 mParent->i_updateClientWatcher();
3382 }
3383
3384 return rc;
3385}
3386
3387HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3388{
3389 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3390 return setError(E_INVALIDARG,
3391 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3392 aPosition, SchemaDefs::MaxBootPosition);
3393
3394 if (aDevice == DeviceType_USB)
3395 return setError(E_NOTIMPL,
3396 tr("Booting from USB device is currently not supported"));
3397
3398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3399
3400 HRESULT rc = i_checkStateDependency(MutableStateDep);
3401 if (FAILED(rc)) return rc;
3402
3403 i_setModified(IsModified_MachineData);
3404 mHWData.backup();
3405 mHWData->mBootOrder[aPosition - 1] = aDevice;
3406
3407 return S_OK;
3408}
3409
3410HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3411{
3412 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3413 return setError(E_INVALIDARG,
3414 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3415 aPosition, SchemaDefs::MaxBootPosition);
3416
3417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3418
3419 *aDevice = mHWData->mBootOrder[aPosition - 1];
3420
3421 return S_OK;
3422}
3423
3424HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3425 LONG aControllerPort,
3426 LONG aDevice,
3427 DeviceType_T aType,
3428 const ComPtr<IMedium> &aMedium)
3429{
3430 IMedium *aM = aMedium;
3431 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3432 aName.c_str(), aControllerPort, aDevice, aType, aM));
3433
3434 // request the host lock first, since might be calling Host methods for getting host drives;
3435 // next, protect the media tree all the while we're in here, as well as our member variables
3436 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3437 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3438
3439 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3440 if (FAILED(rc)) return rc;
3441
3442 /// @todo NEWMEDIA implicit machine registration
3443 if (!mData->mRegistered)
3444 return setError(VBOX_E_INVALID_OBJECT_STATE,
3445 tr("Cannot attach storage devices to an unregistered machine"));
3446
3447 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3448
3449 /* Check for an existing controller. */
3450 ComObjPtr<StorageController> ctl;
3451 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3452 if (FAILED(rc)) return rc;
3453
3454 StorageControllerType_T ctrlType;
3455 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3456 if (FAILED(rc))
3457 return setError(E_FAIL,
3458 tr("Could not get type of controller '%s'"),
3459 aName.c_str());
3460
3461 bool fSilent = false;
3462 Utf8Str strReconfig;
3463
3464 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3465 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3466 if ( mData->mMachineState == MachineState_Paused
3467 && strReconfig == "1")
3468 fSilent = true;
3469
3470 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3471 bool fHotplug = false;
3472 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3473 fHotplug = true;
3474
3475 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3476 return setError(VBOX_E_INVALID_VM_STATE,
3477 tr("Controller '%s' does not support hotplugging"),
3478 aName.c_str());
3479
3480 // check that the port and device are not out of range
3481 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3482 if (FAILED(rc)) return rc;
3483
3484 /* check if the device slot is already busy */
3485 MediumAttachment *pAttachTemp;
3486 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3487 aName,
3488 aControllerPort,
3489 aDevice)))
3490 {
3491 Medium *pMedium = pAttachTemp->i_getMedium();
3492 if (pMedium)
3493 {
3494 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3495 return setError(VBOX_E_OBJECT_IN_USE,
3496 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3497 pMedium->i_getLocationFull().c_str(),
3498 aControllerPort,
3499 aDevice,
3500 aName.c_str());
3501 }
3502 else
3503 return setError(VBOX_E_OBJECT_IN_USE,
3504 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3505 aControllerPort, aDevice, aName.c_str());
3506 }
3507
3508 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3509 if (aMedium && medium.isNull())
3510 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3511
3512 AutoCaller mediumCaller(medium);
3513 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3514
3515 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3516
3517 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3518 && !medium.isNull()
3519 && ( medium->i_getType() != MediumType_Readonly
3520 || medium->i_getDeviceType() != DeviceType_DVD)
3521 )
3522 return setError(VBOX_E_OBJECT_IN_USE,
3523 tr("Medium '%s' is already attached to this virtual machine"),
3524 medium->i_getLocationFull().c_str());
3525
3526 if (!medium.isNull())
3527 {
3528 MediumType_T mtype = medium->i_getType();
3529 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3530 // For DVDs it's not written to the config file, so needs no global config
3531 // version bump. For floppies it's a new attribute "type", which is ignored
3532 // by older VirtualBox version, so needs no global config version bump either.
3533 // For hard disks this type is not accepted.
3534 if (mtype == MediumType_MultiAttach)
3535 {
3536 // This type is new with VirtualBox 4.0 and therefore requires settings
3537 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3538 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3539 // two reasons: The medium type is a property of the media registry tree, which
3540 // can reside in the global config file (for pre-4.0 media); we would therefore
3541 // possibly need to bump the global config version. We don't want to do that though
3542 // because that might make downgrading to pre-4.0 impossible.
3543 // As a result, we can only use these two new types if the medium is NOT in the
3544 // global registry:
3545 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3546 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3547 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3548 )
3549 return setError(VBOX_E_INVALID_OBJECT_STATE,
3550 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3551 "to machines that were created with VirtualBox 4.0 or later"),
3552 medium->i_getLocationFull().c_str());
3553 }
3554 }
3555
3556 bool fIndirect = false;
3557 if (!medium.isNull())
3558 fIndirect = medium->i_isReadOnly();
3559 bool associate = true;
3560
3561 do
3562 {
3563 if ( aType == DeviceType_HardDisk
3564 && mMediumAttachments.isBackedUp())
3565 {
3566 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3567
3568 /* check if the medium was attached to the VM before we started
3569 * changing attachments in which case the attachment just needs to
3570 * be restored */
3571 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3572 {
3573 AssertReturn(!fIndirect, E_FAIL);
3574
3575 /* see if it's the same bus/channel/device */
3576 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3577 {
3578 /* the simplest case: restore the whole attachment
3579 * and return, nothing else to do */
3580 mMediumAttachments->push_back(pAttachTemp);
3581
3582 /* Reattach the medium to the VM. */
3583 if (fHotplug || fSilent)
3584 {
3585 mediumLock.release();
3586 treeLock.release();
3587 alock.release();
3588
3589 MediumLockList *pMediumLockList(new MediumLockList());
3590
3591 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3592 medium /* pToLockWrite */,
3593 false /* fMediumLockWriteAll */,
3594 NULL,
3595 *pMediumLockList);
3596 alock.acquire();
3597 if (FAILED(rc))
3598 delete pMediumLockList;
3599 else
3600 {
3601 mData->mSession.mLockedMedia.Unlock();
3602 alock.release();
3603 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3604 mData->mSession.mLockedMedia.Lock();
3605 alock.acquire();
3606 }
3607 alock.release();
3608
3609 if (SUCCEEDED(rc))
3610 {
3611 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3612 /* Remove lock list in case of error. */
3613 if (FAILED(rc))
3614 {
3615 mData->mSession.mLockedMedia.Unlock();
3616 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3617 mData->mSession.mLockedMedia.Lock();
3618 }
3619 }
3620 }
3621
3622 return S_OK;
3623 }
3624
3625 /* bus/channel/device differ; we need a new attachment object,
3626 * but don't try to associate it again */
3627 associate = false;
3628 break;
3629 }
3630 }
3631
3632 /* go further only if the attachment is to be indirect */
3633 if (!fIndirect)
3634 break;
3635
3636 /* perform the so called smart attachment logic for indirect
3637 * attachments. Note that smart attachment is only applicable to base
3638 * hard disks. */
3639
3640 if (medium->i_getParent().isNull())
3641 {
3642 /* first, investigate the backup copy of the current hard disk
3643 * attachments to make it possible to re-attach existing diffs to
3644 * another device slot w/o losing their contents */
3645 if (mMediumAttachments.isBackedUp())
3646 {
3647 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3648
3649 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3650 uint32_t foundLevel = 0;
3651
3652 for (MediumAttachmentList::const_iterator
3653 it = oldAtts.begin();
3654 it != oldAtts.end();
3655 ++it)
3656 {
3657 uint32_t level = 0;
3658 MediumAttachment *pAttach = *it;
3659 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3660 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3661 if (pMedium.isNull())
3662 continue;
3663
3664 if (pMedium->i_getBase(&level) == medium)
3665 {
3666 /* skip the hard disk if its currently attached (we
3667 * cannot attach the same hard disk twice) */
3668 if (i_findAttachment(*mMediumAttachments.data(),
3669 pMedium))
3670 continue;
3671
3672 /* matched device, channel and bus (i.e. attached to the
3673 * same place) will win and immediately stop the search;
3674 * otherwise the attachment that has the youngest
3675 * descendant of medium will be used
3676 */
3677 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3678 {
3679 /* the simplest case: restore the whole attachment
3680 * and return, nothing else to do */
3681 mMediumAttachments->push_back(*it);
3682
3683 /* Reattach the medium to the VM. */
3684 if (fHotplug || fSilent)
3685 {
3686 mediumLock.release();
3687 treeLock.release();
3688 alock.release();
3689
3690 MediumLockList *pMediumLockList(new MediumLockList());
3691
3692 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3693 medium /* pToLockWrite */,
3694 false /* fMediumLockWriteAll */,
3695 NULL,
3696 *pMediumLockList);
3697 alock.acquire();
3698 if (FAILED(rc))
3699 delete pMediumLockList;
3700 else
3701 {
3702 mData->mSession.mLockedMedia.Unlock();
3703 alock.release();
3704 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3705 mData->mSession.mLockedMedia.Lock();
3706 alock.acquire();
3707 }
3708 alock.release();
3709
3710 if (SUCCEEDED(rc))
3711 {
3712 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3713 /* Remove lock list in case of error. */
3714 if (FAILED(rc))
3715 {
3716 mData->mSession.mLockedMedia.Unlock();
3717 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3718 mData->mSession.mLockedMedia.Lock();
3719 }
3720 }
3721 }
3722
3723 return S_OK;
3724 }
3725 else if ( foundIt == oldAtts.end()
3726 || level > foundLevel /* prefer younger */
3727 )
3728 {
3729 foundIt = it;
3730 foundLevel = level;
3731 }
3732 }
3733 }
3734
3735 if (foundIt != oldAtts.end())
3736 {
3737 /* use the previously attached hard disk */
3738 medium = (*foundIt)->i_getMedium();
3739 mediumCaller.attach(medium);
3740 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3741 mediumLock.attach(medium);
3742 /* not implicit, doesn't require association with this VM */
3743 fIndirect = false;
3744 associate = false;
3745 /* go right to the MediumAttachment creation */
3746 break;
3747 }
3748 }
3749
3750 /* must give up the medium lock and medium tree lock as below we
3751 * go over snapshots, which needs a lock with higher lock order. */
3752 mediumLock.release();
3753 treeLock.release();
3754
3755 /* then, search through snapshots for the best diff in the given
3756 * hard disk's chain to base the new diff on */
3757
3758 ComObjPtr<Medium> base;
3759 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3760 while (snap)
3761 {
3762 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3763
3764 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3765
3766 MediumAttachment *pAttachFound = NULL;
3767 uint32_t foundLevel = 0;
3768
3769 for (MediumAttachmentList::const_iterator
3770 it = snapAtts.begin();
3771 it != snapAtts.end();
3772 ++it)
3773 {
3774 MediumAttachment *pAttach = *it;
3775 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3776 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3777 if (pMedium.isNull())
3778 continue;
3779
3780 uint32_t level = 0;
3781 if (pMedium->i_getBase(&level) == medium)
3782 {
3783 /* matched device, channel and bus (i.e. attached to the
3784 * same place) will win and immediately stop the search;
3785 * otherwise the attachment that has the youngest
3786 * descendant of medium will be used
3787 */
3788 if ( pAttach->i_getDevice() == aDevice
3789 && pAttach->i_getPort() == aControllerPort
3790 && pAttach->i_getControllerName() == aName
3791 )
3792 {
3793 pAttachFound = pAttach;
3794 break;
3795 }
3796 else if ( !pAttachFound
3797 || level > foundLevel /* prefer younger */
3798 )
3799 {
3800 pAttachFound = pAttach;
3801 foundLevel = level;
3802 }
3803 }
3804 }
3805
3806 if (pAttachFound)
3807 {
3808 base = pAttachFound->i_getMedium();
3809 break;
3810 }
3811
3812 snap = snap->i_getParent();
3813 }
3814
3815 /* re-lock medium tree and the medium, as we need it below */
3816 treeLock.acquire();
3817 mediumLock.acquire();
3818
3819 /* found a suitable diff, use it as a base */
3820 if (!base.isNull())
3821 {
3822 medium = base;
3823 mediumCaller.attach(medium);
3824 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3825 mediumLock.attach(medium);
3826 }
3827 }
3828
3829 Utf8Str strFullSnapshotFolder;
3830 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3831
3832 ComObjPtr<Medium> diff;
3833 diff.createObject();
3834 // store this diff in the same registry as the parent
3835 Guid uuidRegistryParent;
3836 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3837 {
3838 // parent image has no registry: this can happen if we're attaching a new immutable
3839 // image that has not yet been attached (medium then points to the base and we're
3840 // creating the diff image for the immutable, and the parent is not yet registered);
3841 // put the parent in the machine registry then
3842 mediumLock.release();
3843 treeLock.release();
3844 alock.release();
3845 i_addMediumToRegistry(medium);
3846 alock.acquire();
3847 treeLock.acquire();
3848 mediumLock.acquire();
3849 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3850 }
3851 rc = diff->init(mParent,
3852 medium->i_getPreferredDiffFormat(),
3853 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3854 uuidRegistryParent,
3855 DeviceType_HardDisk);
3856 if (FAILED(rc)) return rc;
3857
3858 /* Apply the normal locking logic to the entire chain. */
3859 MediumLockList *pMediumLockList(new MediumLockList());
3860 mediumLock.release();
3861 treeLock.release();
3862 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3863 diff /* pToLockWrite */,
3864 false /* fMediumLockWriteAll */,
3865 medium,
3866 *pMediumLockList);
3867 treeLock.acquire();
3868 mediumLock.acquire();
3869 if (SUCCEEDED(rc))
3870 {
3871 mediumLock.release();
3872 treeLock.release();
3873 rc = pMediumLockList->Lock();
3874 treeLock.acquire();
3875 mediumLock.acquire();
3876 if (FAILED(rc))
3877 setError(rc,
3878 tr("Could not lock medium when creating diff '%s'"),
3879 diff->i_getLocationFull().c_str());
3880 else
3881 {
3882 /* will release the lock before the potentially lengthy
3883 * operation, so protect with the special state */
3884 MachineState_T oldState = mData->mMachineState;
3885 i_setMachineState(MachineState_SettingUp);
3886
3887 mediumLock.release();
3888 treeLock.release();
3889 alock.release();
3890
3891 rc = medium->i_createDiffStorage(diff,
3892 medium->i_getPreferredDiffVariant(),
3893 pMediumLockList,
3894 NULL /* aProgress */,
3895 true /* aWait */,
3896 false /* aNotify */);
3897
3898 alock.acquire();
3899 treeLock.acquire();
3900 mediumLock.acquire();
3901
3902 i_setMachineState(oldState);
3903 }
3904 }
3905
3906 /* Unlock the media and free the associated memory. */
3907 delete pMediumLockList;
3908
3909 if (FAILED(rc)) return rc;
3910
3911 /* use the created diff for the actual attachment */
3912 medium = diff;
3913 mediumCaller.attach(medium);
3914 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3915 mediumLock.attach(medium);
3916 }
3917 while (0);
3918
3919 ComObjPtr<MediumAttachment> attachment;
3920 attachment.createObject();
3921 rc = attachment->init(this,
3922 medium,
3923 aName,
3924 aControllerPort,
3925 aDevice,
3926 aType,
3927 fIndirect,
3928 false /* fPassthrough */,
3929 false /* fTempEject */,
3930 false /* fNonRotational */,
3931 false /* fDiscard */,
3932 fHotplug /* fHotPluggable */,
3933 Utf8Str::Empty);
3934 if (FAILED(rc)) return rc;
3935
3936 if (associate && !medium.isNull())
3937 {
3938 // as the last step, associate the medium to the VM
3939 rc = medium->i_addBackReference(mData->mUuid);
3940 // here we can fail because of Deleting, or being in process of creating a Diff
3941 if (FAILED(rc)) return rc;
3942
3943 mediumLock.release();
3944 treeLock.release();
3945 alock.release();
3946 i_addMediumToRegistry(medium);
3947 alock.acquire();
3948 treeLock.acquire();
3949 mediumLock.acquire();
3950 }
3951
3952 /* success: finally remember the attachment */
3953 i_setModified(IsModified_Storage);
3954 mMediumAttachments.backup();
3955 mMediumAttachments->push_back(attachment);
3956
3957 mediumLock.release();
3958 treeLock.release();
3959 alock.release();
3960
3961 if (fHotplug || fSilent)
3962 {
3963 if (!medium.isNull())
3964 {
3965 MediumLockList *pMediumLockList(new MediumLockList());
3966
3967 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3968 medium /* pToLockWrite */,
3969 false /* fMediumLockWriteAll */,
3970 NULL,
3971 *pMediumLockList);
3972 alock.acquire();
3973 if (FAILED(rc))
3974 delete pMediumLockList;
3975 else
3976 {
3977 mData->mSession.mLockedMedia.Unlock();
3978 alock.release();
3979 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3980 mData->mSession.mLockedMedia.Lock();
3981 alock.acquire();
3982 }
3983 alock.release();
3984 }
3985
3986 if (SUCCEEDED(rc))
3987 {
3988 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3989 /* Remove lock list in case of error. */
3990 if (FAILED(rc))
3991 {
3992 mData->mSession.mLockedMedia.Unlock();
3993 mData->mSession.mLockedMedia.Remove(attachment);
3994 mData->mSession.mLockedMedia.Lock();
3995 }
3996 }
3997 }
3998
3999 /* Save modified registries, but skip this machine as it's the caller's
4000 * job to save its settings like all other settings changes. */
4001 mParent->i_unmarkRegistryModified(i_getId());
4002 mParent->i_saveModifiedRegistries();
4003
4004 if (SUCCEEDED(rc))
4005 {
4006 if (fIndirect && medium != aM)
4007 mParent->i_onMediumConfigChanged(medium);
4008 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4009 }
4010
4011 return rc;
4012}
4013
4014HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4015 LONG aDevice)
4016{
4017 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4018 aName.c_str(), aControllerPort, aDevice));
4019
4020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4021
4022 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4023 if (FAILED(rc)) return rc;
4024
4025 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4026
4027 /* Check for an existing controller. */
4028 ComObjPtr<StorageController> ctl;
4029 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4030 if (FAILED(rc)) return rc;
4031
4032 StorageControllerType_T ctrlType;
4033 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4034 if (FAILED(rc))
4035 return setError(E_FAIL,
4036 tr("Could not get type of controller '%s'"),
4037 aName.c_str());
4038
4039 bool fSilent = false;
4040 Utf8Str strReconfig;
4041
4042 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4043 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4044 if ( mData->mMachineState == MachineState_Paused
4045 && strReconfig == "1")
4046 fSilent = true;
4047
4048 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4049 bool fHotplug = false;
4050 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4051 fHotplug = true;
4052
4053 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4054 return setError(VBOX_E_INVALID_VM_STATE,
4055 tr("Controller '%s' does not support hotplugging"),
4056 aName.c_str());
4057
4058 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4059 aName,
4060 aControllerPort,
4061 aDevice);
4062 if (!pAttach)
4063 return setError(VBOX_E_OBJECT_NOT_FOUND,
4064 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4065 aDevice, aControllerPort, aName.c_str());
4066
4067 if (fHotplug && !pAttach->i_getHotPluggable())
4068 return setError(VBOX_E_NOT_SUPPORTED,
4069 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4070 aDevice, aControllerPort, aName.c_str());
4071
4072 /*
4073 * The VM has to detach the device before we delete any implicit diffs.
4074 * If this fails we can roll back without loosing data.
4075 */
4076 if (fHotplug || fSilent)
4077 {
4078 alock.release();
4079 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4080 alock.acquire();
4081 }
4082 if (FAILED(rc)) return rc;
4083
4084 /* If we are here everything went well and we can delete the implicit now. */
4085 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4086
4087 alock.release();
4088
4089 /* Save modified registries, but skip this machine as it's the caller's
4090 * job to save its settings like all other settings changes. */
4091 mParent->i_unmarkRegistryModified(i_getId());
4092 mParent->i_saveModifiedRegistries();
4093
4094 if (SUCCEEDED(rc))
4095 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4096
4097 return rc;
4098}
4099
4100HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4101 LONG aDevice, BOOL aPassthrough)
4102{
4103 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4104 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4105
4106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4107
4108 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4109 if (FAILED(rc)) return rc;
4110
4111 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4112
4113 /* Check for an existing controller. */
4114 ComObjPtr<StorageController> ctl;
4115 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4116 if (FAILED(rc)) return rc;
4117
4118 StorageControllerType_T ctrlType;
4119 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4120 if (FAILED(rc))
4121 return setError(E_FAIL,
4122 tr("Could not get type of controller '%s'"),
4123 aName.c_str());
4124
4125 bool fSilent = false;
4126 Utf8Str strReconfig;
4127
4128 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4129 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4130 if ( mData->mMachineState == MachineState_Paused
4131 && strReconfig == "1")
4132 fSilent = true;
4133
4134 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4135 bool fHotplug = false;
4136 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4137 fHotplug = true;
4138
4139 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4140 return setError(VBOX_E_INVALID_VM_STATE,
4141 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4142 aName.c_str());
4143
4144 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4145 aName,
4146 aControllerPort,
4147 aDevice);
4148 if (!pAttach)
4149 return setError(VBOX_E_OBJECT_NOT_FOUND,
4150 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4151 aDevice, aControllerPort, aName.c_str());
4152
4153
4154 i_setModified(IsModified_Storage);
4155 mMediumAttachments.backup();
4156
4157 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4158
4159 if (pAttach->i_getType() != DeviceType_DVD)
4160 return setError(E_INVALIDARG,
4161 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4162 aDevice, aControllerPort, aName.c_str());
4163
4164 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4165
4166 pAttach->i_updatePassthrough(!!aPassthrough);
4167
4168 attLock.release();
4169 alock.release();
4170 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4171 if (SUCCEEDED(rc) && fValueChanged)
4172 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4173
4174 return rc;
4175}
4176
4177HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4178 LONG aDevice, BOOL aTemporaryEject)
4179{
4180
4181 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4182 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4183
4184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4185
4186 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4187 if (FAILED(rc)) return rc;
4188
4189 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4190 aName,
4191 aControllerPort,
4192 aDevice);
4193 if (!pAttach)
4194 return setError(VBOX_E_OBJECT_NOT_FOUND,
4195 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4196 aDevice, aControllerPort, aName.c_str());
4197
4198
4199 i_setModified(IsModified_Storage);
4200 mMediumAttachments.backup();
4201
4202 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4203
4204 if (pAttach->i_getType() != DeviceType_DVD)
4205 return setError(E_INVALIDARG,
4206 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4207 aDevice, aControllerPort, aName.c_str());
4208 pAttach->i_updateTempEject(!!aTemporaryEject);
4209
4210 return S_OK;
4211}
4212
4213HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4214 LONG aDevice, BOOL aNonRotational)
4215{
4216
4217 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4218 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4219
4220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4221
4222 HRESULT rc = i_checkStateDependency(MutableStateDep);
4223 if (FAILED(rc)) return rc;
4224
4225 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4226
4227 if (Global::IsOnlineOrTransient(mData->mMachineState))
4228 return setError(VBOX_E_INVALID_VM_STATE,
4229 tr("Invalid machine state: %s"),
4230 Global::stringifyMachineState(mData->mMachineState));
4231
4232 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4233 aName,
4234 aControllerPort,
4235 aDevice);
4236 if (!pAttach)
4237 return setError(VBOX_E_OBJECT_NOT_FOUND,
4238 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4239 aDevice, aControllerPort, aName.c_str());
4240
4241
4242 i_setModified(IsModified_Storage);
4243 mMediumAttachments.backup();
4244
4245 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4246
4247 if (pAttach->i_getType() != DeviceType_HardDisk)
4248 return setError(E_INVALIDARG,
4249 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"),
4250 aDevice, aControllerPort, aName.c_str());
4251 pAttach->i_updateNonRotational(!!aNonRotational);
4252
4253 return S_OK;
4254}
4255
4256HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4257 LONG aDevice, BOOL aDiscard)
4258{
4259
4260 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4261 aName.c_str(), aControllerPort, aDevice, aDiscard));
4262
4263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4264
4265 HRESULT rc = i_checkStateDependency(MutableStateDep);
4266 if (FAILED(rc)) return rc;
4267
4268 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4269
4270 if (Global::IsOnlineOrTransient(mData->mMachineState))
4271 return setError(VBOX_E_INVALID_VM_STATE,
4272 tr("Invalid machine state: %s"),
4273 Global::stringifyMachineState(mData->mMachineState));
4274
4275 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4276 aName,
4277 aControllerPort,
4278 aDevice);
4279 if (!pAttach)
4280 return setError(VBOX_E_OBJECT_NOT_FOUND,
4281 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4282 aDevice, aControllerPort, aName.c_str());
4283
4284
4285 i_setModified(IsModified_Storage);
4286 mMediumAttachments.backup();
4287
4288 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4289
4290 if (pAttach->i_getType() != DeviceType_HardDisk)
4291 return setError(E_INVALIDARG,
4292 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"),
4293 aDevice, aControllerPort, aName.c_str());
4294 pAttach->i_updateDiscard(!!aDiscard);
4295
4296 return S_OK;
4297}
4298
4299HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4300 LONG aDevice, BOOL aHotPluggable)
4301{
4302 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4303 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4304
4305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4306
4307 HRESULT rc = i_checkStateDependency(MutableStateDep);
4308 if (FAILED(rc)) return rc;
4309
4310 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4311
4312 if (Global::IsOnlineOrTransient(mData->mMachineState))
4313 return setError(VBOX_E_INVALID_VM_STATE,
4314 tr("Invalid machine state: %s"),
4315 Global::stringifyMachineState(mData->mMachineState));
4316
4317 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4318 aName,
4319 aControllerPort,
4320 aDevice);
4321 if (!pAttach)
4322 return setError(VBOX_E_OBJECT_NOT_FOUND,
4323 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4324 aDevice, aControllerPort, aName.c_str());
4325
4326 /* Check for an existing controller. */
4327 ComObjPtr<StorageController> ctl;
4328 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4329 if (FAILED(rc)) return rc;
4330
4331 StorageControllerType_T ctrlType;
4332 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4333 if (FAILED(rc))
4334 return setError(E_FAIL,
4335 tr("Could not get type of controller '%s'"),
4336 aName.c_str());
4337
4338 if (!i_isControllerHotplugCapable(ctrlType))
4339 return setError(VBOX_E_NOT_SUPPORTED,
4340 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4341 aName.c_str());
4342
4343 i_setModified(IsModified_Storage);
4344 mMediumAttachments.backup();
4345
4346 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4347
4348 if (pAttach->i_getType() == DeviceType_Floppy)
4349 return setError(E_INVALIDARG,
4350 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"),
4351 aDevice, aControllerPort, aName.c_str());
4352 pAttach->i_updateHotPluggable(!!aHotPluggable);
4353
4354 return S_OK;
4355}
4356
4357HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4358 LONG aDevice)
4359{
4360 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4361 aName.c_str(), aControllerPort, aDevice));
4362
4363 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4364}
4365
4366HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4367 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4368{
4369 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4370 aName.c_str(), aControllerPort, aDevice));
4371
4372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4373
4374 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4375 if (FAILED(rc)) return rc;
4376
4377 if (Global::IsOnlineOrTransient(mData->mMachineState))
4378 return setError(VBOX_E_INVALID_VM_STATE,
4379 tr("Invalid machine state: %s"),
4380 Global::stringifyMachineState(mData->mMachineState));
4381
4382 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4383 aName,
4384 aControllerPort,
4385 aDevice);
4386 if (!pAttach)
4387 return setError(VBOX_E_OBJECT_NOT_FOUND,
4388 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4389 aDevice, aControllerPort, aName.c_str());
4390
4391
4392 i_setModified(IsModified_Storage);
4393 mMediumAttachments.backup();
4394
4395 IBandwidthGroup *iB = aBandwidthGroup;
4396 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4397 if (aBandwidthGroup && group.isNull())
4398 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4399
4400 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4401
4402 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4403 if (strBandwidthGroupOld.isNotEmpty())
4404 {
4405 /* Get the bandwidth group object and release it - this must not fail. */
4406 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4407 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4408 Assert(SUCCEEDED(rc));
4409
4410 pBandwidthGroupOld->i_release();
4411 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4412 }
4413
4414 if (!group.isNull())
4415 {
4416 group->i_reference();
4417 pAttach->i_updateBandwidthGroup(group->i_getName());
4418 }
4419
4420 return S_OK;
4421}
4422
4423HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4424 LONG aControllerPort,
4425 LONG aDevice,
4426 DeviceType_T aType)
4427{
4428 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4429 aName.c_str(), aControllerPort, aDevice, aType));
4430
4431 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4432}
4433
4434
4435HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4436 LONG aControllerPort,
4437 LONG aDevice,
4438 BOOL aForce)
4439{
4440 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4441 aName.c_str(), aControllerPort, aForce));
4442
4443 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4444}
4445
4446HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4447 LONG aControllerPort,
4448 LONG aDevice,
4449 const ComPtr<IMedium> &aMedium,
4450 BOOL aForce)
4451{
4452 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4453 aName.c_str(), aControllerPort, aDevice, aForce));
4454
4455 // request the host lock first, since might be calling Host methods for getting host drives;
4456 // next, protect the media tree all the while we're in here, as well as our member variables
4457 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4458 this->lockHandle(),
4459 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4460
4461 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4462 aName,
4463 aControllerPort,
4464 aDevice);
4465 if (pAttach.isNull())
4466 return setError(VBOX_E_OBJECT_NOT_FOUND,
4467 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4468 aDevice, aControllerPort, aName.c_str());
4469
4470 /* Remember previously mounted medium. The medium before taking the
4471 * backup is not necessarily the same thing. */
4472 ComObjPtr<Medium> oldmedium;
4473 oldmedium = pAttach->i_getMedium();
4474
4475 IMedium *iM = aMedium;
4476 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4477 if (aMedium && pMedium.isNull())
4478 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4479
4480 AutoCaller mediumCaller(pMedium);
4481 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4482
4483 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4484 if (pMedium)
4485 {
4486 DeviceType_T mediumType = pAttach->i_getType();
4487 switch (mediumType)
4488 {
4489 case DeviceType_DVD:
4490 case DeviceType_Floppy:
4491 break;
4492
4493 default:
4494 return setError(VBOX_E_INVALID_OBJECT_STATE,
4495 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4496 aControllerPort,
4497 aDevice,
4498 aName.c_str());
4499 }
4500 }
4501
4502 i_setModified(IsModified_Storage);
4503 mMediumAttachments.backup();
4504
4505 {
4506 // The backup operation makes the pAttach reference point to the
4507 // old settings. Re-get the correct reference.
4508 pAttach = i_findAttachment(*mMediumAttachments.data(),
4509 aName,
4510 aControllerPort,
4511 aDevice);
4512 if (!oldmedium.isNull())
4513 oldmedium->i_removeBackReference(mData->mUuid);
4514 if (!pMedium.isNull())
4515 {
4516 pMedium->i_addBackReference(mData->mUuid);
4517
4518 mediumLock.release();
4519 multiLock.release();
4520 i_addMediumToRegistry(pMedium);
4521 multiLock.acquire();
4522 mediumLock.acquire();
4523 }
4524
4525 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4526 pAttach->i_updateMedium(pMedium);
4527 }
4528
4529 i_setModified(IsModified_Storage);
4530
4531 mediumLock.release();
4532 multiLock.release();
4533 HRESULT rc = i_onMediumChange(pAttach, aForce);
4534 multiLock.acquire();
4535 mediumLock.acquire();
4536
4537 /* On error roll back this change only. */
4538 if (FAILED(rc))
4539 {
4540 if (!pMedium.isNull())
4541 pMedium->i_removeBackReference(mData->mUuid);
4542 pAttach = i_findAttachment(*mMediumAttachments.data(),
4543 aName,
4544 aControllerPort,
4545 aDevice);
4546 /* If the attachment is gone in the meantime, bail out. */
4547 if (pAttach.isNull())
4548 return rc;
4549 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4550 if (!oldmedium.isNull())
4551 oldmedium->i_addBackReference(mData->mUuid);
4552 pAttach->i_updateMedium(oldmedium);
4553 }
4554
4555 mediumLock.release();
4556 multiLock.release();
4557
4558 /* Save modified registries, but skip this machine as it's the caller's
4559 * job to save its settings like all other settings changes. */
4560 mParent->i_unmarkRegistryModified(i_getId());
4561 mParent->i_saveModifiedRegistries();
4562
4563 return rc;
4564}
4565HRESULT Machine::getMedium(const com::Utf8Str &aName,
4566 LONG aControllerPort,
4567 LONG aDevice,
4568 ComPtr<IMedium> &aMedium)
4569{
4570 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4571 aName.c_str(), aControllerPort, aDevice));
4572
4573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4574
4575 aMedium = NULL;
4576
4577 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4578 aName,
4579 aControllerPort,
4580 aDevice);
4581 if (pAttach.isNull())
4582 return setError(VBOX_E_OBJECT_NOT_FOUND,
4583 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4584 aDevice, aControllerPort, aName.c_str());
4585
4586 aMedium = pAttach->i_getMedium();
4587
4588 return S_OK;
4589}
4590
4591HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4592{
4593 if (aSlot < RT_ELEMENTS(mSerialPorts))
4594 {
4595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4596 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4597 return S_OK;
4598 }
4599 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4600}
4601
4602HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4603{
4604 if (aSlot < RT_ELEMENTS(mParallelPorts))
4605 {
4606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4607 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4608 return S_OK;
4609 }
4610 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4611}
4612
4613
4614HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4615{
4616 /* Do not assert if slot is out of range, just return the advertised
4617 status. testdriver/vbox.py triggers this in logVmInfo. */
4618 if (aSlot >= mNetworkAdapters.size())
4619 return setError(E_INVALIDARG,
4620 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4621 aSlot, mNetworkAdapters.size());
4622
4623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4624
4625 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4626
4627 return S_OK;
4628}
4629
4630HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4631{
4632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4633
4634 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4635 size_t i = 0;
4636 for (settings::StringsMap::const_iterator
4637 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4638 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4639 ++it, ++i)
4640 aKeys[i] = it->first;
4641
4642 return S_OK;
4643}
4644
4645 /**
4646 * @note Locks this object for reading.
4647 */
4648HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4649 com::Utf8Str &aValue)
4650{
4651 /* start with nothing found */
4652 aValue = "";
4653
4654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4655
4656 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4657 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4658 // found:
4659 aValue = it->second; // source is a Utf8Str
4660
4661 /* return the result to caller (may be empty) */
4662 return S_OK;
4663}
4664
4665 /**
4666 * @note Locks mParent for writing + this object for writing.
4667 */
4668HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4669{
4670 /* Because control characters in aKey have caused problems in the settings
4671 * they are rejected unless the key should be deleted. */
4672 if (!aValue.isEmpty())
4673 {
4674 for (size_t i = 0; i < aKey.length(); ++i)
4675 {
4676 char ch = aKey[i];
4677 if (RTLocCIsCntrl(ch))
4678 return E_INVALIDARG;
4679 }
4680 }
4681
4682 Utf8Str strOldValue; // empty
4683
4684 // locking note: we only hold the read lock briefly to look up the old value,
4685 // then release it and call the onExtraCanChange callbacks. There is a small
4686 // chance of a race insofar as the callback might be called twice if two callers
4687 // change the same key at the same time, but that's a much better solution
4688 // than the deadlock we had here before. The actual changing of the extradata
4689 // is then performed under the write lock and race-free.
4690
4691 // look up the old value first; if nothing has changed then we need not do anything
4692 {
4693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4694
4695 // For snapshots don't even think about allowing changes, extradata
4696 // is global for a machine, so there is nothing snapshot specific.
4697 if (i_isSnapshotMachine())
4698 return setError(VBOX_E_INVALID_VM_STATE,
4699 tr("Cannot set extradata for a snapshot"));
4700
4701 // check if the right IMachine instance is used
4702 if (mData->mRegistered && !i_isSessionMachine())
4703 return setError(VBOX_E_INVALID_VM_STATE,
4704 tr("Cannot set extradata for an immutable machine"));
4705
4706 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4707 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4708 strOldValue = it->second;
4709 }
4710
4711 bool fChanged;
4712 if ((fChanged = (strOldValue != aValue)))
4713 {
4714 // ask for permission from all listeners outside the locks;
4715 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4716 // lock to copy the list of callbacks to invoke
4717 Bstr bstrError;
4718 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4719 {
4720 const char *sep = bstrError.isEmpty() ? "" : ": ";
4721 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4722 return setError(E_ACCESSDENIED,
4723 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4724 aKey.c_str(),
4725 aValue.c_str(),
4726 sep,
4727 bstrError.raw());
4728 }
4729
4730 // data is changing and change not vetoed: then write it out under the lock
4731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4732
4733 if (aValue.isEmpty())
4734 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4735 else
4736 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4737 // creates a new key if needed
4738
4739 bool fNeedsGlobalSaveSettings = false;
4740 // This saving of settings is tricky: there is no "old state" for the
4741 // extradata items at all (unlike all other settings), so the old/new
4742 // settings comparison would give a wrong result!
4743 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4744
4745 if (fNeedsGlobalSaveSettings)
4746 {
4747 // save the global settings; for that we should hold only the VirtualBox lock
4748 alock.release();
4749 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4750 mParent->i_saveSettings();
4751 }
4752 }
4753
4754 // fire notification outside the lock
4755 if (fChanged)
4756 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4757
4758 return S_OK;
4759}
4760
4761HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4762{
4763 aProgress = NULL;
4764 NOREF(aSettingsFilePath);
4765 ReturnComNotImplemented();
4766}
4767
4768HRESULT Machine::saveSettings()
4769{
4770 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4771
4772 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4773 if (FAILED(rc)) return rc;
4774
4775 /* the settings file path may never be null */
4776 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4777
4778 /* save all VM data excluding snapshots */
4779 bool fNeedsGlobalSaveSettings = false;
4780 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4781 mlock.release();
4782
4783 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4784 {
4785 // save the global settings; for that we should hold only the VirtualBox lock
4786 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4787 rc = mParent->i_saveSettings();
4788 }
4789
4790 return rc;
4791}
4792
4793
4794HRESULT Machine::discardSettings()
4795{
4796 /*
4797 * We need to take the machine list lock here as well as the machine one
4798 * or we'll get into trouble should any media stuff require rolling back.
4799 *
4800 * Details:
4801 *
4802 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4803 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4804 * 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]
4805 * 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
4806 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4807 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4808 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4809 * 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
4810 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4811 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4812 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4813 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4814 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4815 * 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]
4816 * 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] (*)
4817 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4818 * 0:005> k
4819 * # Child-SP RetAddr Call Site
4820 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4821 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4822 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4823 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4824 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4825 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4826 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4827 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4828 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4829 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4830 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4831 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4832 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4833 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4834 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4835 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4836 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4837 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4838 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4839 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4840 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4841 *
4842 */
4843 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4845
4846 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4847 if (FAILED(rc)) return rc;
4848
4849 /*
4850 * during this rollback, the session will be notified if data has
4851 * been actually changed
4852 */
4853 i_rollback(true /* aNotify */);
4854
4855 return S_OK;
4856}
4857
4858/** @note Locks objects! */
4859HRESULT Machine::unregister(AutoCaller &autoCaller,
4860 CleanupMode_T aCleanupMode,
4861 std::vector<ComPtr<IMedium> > &aMedia)
4862{
4863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4864
4865 Guid id(i_getId());
4866
4867 if (mData->mSession.mState != SessionState_Unlocked)
4868 return setError(VBOX_E_INVALID_OBJECT_STATE,
4869 tr("Cannot unregister the machine '%s' while it is locked"),
4870 mUserData->s.strName.c_str());
4871
4872 // wait for state dependents to drop to zero
4873 i_ensureNoStateDependencies();
4874
4875 if (!mData->mAccessible)
4876 {
4877 // inaccessible machines can only be unregistered; uninitialize ourselves
4878 // here because currently there may be no unregistered that are inaccessible
4879 // (this state combination is not supported). Note releasing the caller and
4880 // leaving the lock before calling uninit()
4881 alock.release();
4882 autoCaller.release();
4883
4884 uninit();
4885
4886 mParent->i_unregisterMachine(this, id);
4887 // calls VirtualBox::i_saveSettings()
4888
4889 return S_OK;
4890 }
4891
4892 HRESULT rc = S_OK;
4893 mData->llFilesToDelete.clear();
4894
4895 if (!mSSData->strStateFilePath.isEmpty())
4896 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4897
4898 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4899 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4900 mData->llFilesToDelete.push_back(strNVRAMFile);
4901
4902 // This list collects the medium objects from all medium attachments
4903 // which we will detach from the machine and its snapshots, in a specific
4904 // order which allows for closing all media without getting "media in use"
4905 // errors, simply by going through the list from the front to the back:
4906 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4907 // and must be closed before the parent media from the snapshots, or closing the parents
4908 // will fail because they still have children);
4909 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4910 // the root ("first") snapshot of the machine.
4911 MediaList llMedia;
4912
4913 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4914 && mMediumAttachments->size()
4915 )
4916 {
4917 // we have media attachments: detach them all and add the Medium objects to our list
4918 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4919 }
4920
4921 if (mData->mFirstSnapshot)
4922 {
4923 // add the media from the medium attachments of the snapshots to llMedia
4924 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4925 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4926 // into the children first
4927
4928 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4929 MachineState_T oldState = mData->mMachineState;
4930 mData->mMachineState = MachineState_DeletingSnapshot;
4931
4932 // make a copy of the first snapshot reference so the refcount does not
4933 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4934 // (would hang due to the AutoCaller voodoo)
4935 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4936
4937 // GO!
4938 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4939
4940 mData->mMachineState = oldState;
4941 }
4942
4943 if (FAILED(rc))
4944 {
4945 i_rollbackMedia();
4946 return rc;
4947 }
4948
4949 // commit all the media changes made above
4950 i_commitMedia();
4951
4952 mData->mRegistered = false;
4953
4954 // machine lock no longer needed
4955 alock.release();
4956
4957 /* Make sure that the settings of the current VM are not saved, because
4958 * they are rather crippled at this point to meet the cleanup expectations
4959 * and there's no point destroying the VM config on disk just because. */
4960 mParent->i_unmarkRegistryModified(id);
4961
4962 // return media to caller
4963 aMedia.resize(llMedia.size());
4964 size_t i = 0;
4965 for (MediaList::const_iterator
4966 it = llMedia.begin();
4967 it != llMedia.end();
4968 ++it, ++i)
4969 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4970
4971 mParent->i_unregisterMachine(this, id);
4972 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4973
4974 return S_OK;
4975}
4976
4977/**
4978 * Task record for deleting a machine config.
4979 */
4980class Machine::DeleteConfigTask
4981 : public Machine::Task
4982{
4983public:
4984 DeleteConfigTask(Machine *m,
4985 Progress *p,
4986 const Utf8Str &t,
4987 const RTCList<ComPtr<IMedium> > &llMediums,
4988 const StringsList &llFilesToDelete)
4989 : Task(m, p, t),
4990 m_llMediums(llMediums),
4991 m_llFilesToDelete(llFilesToDelete)
4992 {}
4993
4994private:
4995 void handler()
4996 {
4997 try
4998 {
4999 m_pMachine->i_deleteConfigHandler(*this);
5000 }
5001 catch (...)
5002 {
5003 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5004 }
5005 }
5006
5007 RTCList<ComPtr<IMedium> > m_llMediums;
5008 StringsList m_llFilesToDelete;
5009
5010 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5011};
5012
5013/**
5014 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5015 * SessionMachine::taskHandler().
5016 *
5017 * @note Locks this object for writing.
5018 *
5019 * @param task
5020 * @return
5021 */
5022void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5023{
5024 LogFlowThisFuncEnter();
5025
5026 AutoCaller autoCaller(this);
5027 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5028 if (FAILED(autoCaller.rc()))
5029 {
5030 /* we might have been uninitialized because the session was accidentally
5031 * closed by the client, so don't assert */
5032 HRESULT rc = setError(E_FAIL,
5033 tr("The session has been accidentally closed"));
5034 task.m_pProgress->i_notifyComplete(rc);
5035 LogFlowThisFuncLeave();
5036 return;
5037 }
5038
5039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5040
5041 HRESULT rc = S_OK;
5042
5043 try
5044 {
5045 ULONG uLogHistoryCount = 3;
5046 ComPtr<ISystemProperties> systemProperties;
5047 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5048 if (FAILED(rc)) throw rc;
5049
5050 if (!systemProperties.isNull())
5051 {
5052 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5053 if (FAILED(rc)) throw rc;
5054 }
5055
5056 MachineState_T oldState = mData->mMachineState;
5057 i_setMachineState(MachineState_SettingUp);
5058 alock.release();
5059 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5060 {
5061 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5062 {
5063 AutoCaller mac(pMedium);
5064 if (FAILED(mac.rc())) throw mac.rc();
5065 Utf8Str strLocation = pMedium->i_getLocationFull();
5066 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5067 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5068 if (FAILED(rc)) throw rc;
5069 }
5070 if (pMedium->i_isMediumFormatFile())
5071 {
5072 ComPtr<IProgress> pProgress2;
5073 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5074 if (FAILED(rc)) throw rc;
5075 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5076 if (FAILED(rc)) throw rc;
5077 }
5078
5079 /* Close the medium, deliberately without checking the return
5080 * code, and without leaving any trace in the error info, as
5081 * a failure here is a very minor issue, which shouldn't happen
5082 * as above we even managed to delete the medium. */
5083 {
5084 ErrorInfoKeeper eik;
5085 pMedium->Close();
5086 }
5087 }
5088 i_setMachineState(oldState);
5089 alock.acquire();
5090
5091 // delete the files pushed on the task list by Machine::Delete()
5092 // (this includes saved states of the machine and snapshots and
5093 // medium storage files from the IMedium list passed in, and the
5094 // machine XML file)
5095 for (StringsList::const_iterator
5096 it = task.m_llFilesToDelete.begin();
5097 it != task.m_llFilesToDelete.end();
5098 ++it)
5099 {
5100 const Utf8Str &strFile = *it;
5101 LogFunc(("Deleting file %s\n", strFile.c_str()));
5102 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5103 if (FAILED(rc)) throw rc;
5104
5105 int vrc = RTFileDelete(strFile.c_str());
5106 if (RT_FAILURE(vrc))
5107 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5108 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5109 }
5110
5111 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5112 if (FAILED(rc)) throw rc;
5113
5114 /* delete the settings only when the file actually exists */
5115 if (mData->pMachineConfigFile->fileExists())
5116 {
5117 /* Delete any backup or uncommitted XML files. Ignore failures.
5118 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5119 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5120 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5121 RTFileDelete(otherXml.c_str());
5122 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5123 RTFileDelete(otherXml.c_str());
5124
5125 /* delete the Logs folder, nothing important should be left
5126 * there (we don't check for errors because the user might have
5127 * some private files there that we don't want to delete) */
5128 Utf8Str logFolder;
5129 getLogFolder(logFolder);
5130 Assert(logFolder.length());
5131 if (RTDirExists(logFolder.c_str()))
5132 {
5133 /* Delete all VBox.log[.N] files from the Logs folder
5134 * (this must be in sync with the rotation logic in
5135 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5136 * files that may have been created by the GUI. */
5137 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5138 RTFileDelete(log.c_str());
5139 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5140 RTFileDelete(log.c_str());
5141 for (ULONG i = uLogHistoryCount; i > 0; i--)
5142 {
5143 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5144 RTFileDelete(log.c_str());
5145 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5146 RTFileDelete(log.c_str());
5147 }
5148#if defined(RT_OS_WINDOWS)
5149 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5150 RTFileDelete(log.c_str());
5151 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5152 RTFileDelete(log.c_str());
5153#endif
5154
5155 RTDirRemove(logFolder.c_str());
5156 }
5157
5158 /* delete the Snapshots folder, nothing important should be left
5159 * there (we don't check for errors because the user might have
5160 * some private files there that we don't want to delete) */
5161 Utf8Str strFullSnapshotFolder;
5162 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5163 Assert(!strFullSnapshotFolder.isEmpty());
5164 if (RTDirExists(strFullSnapshotFolder.c_str()))
5165 RTDirRemove(strFullSnapshotFolder.c_str());
5166
5167 // delete the directory that contains the settings file, but only
5168 // if it matches the VM name
5169 Utf8Str settingsDir;
5170 if (i_isInOwnDir(&settingsDir))
5171 RTDirRemove(settingsDir.c_str());
5172 }
5173
5174 alock.release();
5175
5176 mParent->i_saveModifiedRegistries();
5177 }
5178 catch (HRESULT aRC) { rc = aRC; }
5179
5180 task.m_pProgress->i_notifyComplete(rc);
5181
5182 LogFlowThisFuncLeave();
5183}
5184
5185HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5186{
5187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5188
5189 HRESULT rc = i_checkStateDependency(MutableStateDep);
5190 if (FAILED(rc)) return rc;
5191
5192 if (mData->mRegistered)
5193 return setError(VBOX_E_INVALID_VM_STATE,
5194 tr("Cannot delete settings of a registered machine"));
5195
5196 // collect files to delete
5197 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5198 // machine config file
5199 if (mData->pMachineConfigFile->fileExists())
5200 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5201 // backup of machine config file
5202 Utf8Str strTmp(mData->m_strConfigFileFull);
5203 strTmp.append("-prev");
5204 if (RTFileExists(strTmp.c_str()))
5205 llFilesToDelete.push_back(strTmp);
5206
5207 RTCList<ComPtr<IMedium> > llMediums;
5208 for (size_t i = 0; i < aMedia.size(); ++i)
5209 {
5210 IMedium *pIMedium(aMedia[i]);
5211 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5212 if (pMedium.isNull())
5213 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5214 SafeArray<BSTR> ids;
5215 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5216 if (FAILED(rc)) return rc;
5217 /* At this point the medium should not have any back references
5218 * anymore. If it has it is attached to another VM and *must* not
5219 * deleted. */
5220 if (ids.size() < 1)
5221 llMediums.append(pMedium);
5222 }
5223
5224 ComObjPtr<Progress> pProgress;
5225 pProgress.createObject();
5226 rc = pProgress->init(i_getVirtualBox(),
5227 static_cast<IMachine*>(this) /* aInitiator */,
5228 tr("Deleting files"),
5229 true /* fCancellable */,
5230 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5231 tr("Collecting file inventory"));
5232 if (FAILED(rc))
5233 return rc;
5234
5235 /* create and start the task on a separate thread (note that it will not
5236 * start working until we release alock) */
5237 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5238 rc = pTask->createThread();
5239 pTask = NULL;
5240 if (FAILED(rc))
5241 return rc;
5242
5243 pProgress.queryInterfaceTo(aProgress.asOutParam());
5244
5245 LogFlowFuncLeave();
5246
5247 return S_OK;
5248}
5249
5250HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5251{
5252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5253
5254 ComObjPtr<Snapshot> pSnapshot;
5255 HRESULT rc;
5256
5257 if (aNameOrId.isEmpty())
5258 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5259 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5260 else
5261 {
5262 Guid uuid(aNameOrId);
5263 if (uuid.isValid())
5264 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5265 else
5266 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5267 }
5268 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5269
5270 return rc;
5271}
5272
5273HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5274 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5275{
5276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5277
5278 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5279 if (FAILED(rc)) return rc;
5280
5281 ComObjPtr<SharedFolder> sharedFolder;
5282 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5283 if (SUCCEEDED(rc))
5284 return setError(VBOX_E_OBJECT_IN_USE,
5285 tr("Shared folder named '%s' already exists"),
5286 aName.c_str());
5287
5288 sharedFolder.createObject();
5289 rc = sharedFolder->init(i_getMachine(),
5290 aName,
5291 aHostPath,
5292 !!aWritable,
5293 !!aAutomount,
5294 aAutoMountPoint,
5295 true /* fFailOnError */);
5296 if (FAILED(rc)) return rc;
5297
5298 i_setModified(IsModified_SharedFolders);
5299 mHWData.backup();
5300 mHWData->mSharedFolders.push_back(sharedFolder);
5301
5302 /* inform the direct session if any */
5303 alock.release();
5304 i_onSharedFolderChange();
5305
5306 return S_OK;
5307}
5308
5309HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5310{
5311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5312
5313 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5314 if (FAILED(rc)) return rc;
5315
5316 ComObjPtr<SharedFolder> sharedFolder;
5317 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5318 if (FAILED(rc)) return rc;
5319
5320 i_setModified(IsModified_SharedFolders);
5321 mHWData.backup();
5322 mHWData->mSharedFolders.remove(sharedFolder);
5323
5324 /* inform the direct session if any */
5325 alock.release();
5326 i_onSharedFolderChange();
5327
5328 return S_OK;
5329}
5330
5331HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5332{
5333 /* start with No */
5334 *aCanShow = FALSE;
5335
5336 ComPtr<IInternalSessionControl> directControl;
5337 {
5338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5339
5340 if (mData->mSession.mState != SessionState_Locked)
5341 return setError(VBOX_E_INVALID_VM_STATE,
5342 tr("Machine is not locked for session (session state: %s)"),
5343 Global::stringifySessionState(mData->mSession.mState));
5344
5345 if (mData->mSession.mLockType == LockType_VM)
5346 directControl = mData->mSession.mDirectControl;
5347 }
5348
5349 /* ignore calls made after #OnSessionEnd() is called */
5350 if (!directControl)
5351 return S_OK;
5352
5353 LONG64 dummy;
5354 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5355}
5356
5357HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5358{
5359 ComPtr<IInternalSessionControl> directControl;
5360 {
5361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5362
5363 if (mData->mSession.mState != SessionState_Locked)
5364 return setError(E_FAIL,
5365 tr("Machine is not locked for session (session state: %s)"),
5366 Global::stringifySessionState(mData->mSession.mState));
5367
5368 if (mData->mSession.mLockType == LockType_VM)
5369 directControl = mData->mSession.mDirectControl;
5370 }
5371
5372 /* ignore calls made after #OnSessionEnd() is called */
5373 if (!directControl)
5374 return S_OK;
5375
5376 BOOL dummy;
5377 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5378}
5379
5380#ifdef VBOX_WITH_GUEST_PROPS
5381/**
5382 * Look up a guest property in VBoxSVC's internal structures.
5383 */
5384HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5385 com::Utf8Str &aValue,
5386 LONG64 *aTimestamp,
5387 com::Utf8Str &aFlags) const
5388{
5389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5390
5391 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5392 if (it != mHWData->mGuestProperties.end())
5393 {
5394 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5395 aValue = it->second.strValue;
5396 *aTimestamp = it->second.mTimestamp;
5397 GuestPropWriteFlags(it->second.mFlags, szFlags);
5398 aFlags = Utf8Str(szFlags);
5399 }
5400
5401 return S_OK;
5402}
5403
5404/**
5405 * Query the VM that a guest property belongs to for the property.
5406 * @returns E_ACCESSDENIED if the VM process is not available or not
5407 * currently handling queries and the lookup should then be done in
5408 * VBoxSVC.
5409 */
5410HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5411 com::Utf8Str &aValue,
5412 LONG64 *aTimestamp,
5413 com::Utf8Str &aFlags) const
5414{
5415 HRESULT rc = S_OK;
5416 Bstr bstrValue;
5417 Bstr bstrFlags;
5418
5419 ComPtr<IInternalSessionControl> directControl;
5420 {
5421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
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 rc = E_ACCESSDENIED;
5429 else
5430 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5431 0 /* accessMode */,
5432 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5433
5434 aValue = bstrValue;
5435 aFlags = bstrFlags;
5436
5437 return rc;
5438}
5439#endif // VBOX_WITH_GUEST_PROPS
5440
5441HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5442 com::Utf8Str &aValue,
5443 LONG64 *aTimestamp,
5444 com::Utf8Str &aFlags)
5445{
5446#ifndef VBOX_WITH_GUEST_PROPS
5447 ReturnComNotImplemented();
5448#else // VBOX_WITH_GUEST_PROPS
5449
5450 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5451
5452 if (rc == E_ACCESSDENIED)
5453 /* The VM is not running or the service is not (yet) accessible */
5454 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5455 return rc;
5456#endif // VBOX_WITH_GUEST_PROPS
5457}
5458
5459HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5460{
5461 LONG64 dummyTimestamp;
5462 com::Utf8Str dummyFlags;
5463 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5464 return rc;
5465
5466}
5467HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5468{
5469 com::Utf8Str dummyFlags;
5470 com::Utf8Str dummyValue;
5471 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5472 return rc;
5473}
5474
5475#ifdef VBOX_WITH_GUEST_PROPS
5476/**
5477 * Set a guest property in VBoxSVC's internal structures.
5478 */
5479HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5480 const com::Utf8Str &aFlags, bool fDelete)
5481{
5482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5483 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5484 if (FAILED(rc)) return rc;
5485
5486 try
5487 {
5488 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5489 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5490 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5491
5492 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5493 if (it == mHWData->mGuestProperties.end())
5494 {
5495 if (!fDelete)
5496 {
5497 i_setModified(IsModified_MachineData);
5498 mHWData.backupEx();
5499
5500 RTTIMESPEC time;
5501 HWData::GuestProperty prop;
5502 prop.strValue = Bstr(aValue).raw();
5503 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5504 prop.mFlags = fFlags;
5505 mHWData->mGuestProperties[aName] = prop;
5506 }
5507 }
5508 else
5509 {
5510 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5511 {
5512 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5513 }
5514 else
5515 {
5516 i_setModified(IsModified_MachineData);
5517 mHWData.backupEx();
5518
5519 /* The backupEx() operation invalidates our iterator,
5520 * so get a new one. */
5521 it = mHWData->mGuestProperties.find(aName);
5522 Assert(it != mHWData->mGuestProperties.end());
5523
5524 if (!fDelete)
5525 {
5526 RTTIMESPEC time;
5527 it->second.strValue = aValue;
5528 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5529 it->second.mFlags = fFlags;
5530 }
5531 else
5532 mHWData->mGuestProperties.erase(it);
5533 }
5534 }
5535
5536 if (SUCCEEDED(rc))
5537 {
5538 alock.release();
5539
5540 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5541 }
5542 }
5543 catch (std::bad_alloc &)
5544 {
5545 rc = E_OUTOFMEMORY;
5546 }
5547
5548 return rc;
5549}
5550
5551/**
5552 * Set a property on the VM that that property belongs to.
5553 * @returns E_ACCESSDENIED if the VM process is not available or not
5554 * currently handling queries and the setting should then be done in
5555 * VBoxSVC.
5556 */
5557HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5558 const com::Utf8Str &aFlags, bool fDelete)
5559{
5560 HRESULT rc;
5561
5562 try
5563 {
5564 ComPtr<IInternalSessionControl> directControl;
5565 {
5566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5567 if (mData->mSession.mLockType == LockType_VM)
5568 directControl = mData->mSession.mDirectControl;
5569 }
5570
5571 Bstr dummy1; /* will not be changed (setter) */
5572 Bstr dummy2; /* will not be changed (setter) */
5573 LONG64 dummy64;
5574 if (!directControl)
5575 rc = E_ACCESSDENIED;
5576 else
5577 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5578 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5579 fDelete ? 2 : 1 /* accessMode */,
5580 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5581 }
5582 catch (std::bad_alloc &)
5583 {
5584 rc = E_OUTOFMEMORY;
5585 }
5586
5587 return rc;
5588}
5589#endif // VBOX_WITH_GUEST_PROPS
5590
5591HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5592 const com::Utf8Str &aFlags)
5593{
5594#ifndef VBOX_WITH_GUEST_PROPS
5595 ReturnComNotImplemented();
5596#else // VBOX_WITH_GUEST_PROPS
5597 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5598 if (rc == E_ACCESSDENIED)
5599 /* The VM is not running or the service is not (yet) accessible */
5600 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5601 return rc;
5602#endif // VBOX_WITH_GUEST_PROPS
5603}
5604
5605HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5606{
5607 return setGuestProperty(aProperty, aValue, "");
5608}
5609
5610HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5611{
5612#ifndef VBOX_WITH_GUEST_PROPS
5613 ReturnComNotImplemented();
5614#else // VBOX_WITH_GUEST_PROPS
5615 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5616 if (rc == E_ACCESSDENIED)
5617 /* The VM is not running or the service is not (yet) accessible */
5618 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5619 return rc;
5620#endif // VBOX_WITH_GUEST_PROPS
5621}
5622
5623#ifdef VBOX_WITH_GUEST_PROPS
5624/**
5625 * Enumerate the guest properties in VBoxSVC's internal structures.
5626 */
5627HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5628 std::vector<com::Utf8Str> &aNames,
5629 std::vector<com::Utf8Str> &aValues,
5630 std::vector<LONG64> &aTimestamps,
5631 std::vector<com::Utf8Str> &aFlags)
5632{
5633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5634 Utf8Str strPatterns(aPatterns);
5635
5636 /*
5637 * Look for matching patterns and build up a list.
5638 */
5639 HWData::GuestPropertyMap propMap;
5640 for (HWData::GuestPropertyMap::const_iterator
5641 it = mHWData->mGuestProperties.begin();
5642 it != mHWData->mGuestProperties.end();
5643 ++it)
5644 {
5645 if ( strPatterns.isEmpty()
5646 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5647 RTSTR_MAX,
5648 it->first.c_str(),
5649 RTSTR_MAX,
5650 NULL)
5651 )
5652 propMap.insert(*it);
5653 }
5654
5655 alock.release();
5656
5657 /*
5658 * And build up the arrays for returning the property information.
5659 */
5660 size_t cEntries = propMap.size();
5661
5662 aNames.resize(cEntries);
5663 aValues.resize(cEntries);
5664 aTimestamps.resize(cEntries);
5665 aFlags.resize(cEntries);
5666
5667 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5668 size_t i = 0;
5669 for (HWData::GuestPropertyMap::const_iterator
5670 it = propMap.begin();
5671 it != propMap.end();
5672 ++it, ++i)
5673 {
5674 aNames[i] = it->first;
5675 aValues[i] = it->second.strValue;
5676 aTimestamps[i] = it->second.mTimestamp;
5677 GuestPropWriteFlags(it->second.mFlags, szFlags);
5678 aFlags[i] = Utf8Str(szFlags);
5679 }
5680
5681 return S_OK;
5682}
5683
5684/**
5685 * Enumerate the properties managed by a VM.
5686 * @returns E_ACCESSDENIED if the VM process is not available or not
5687 * currently handling queries and the setting should then be done in
5688 * VBoxSVC.
5689 */
5690HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5691 std::vector<com::Utf8Str> &aNames,
5692 std::vector<com::Utf8Str> &aValues,
5693 std::vector<LONG64> &aTimestamps,
5694 std::vector<com::Utf8Str> &aFlags)
5695{
5696 HRESULT rc;
5697 ComPtr<IInternalSessionControl> directControl;
5698 {
5699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5700 if (mData->mSession.mLockType == LockType_VM)
5701 directControl = mData->mSession.mDirectControl;
5702 }
5703
5704 com::SafeArray<BSTR> bNames;
5705 com::SafeArray<BSTR> bValues;
5706 com::SafeArray<LONG64> bTimestamps;
5707 com::SafeArray<BSTR> bFlags;
5708
5709 if (!directControl)
5710 rc = E_ACCESSDENIED;
5711 else
5712 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5713 ComSafeArrayAsOutParam(bNames),
5714 ComSafeArrayAsOutParam(bValues),
5715 ComSafeArrayAsOutParam(bTimestamps),
5716 ComSafeArrayAsOutParam(bFlags));
5717 size_t i;
5718 aNames.resize(bNames.size());
5719 for (i = 0; i < bNames.size(); ++i)
5720 aNames[i] = Utf8Str(bNames[i]);
5721 aValues.resize(bValues.size());
5722 for (i = 0; i < bValues.size(); ++i)
5723 aValues[i] = Utf8Str(bValues[i]);
5724 aTimestamps.resize(bTimestamps.size());
5725 for (i = 0; i < bTimestamps.size(); ++i)
5726 aTimestamps[i] = bTimestamps[i];
5727 aFlags.resize(bFlags.size());
5728 for (i = 0; i < bFlags.size(); ++i)
5729 aFlags[i] = Utf8Str(bFlags[i]);
5730
5731 return rc;
5732}
5733#endif // VBOX_WITH_GUEST_PROPS
5734HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5735 std::vector<com::Utf8Str> &aNames,
5736 std::vector<com::Utf8Str> &aValues,
5737 std::vector<LONG64> &aTimestamps,
5738 std::vector<com::Utf8Str> &aFlags)
5739{
5740#ifndef VBOX_WITH_GUEST_PROPS
5741 ReturnComNotImplemented();
5742#else // VBOX_WITH_GUEST_PROPS
5743
5744 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5745
5746 if (rc == E_ACCESSDENIED)
5747 /* The VM is not running or the service is not (yet) accessible */
5748 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5749 return rc;
5750#endif // VBOX_WITH_GUEST_PROPS
5751}
5752
5753HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5754 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5755{
5756 MediumAttachmentList atts;
5757
5758 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5759 if (FAILED(rc)) return rc;
5760
5761 aMediumAttachments.resize(atts.size());
5762 size_t i = 0;
5763 for (MediumAttachmentList::const_iterator
5764 it = atts.begin();
5765 it != atts.end();
5766 ++it, ++i)
5767 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5768
5769 return S_OK;
5770}
5771
5772HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5773 LONG aControllerPort,
5774 LONG aDevice,
5775 ComPtr<IMediumAttachment> &aAttachment)
5776{
5777 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5778 aName.c_str(), aControllerPort, aDevice));
5779
5780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5781
5782 aAttachment = NULL;
5783
5784 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5785 aName,
5786 aControllerPort,
5787 aDevice);
5788 if (pAttach.isNull())
5789 return setError(VBOX_E_OBJECT_NOT_FOUND,
5790 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5791 aDevice, aControllerPort, aName.c_str());
5792
5793 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5794
5795 return S_OK;
5796}
5797
5798
5799HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5800 StorageBus_T aConnectionType,
5801 ComPtr<IStorageController> &aController)
5802{
5803 if ( (aConnectionType <= StorageBus_Null)
5804 || (aConnectionType > StorageBus_VirtioSCSI))
5805 return setError(E_INVALIDARG,
5806 tr("Invalid connection type: %d"),
5807 aConnectionType);
5808
5809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5810
5811 HRESULT rc = i_checkStateDependency(MutableStateDep);
5812 if (FAILED(rc)) return rc;
5813
5814 /* try to find one with the name first. */
5815 ComObjPtr<StorageController> ctrl;
5816
5817 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5818 if (SUCCEEDED(rc))
5819 return setError(VBOX_E_OBJECT_IN_USE,
5820 tr("Storage controller named '%s' already exists"),
5821 aName.c_str());
5822
5823 ctrl.createObject();
5824
5825 /* get a new instance number for the storage controller */
5826 ULONG ulInstance = 0;
5827 bool fBootable = true;
5828 for (StorageControllerList::const_iterator
5829 it = mStorageControllers->begin();
5830 it != mStorageControllers->end();
5831 ++it)
5832 {
5833 if ((*it)->i_getStorageBus() == aConnectionType)
5834 {
5835 ULONG ulCurInst = (*it)->i_getInstance();
5836
5837 if (ulCurInst >= ulInstance)
5838 ulInstance = ulCurInst + 1;
5839
5840 /* Only one controller of each type can be marked as bootable. */
5841 if ((*it)->i_getBootable())
5842 fBootable = false;
5843 }
5844 }
5845
5846 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5847 if (FAILED(rc)) return rc;
5848
5849 i_setModified(IsModified_Storage);
5850 mStorageControllers.backup();
5851 mStorageControllers->push_back(ctrl);
5852
5853 ctrl.queryInterfaceTo(aController.asOutParam());
5854
5855 /* inform the direct session if any */
5856 alock.release();
5857 i_onStorageControllerChange(i_getId(), aName);
5858
5859 return S_OK;
5860}
5861
5862HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5863 ComPtr<IStorageController> &aStorageController)
5864{
5865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5866
5867 ComObjPtr<StorageController> ctrl;
5868
5869 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5870 if (SUCCEEDED(rc))
5871 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5872
5873 return rc;
5874}
5875
5876HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5877 ULONG aInstance,
5878 ComPtr<IStorageController> &aStorageController)
5879{
5880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5881
5882 for (StorageControllerList::const_iterator
5883 it = mStorageControllers->begin();
5884 it != mStorageControllers->end();
5885 ++it)
5886 {
5887 if ( (*it)->i_getStorageBus() == aConnectionType
5888 && (*it)->i_getInstance() == aInstance)
5889 {
5890 (*it).queryInterfaceTo(aStorageController.asOutParam());
5891 return S_OK;
5892 }
5893 }
5894
5895 return setError(VBOX_E_OBJECT_NOT_FOUND,
5896 tr("Could not find a storage controller with instance number '%lu'"),
5897 aInstance);
5898}
5899
5900HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5901{
5902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5903
5904 HRESULT rc = i_checkStateDependency(MutableStateDep);
5905 if (FAILED(rc)) return rc;
5906
5907 ComObjPtr<StorageController> ctrl;
5908
5909 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5910 if (SUCCEEDED(rc))
5911 {
5912 /* Ensure that only one controller of each type is marked as bootable. */
5913 if (aBootable == TRUE)
5914 {
5915 for (StorageControllerList::const_iterator
5916 it = mStorageControllers->begin();
5917 it != mStorageControllers->end();
5918 ++it)
5919 {
5920 ComObjPtr<StorageController> aCtrl = (*it);
5921
5922 if ( (aCtrl->i_getName() != aName)
5923 && aCtrl->i_getBootable() == TRUE
5924 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5925 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5926 {
5927 aCtrl->i_setBootable(FALSE);
5928 break;
5929 }
5930 }
5931 }
5932
5933 if (SUCCEEDED(rc))
5934 {
5935 ctrl->i_setBootable(aBootable);
5936 i_setModified(IsModified_Storage);
5937 }
5938 }
5939
5940 if (SUCCEEDED(rc))
5941 {
5942 /* inform the direct session if any */
5943 alock.release();
5944 i_onStorageControllerChange(i_getId(), aName);
5945 }
5946
5947 return rc;
5948}
5949
5950HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5951{
5952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5953
5954 HRESULT rc = i_checkStateDependency(MutableStateDep);
5955 if (FAILED(rc)) return rc;
5956
5957 ComObjPtr<StorageController> ctrl;
5958 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5959 if (FAILED(rc)) return rc;
5960
5961 MediumAttachmentList llDetachedAttachments;
5962 {
5963 /* find all attached devices to the appropriate storage controller and detach them all */
5964 // make a temporary list because detachDevice invalidates iterators into
5965 // mMediumAttachments
5966 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5967
5968 for (MediumAttachmentList::const_iterator
5969 it = llAttachments2.begin();
5970 it != llAttachments2.end();
5971 ++it)
5972 {
5973 MediumAttachment *pAttachTemp = *it;
5974
5975 AutoCaller localAutoCaller(pAttachTemp);
5976 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5977
5978 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5979
5980 if (pAttachTemp->i_getControllerName() == aName)
5981 {
5982 llDetachedAttachments.push_back(pAttachTemp);
5983 rc = i_detachDevice(pAttachTemp, alock, NULL);
5984 if (FAILED(rc)) return rc;
5985 }
5986 }
5987 }
5988
5989 /* send event about detached devices before removing parent controller */
5990 for (MediumAttachmentList::const_iterator
5991 it = llDetachedAttachments.begin();
5992 it != llDetachedAttachments.end();
5993 ++it)
5994 {
5995 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5996 }
5997
5998 /* We can remove it now. */
5999 i_setModified(IsModified_Storage);
6000 mStorageControllers.backup();
6001
6002 ctrl->i_unshare();
6003
6004 mStorageControllers->remove(ctrl);
6005
6006 /* inform the direct session if any */
6007 alock.release();
6008 i_onStorageControllerChange(i_getId(), aName);
6009
6010 return S_OK;
6011}
6012
6013HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6014 ComPtr<IUSBController> &aController)
6015{
6016 if ( (aType <= USBControllerType_Null)
6017 || (aType >= USBControllerType_Last))
6018 return setError(E_INVALIDARG,
6019 tr("Invalid USB controller type: %d"),
6020 aType);
6021
6022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6023
6024 HRESULT rc = i_checkStateDependency(MutableStateDep);
6025 if (FAILED(rc)) return rc;
6026
6027 /* try to find one with the same type first. */
6028 ComObjPtr<USBController> ctrl;
6029
6030 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6031 if (SUCCEEDED(rc))
6032 return setError(VBOX_E_OBJECT_IN_USE,
6033 tr("USB controller named '%s' already exists"),
6034 aName.c_str());
6035
6036 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6037 ULONG maxInstances;
6038 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6039 if (FAILED(rc))
6040 return rc;
6041
6042 ULONG cInstances = i_getUSBControllerCountByType(aType);
6043 if (cInstances >= maxInstances)
6044 return setError(E_INVALIDARG,
6045 tr("Too many USB controllers of this type"));
6046
6047 ctrl.createObject();
6048
6049 rc = ctrl->init(this, aName, aType);
6050 if (FAILED(rc)) return rc;
6051
6052 i_setModified(IsModified_USB);
6053 mUSBControllers.backup();
6054 mUSBControllers->push_back(ctrl);
6055
6056 ctrl.queryInterfaceTo(aController.asOutParam());
6057
6058 /* inform the direct session if any */
6059 alock.release();
6060 i_onUSBControllerChange();
6061
6062 return S_OK;
6063}
6064
6065HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6066{
6067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6068
6069 ComObjPtr<USBController> ctrl;
6070
6071 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6072 if (SUCCEEDED(rc))
6073 ctrl.queryInterfaceTo(aController.asOutParam());
6074
6075 return rc;
6076}
6077
6078HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6079 ULONG *aControllers)
6080{
6081 if ( (aType <= USBControllerType_Null)
6082 || (aType >= USBControllerType_Last))
6083 return setError(E_INVALIDARG,
6084 tr("Invalid USB controller type: %d"),
6085 aType);
6086
6087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6088
6089 ComObjPtr<USBController> ctrl;
6090
6091 *aControllers = i_getUSBControllerCountByType(aType);
6092
6093 return S_OK;
6094}
6095
6096HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6097{
6098
6099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6100
6101 HRESULT rc = i_checkStateDependency(MutableStateDep);
6102 if (FAILED(rc)) return rc;
6103
6104 ComObjPtr<USBController> ctrl;
6105 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6106 if (FAILED(rc)) return rc;
6107
6108 i_setModified(IsModified_USB);
6109 mUSBControllers.backup();
6110
6111 ctrl->i_unshare();
6112
6113 mUSBControllers->remove(ctrl);
6114
6115 /* inform the direct session if any */
6116 alock.release();
6117 i_onUSBControllerChange();
6118
6119 return S_OK;
6120}
6121
6122HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6123 ULONG *aOriginX,
6124 ULONG *aOriginY,
6125 ULONG *aWidth,
6126 ULONG *aHeight,
6127 BOOL *aEnabled)
6128{
6129 uint32_t u32OriginX= 0;
6130 uint32_t u32OriginY= 0;
6131 uint32_t u32Width = 0;
6132 uint32_t u32Height = 0;
6133 uint16_t u16Flags = 0;
6134
6135 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6136 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6137 if (RT_FAILURE(vrc))
6138 {
6139#ifdef RT_OS_WINDOWS
6140 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6141 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6142 * So just assign fEnable to TRUE again.
6143 * The right fix would be to change GUI API wrappers to make sure that parameters
6144 * are changed only if API succeeds.
6145 */
6146 *aEnabled = TRUE;
6147#endif
6148 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6149 tr("Saved guest size is not available (%Rrc)"),
6150 vrc);
6151 }
6152
6153 *aOriginX = u32OriginX;
6154 *aOriginY = u32OriginY;
6155 *aWidth = u32Width;
6156 *aHeight = u32Height;
6157 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6158
6159 return S_OK;
6160}
6161
6162HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6163 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6164{
6165 if (aScreenId != 0)
6166 return E_NOTIMPL;
6167
6168 if ( aBitmapFormat != BitmapFormat_BGR0
6169 && aBitmapFormat != BitmapFormat_BGRA
6170 && aBitmapFormat != BitmapFormat_RGBA
6171 && aBitmapFormat != BitmapFormat_PNG)
6172 return setError(E_NOTIMPL,
6173 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6174
6175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6176
6177 uint8_t *pu8Data = NULL;
6178 uint32_t cbData = 0;
6179 uint32_t u32Width = 0;
6180 uint32_t u32Height = 0;
6181
6182 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6183
6184 if (RT_FAILURE(vrc))
6185 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6186 tr("Saved thumbnail data is not available (%Rrc)"),
6187 vrc);
6188
6189 HRESULT hr = S_OK;
6190
6191 *aWidth = u32Width;
6192 *aHeight = u32Height;
6193
6194 if (cbData > 0)
6195 {
6196 /* Convert pixels to the format expected by the API caller. */
6197 if (aBitmapFormat == BitmapFormat_BGR0)
6198 {
6199 /* [0] B, [1] G, [2] R, [3] 0. */
6200 aData.resize(cbData);
6201 memcpy(&aData.front(), pu8Data, cbData);
6202 }
6203 else if (aBitmapFormat == BitmapFormat_BGRA)
6204 {
6205 /* [0] B, [1] G, [2] R, [3] A. */
6206 aData.resize(cbData);
6207 for (uint32_t i = 0; i < cbData; i += 4)
6208 {
6209 aData[i] = pu8Data[i];
6210 aData[i + 1] = pu8Data[i + 1];
6211 aData[i + 2] = pu8Data[i + 2];
6212 aData[i + 3] = 0xff;
6213 }
6214 }
6215 else if (aBitmapFormat == BitmapFormat_RGBA)
6216 {
6217 /* [0] R, [1] G, [2] B, [3] A. */
6218 aData.resize(cbData);
6219 for (uint32_t i = 0; i < cbData; i += 4)
6220 {
6221 aData[i] = pu8Data[i + 2];
6222 aData[i + 1] = pu8Data[i + 1];
6223 aData[i + 2] = pu8Data[i];
6224 aData[i + 3] = 0xff;
6225 }
6226 }
6227 else if (aBitmapFormat == BitmapFormat_PNG)
6228 {
6229 uint8_t *pu8PNG = NULL;
6230 uint32_t cbPNG = 0;
6231 uint32_t cxPNG = 0;
6232 uint32_t cyPNG = 0;
6233
6234 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6235
6236 if (RT_SUCCESS(vrc))
6237 {
6238 aData.resize(cbPNG);
6239 if (cbPNG)
6240 memcpy(&aData.front(), pu8PNG, cbPNG);
6241 }
6242 else
6243 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6244 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6245 vrc);
6246
6247 RTMemFree(pu8PNG);
6248 }
6249 }
6250
6251 freeSavedDisplayScreenshot(pu8Data);
6252
6253 return hr;
6254}
6255
6256HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6257 ULONG *aWidth,
6258 ULONG *aHeight,
6259 std::vector<BitmapFormat_T> &aBitmapFormats)
6260{
6261 if (aScreenId != 0)
6262 return E_NOTIMPL;
6263
6264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6265
6266 uint8_t *pu8Data = NULL;
6267 uint32_t cbData = 0;
6268 uint32_t u32Width = 0;
6269 uint32_t u32Height = 0;
6270
6271 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6272
6273 if (RT_FAILURE(vrc))
6274 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6275 tr("Saved screenshot data is not available (%Rrc)"),
6276 vrc);
6277
6278 *aWidth = u32Width;
6279 *aHeight = u32Height;
6280 aBitmapFormats.resize(1);
6281 aBitmapFormats[0] = BitmapFormat_PNG;
6282
6283 freeSavedDisplayScreenshot(pu8Data);
6284
6285 return S_OK;
6286}
6287
6288HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6289 BitmapFormat_T aBitmapFormat,
6290 ULONG *aWidth,
6291 ULONG *aHeight,
6292 std::vector<BYTE> &aData)
6293{
6294 if (aScreenId != 0)
6295 return E_NOTIMPL;
6296
6297 if (aBitmapFormat != BitmapFormat_PNG)
6298 return E_NOTIMPL;
6299
6300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6301
6302 uint8_t *pu8Data = NULL;
6303 uint32_t cbData = 0;
6304 uint32_t u32Width = 0;
6305 uint32_t u32Height = 0;
6306
6307 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6308
6309 if (RT_FAILURE(vrc))
6310 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6311 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6312 vrc);
6313
6314 *aWidth = u32Width;
6315 *aHeight = u32Height;
6316
6317 aData.resize(cbData);
6318 if (cbData)
6319 memcpy(&aData.front(), pu8Data, cbData);
6320
6321 freeSavedDisplayScreenshot(pu8Data);
6322
6323 return S_OK;
6324}
6325
6326HRESULT Machine::hotPlugCPU(ULONG aCpu)
6327{
6328 HRESULT rc = S_OK;
6329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6330
6331 if (!mHWData->mCPUHotPlugEnabled)
6332 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6333
6334 if (aCpu >= mHWData->mCPUCount)
6335 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6336
6337 if (mHWData->mCPUAttached[aCpu])
6338 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6339
6340 alock.release();
6341 rc = i_onCPUChange(aCpu, false);
6342 alock.acquire();
6343 if (FAILED(rc)) return rc;
6344
6345 i_setModified(IsModified_MachineData);
6346 mHWData.backup();
6347 mHWData->mCPUAttached[aCpu] = true;
6348
6349 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6350 if (Global::IsOnline(mData->mMachineState))
6351 i_saveSettings(NULL);
6352
6353 return S_OK;
6354}
6355
6356HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6357{
6358 HRESULT rc = S_OK;
6359
6360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6361
6362 if (!mHWData->mCPUHotPlugEnabled)
6363 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6364
6365 if (aCpu >= SchemaDefs::MaxCPUCount)
6366 return setError(E_INVALIDARG,
6367 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6368 SchemaDefs::MaxCPUCount);
6369
6370 if (!mHWData->mCPUAttached[aCpu])
6371 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6372
6373 /* CPU 0 can't be detached */
6374 if (aCpu == 0)
6375 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6376
6377 alock.release();
6378 rc = i_onCPUChange(aCpu, true);
6379 alock.acquire();
6380 if (FAILED(rc)) return rc;
6381
6382 i_setModified(IsModified_MachineData);
6383 mHWData.backup();
6384 mHWData->mCPUAttached[aCpu] = false;
6385
6386 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6387 if (Global::IsOnline(mData->mMachineState))
6388 i_saveSettings(NULL);
6389
6390 return S_OK;
6391}
6392
6393HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6394{
6395 *aAttached = false;
6396
6397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6398
6399 /* If hotplug is enabled the CPU is always enabled. */
6400 if (!mHWData->mCPUHotPlugEnabled)
6401 {
6402 if (aCpu < mHWData->mCPUCount)
6403 *aAttached = true;
6404 }
6405 else
6406 {
6407 if (aCpu < SchemaDefs::MaxCPUCount)
6408 *aAttached = mHWData->mCPUAttached[aCpu];
6409 }
6410
6411 return S_OK;
6412}
6413
6414HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6415{
6416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6417
6418 Utf8Str log = i_getLogFilename(aIdx);
6419 if (!RTFileExists(log.c_str()))
6420 log.setNull();
6421 aFilename = log;
6422
6423 return S_OK;
6424}
6425
6426HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6427{
6428 if (aSize < 0)
6429 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6430
6431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6432
6433 HRESULT rc = S_OK;
6434 Utf8Str log = i_getLogFilename(aIdx);
6435
6436 /* do not unnecessarily hold the lock while doing something which does
6437 * not need the lock and potentially takes a long time. */
6438 alock.release();
6439
6440 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6441 * keeps the SOAP reply size under 1M for the webservice (we're using
6442 * base64 encoded strings for binary data for years now, avoiding the
6443 * expansion of each byte array element to approx. 25 bytes of XML. */
6444 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6445 aData.resize(cbData);
6446
6447 RTFILE LogFile;
6448 int vrc = RTFileOpen(&LogFile, log.c_str(),
6449 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6450 if (RT_SUCCESS(vrc))
6451 {
6452 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6453 if (RT_SUCCESS(vrc))
6454 aData.resize(cbData);
6455 else
6456 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6457 tr("Could not read log file '%s' (%Rrc)"),
6458 log.c_str(), vrc);
6459 RTFileClose(LogFile);
6460 }
6461 else
6462 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6463 tr("Could not open log file '%s' (%Rrc)"),
6464 log.c_str(), vrc);
6465
6466 if (FAILED(rc))
6467 aData.resize(0);
6468
6469 return rc;
6470}
6471
6472
6473/**
6474 * Currently this method doesn't attach device to the running VM,
6475 * just makes sure it's plugged on next VM start.
6476 */
6477HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6478{
6479 // lock scope
6480 {
6481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6482
6483 HRESULT rc = i_checkStateDependency(MutableStateDep);
6484 if (FAILED(rc)) return rc;
6485
6486 ChipsetType_T aChipset = ChipsetType_PIIX3;
6487 COMGETTER(ChipsetType)(&aChipset);
6488
6489 if (aChipset != ChipsetType_ICH9)
6490 {
6491 return setError(E_INVALIDARG,
6492 tr("Host PCI attachment only supported with ICH9 chipset"));
6493 }
6494
6495 // check if device with this host PCI address already attached
6496 for (HWData::PCIDeviceAssignmentList::const_iterator
6497 it = mHWData->mPCIDeviceAssignments.begin();
6498 it != mHWData->mPCIDeviceAssignments.end();
6499 ++it)
6500 {
6501 LONG iHostAddress = -1;
6502 ComPtr<PCIDeviceAttachment> pAttach;
6503 pAttach = *it;
6504 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6505 if (iHostAddress == aHostAddress)
6506 return setError(E_INVALIDARG,
6507 tr("Device with host PCI address already attached to this VM"));
6508 }
6509
6510 ComObjPtr<PCIDeviceAttachment> pda;
6511 char name[32];
6512
6513 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6514 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6515 pda.createObject();
6516 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6517 i_setModified(IsModified_MachineData);
6518 mHWData.backup();
6519 mHWData->mPCIDeviceAssignments.push_back(pda);
6520 }
6521
6522 return S_OK;
6523}
6524
6525/**
6526 * Currently this method doesn't detach device from the running VM,
6527 * just makes sure it's not plugged on next VM start.
6528 */
6529HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6530{
6531 ComObjPtr<PCIDeviceAttachment> pAttach;
6532 bool fRemoved = false;
6533 HRESULT rc;
6534
6535 // lock scope
6536 {
6537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 rc = i_checkStateDependency(MutableStateDep);
6540 if (FAILED(rc)) return rc;
6541
6542 for (HWData::PCIDeviceAssignmentList::const_iterator
6543 it = mHWData->mPCIDeviceAssignments.begin();
6544 it != mHWData->mPCIDeviceAssignments.end();
6545 ++it)
6546 {
6547 LONG iHostAddress = -1;
6548 pAttach = *it;
6549 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6550 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6551 {
6552 i_setModified(IsModified_MachineData);
6553 mHWData.backup();
6554 mHWData->mPCIDeviceAssignments.remove(pAttach);
6555 fRemoved = true;
6556 break;
6557 }
6558 }
6559 }
6560
6561
6562 /* Fire event outside of the lock */
6563 if (fRemoved)
6564 {
6565 Assert(!pAttach.isNull());
6566 ComPtr<IEventSource> es;
6567 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6568 Assert(SUCCEEDED(rc));
6569 Bstr mid;
6570 rc = this->COMGETTER(Id)(mid.asOutParam());
6571 Assert(SUCCEEDED(rc));
6572 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6573 }
6574
6575 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6576 tr("No host PCI device %08x attached"),
6577 aHostAddress
6578 );
6579}
6580
6581HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6582{
6583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6584
6585 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6586 size_t i = 0;
6587 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6588 it = mHWData->mPCIDeviceAssignments.begin();
6589 it != mHWData->mPCIDeviceAssignments.end();
6590 ++it, ++i)
6591 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6592
6593 return S_OK;
6594}
6595
6596HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6597{
6598 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6599
6600 return S_OK;
6601}
6602
6603HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6604{
6605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6606
6607 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6608
6609 return S_OK;
6610}
6611
6612HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6613{
6614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6615 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6616 if (SUCCEEDED(hrc))
6617 {
6618 hrc = mHWData.backupEx();
6619 if (SUCCEEDED(hrc))
6620 {
6621 i_setModified(IsModified_MachineData);
6622 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6623 }
6624 }
6625 return hrc;
6626}
6627
6628HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6629{
6630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6631 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6632 return S_OK;
6633}
6634
6635HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6636{
6637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6638 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6639 if (SUCCEEDED(hrc))
6640 {
6641 hrc = mHWData.backupEx();
6642 if (SUCCEEDED(hrc))
6643 {
6644 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6645 if (SUCCEEDED(hrc))
6646 i_setModified(IsModified_MachineData);
6647 }
6648 }
6649 return hrc;
6650}
6651
6652HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6653{
6654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6655
6656 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6657
6658 return S_OK;
6659}
6660
6661HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6662{
6663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6664 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6665 if (SUCCEEDED(hrc))
6666 {
6667 hrc = mHWData.backupEx();
6668 if (SUCCEEDED(hrc))
6669 {
6670 i_setModified(IsModified_MachineData);
6671 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6672 }
6673 }
6674 return hrc;
6675}
6676
6677HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6678{
6679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6680
6681 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6682
6683 return S_OK;
6684}
6685
6686HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6687{
6688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6689
6690 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6691 if ( SUCCEEDED(hrc)
6692 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6693 {
6694 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6695 int vrc;
6696
6697 if (aAutostartEnabled)
6698 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6699 else
6700 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6701
6702 if (RT_SUCCESS(vrc))
6703 {
6704 hrc = mHWData.backupEx();
6705 if (SUCCEEDED(hrc))
6706 {
6707 i_setModified(IsModified_MachineData);
6708 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6709 }
6710 }
6711 else if (vrc == VERR_NOT_SUPPORTED)
6712 hrc = setError(VBOX_E_NOT_SUPPORTED,
6713 tr("The VM autostart feature is not supported on this platform"));
6714 else if (vrc == VERR_PATH_NOT_FOUND)
6715 hrc = setError(E_FAIL,
6716 tr("The path to the autostart database is not set"));
6717 else
6718 hrc = setError(E_UNEXPECTED,
6719 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6720 aAutostartEnabled ? "Adding" : "Removing",
6721 mUserData->s.strName.c_str(), vrc);
6722 }
6723 return hrc;
6724}
6725
6726HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6727{
6728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6729
6730 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6731
6732 return S_OK;
6733}
6734
6735HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6736{
6737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6738 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6739 if (SUCCEEDED(hrc))
6740 {
6741 hrc = mHWData.backupEx();
6742 if (SUCCEEDED(hrc))
6743 {
6744 i_setModified(IsModified_MachineData);
6745 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6746 }
6747 }
6748 return hrc;
6749}
6750
6751HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6752{
6753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6754
6755 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6756
6757 return S_OK;
6758}
6759
6760HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6761{
6762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6763 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6764 if ( SUCCEEDED(hrc)
6765 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6766 {
6767 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6768 int vrc;
6769
6770 if (aAutostopType != AutostopType_Disabled)
6771 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6772 else
6773 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6774
6775 if (RT_SUCCESS(vrc))
6776 {
6777 hrc = mHWData.backupEx();
6778 if (SUCCEEDED(hrc))
6779 {
6780 i_setModified(IsModified_MachineData);
6781 mHWData->mAutostart.enmAutostopType = aAutostopType;
6782 }
6783 }
6784 else if (vrc == VERR_NOT_SUPPORTED)
6785 hrc = setError(VBOX_E_NOT_SUPPORTED,
6786 tr("The VM autostop feature is not supported on this platform"));
6787 else if (vrc == VERR_PATH_NOT_FOUND)
6788 hrc = setError(E_FAIL,
6789 tr("The path to the autostart database is not set"));
6790 else
6791 hrc = setError(E_UNEXPECTED,
6792 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6793 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6794 mUserData->s.strName.c_str(), vrc);
6795 }
6796 return hrc;
6797}
6798
6799HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6800{
6801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6802
6803 aDefaultFrontend = mHWData->mDefaultFrontend;
6804
6805 return S_OK;
6806}
6807
6808HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6809{
6810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6811 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6812 if (SUCCEEDED(hrc))
6813 {
6814 hrc = mHWData.backupEx();
6815 if (SUCCEEDED(hrc))
6816 {
6817 i_setModified(IsModified_MachineData);
6818 mHWData->mDefaultFrontend = aDefaultFrontend;
6819 }
6820 }
6821 return hrc;
6822}
6823
6824HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6825{
6826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6827 size_t cbIcon = mUserData->s.ovIcon.size();
6828 aIcon.resize(cbIcon);
6829 if (cbIcon)
6830 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6831 return S_OK;
6832}
6833
6834HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6835{
6836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6837 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6838 if (SUCCEEDED(hrc))
6839 {
6840 i_setModified(IsModified_MachineData);
6841 mUserData.backup();
6842 size_t cbIcon = aIcon.size();
6843 mUserData->s.ovIcon.resize(cbIcon);
6844 if (cbIcon)
6845 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6846 }
6847 return hrc;
6848}
6849
6850HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6851{
6852#ifdef VBOX_WITH_USB
6853 *aUSBProxyAvailable = true;
6854#else
6855 *aUSBProxyAvailable = false;
6856#endif
6857 return S_OK;
6858}
6859
6860HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6861{
6862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6863
6864 *aVMProcessPriority = mUserData->s.enmVMPriority;
6865
6866 return S_OK;
6867}
6868
6869HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6870{
6871 RT_NOREF(aVMProcessPriority);
6872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6873 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6874 if (SUCCEEDED(hrc))
6875 {
6876 hrc = mUserData.backupEx();
6877 if (SUCCEEDED(hrc))
6878 {
6879 i_setModified(IsModified_MachineData);
6880 mUserData->s.enmVMPriority = aVMProcessPriority;
6881 }
6882 }
6883 alock.release();
6884 if (SUCCEEDED(hrc))
6885 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6886 return hrc;
6887}
6888
6889HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6890 ComPtr<IProgress> &aProgress)
6891{
6892 ComObjPtr<Progress> pP;
6893 Progress *ppP = pP;
6894 IProgress *iP = static_cast<IProgress *>(ppP);
6895 IProgress **pProgress = &iP;
6896
6897 IMachine *pTarget = aTarget;
6898
6899 /* Convert the options. */
6900 RTCList<CloneOptions_T> optList;
6901 if (aOptions.size())
6902 for (size_t i = 0; i < aOptions.size(); ++i)
6903 optList.append(aOptions[i]);
6904
6905 if (optList.contains(CloneOptions_Link))
6906 {
6907 if (!i_isSnapshotMachine())
6908 return setError(E_INVALIDARG,
6909 tr("Linked clone can only be created from a snapshot"));
6910 if (aMode != CloneMode_MachineState)
6911 return setError(E_INVALIDARG,
6912 tr("Linked clone can only be created for a single machine state"));
6913 }
6914 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6915
6916 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6917
6918 HRESULT rc = pWorker->start(pProgress);
6919
6920 pP = static_cast<Progress *>(*pProgress);
6921 pP.queryInterfaceTo(aProgress.asOutParam());
6922
6923 return rc;
6924
6925}
6926
6927HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6928 const com::Utf8Str &aType,
6929 ComPtr<IProgress> &aProgress)
6930{
6931 LogFlowThisFuncEnter();
6932
6933 ComObjPtr<Progress> ptrProgress;
6934 HRESULT hrc = ptrProgress.createObject();
6935 if (SUCCEEDED(hrc))
6936 {
6937 /* Initialize our worker task */
6938 MachineMoveVM *pTask = NULL;
6939 try
6940 {
6941 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6942 }
6943 catch (std::bad_alloc &)
6944 {
6945 return E_OUTOFMEMORY;
6946 }
6947
6948 hrc = pTask->init();//no exceptions are thrown
6949
6950 if (SUCCEEDED(hrc))
6951 {
6952 hrc = pTask->createThread();
6953 pTask = NULL; /* Consumed by createThread(). */
6954 if (SUCCEEDED(hrc))
6955 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6956 else
6957 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6958 }
6959 else
6960 delete pTask;
6961 }
6962
6963 LogFlowThisFuncLeave();
6964 return hrc;
6965
6966}
6967
6968HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6969{
6970 NOREF(aProgress);
6971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6972
6973 // This check should always fail.
6974 HRESULT rc = i_checkStateDependency(MutableStateDep);
6975 if (FAILED(rc)) return rc;
6976
6977 AssertFailedReturn(E_NOTIMPL);
6978}
6979
6980HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6981{
6982 NOREF(aSavedStateFile);
6983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6984
6985 // This check should always fail.
6986 HRESULT rc = i_checkStateDependency(MutableStateDep);
6987 if (FAILED(rc)) return rc;
6988
6989 AssertFailedReturn(E_NOTIMPL);
6990}
6991
6992HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6993{
6994 NOREF(aFRemoveFile);
6995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6996
6997 // This check should always fail.
6998 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
6999 if (FAILED(rc)) return rc;
7000
7001 AssertFailedReturn(E_NOTIMPL);
7002}
7003
7004// public methods for internal purposes
7005/////////////////////////////////////////////////////////////////////////////
7006
7007/**
7008 * Adds the given IsModified_* flag to the dirty flags of the machine.
7009 * This must be called either during i_loadSettings or under the machine write lock.
7010 * @param fl Flag
7011 * @param fAllowStateModification If state modifications are allowed.
7012 */
7013void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7014{
7015 mData->flModifications |= fl;
7016 if (fAllowStateModification && i_isStateModificationAllowed())
7017 mData->mCurrentStateModified = true;
7018}
7019
7020/**
7021 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7022 * care of the write locking.
7023 *
7024 * @param fModification The flag to add.
7025 * @param fAllowStateModification If state modifications are allowed.
7026 */
7027void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7028{
7029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7030 i_setModified(fModification, fAllowStateModification);
7031}
7032
7033/**
7034 * Saves the registry entry of this machine to the given configuration node.
7035 *
7036 * @param data Machine registry data.
7037 *
7038 * @note locks this object for reading.
7039 */
7040HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7041{
7042 AutoLimitedCaller autoCaller(this);
7043 AssertComRCReturnRC(autoCaller.rc());
7044
7045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7046
7047 data.uuid = mData->mUuid;
7048 data.strSettingsFile = mData->m_strConfigFile;
7049
7050 return S_OK;
7051}
7052
7053/**
7054 * Calculates the absolute path of the given path taking the directory of the
7055 * machine settings file as the current directory.
7056 *
7057 * @param strPath Path to calculate the absolute path for.
7058 * @param aResult Where to put the result (used only on success, can be the
7059 * same Utf8Str instance as passed in @a aPath).
7060 * @return IPRT result.
7061 *
7062 * @note Locks this object for reading.
7063 */
7064int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7065{
7066 AutoCaller autoCaller(this);
7067 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7068
7069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7070
7071 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7072
7073 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7074
7075 strSettingsDir.stripFilename();
7076 char szFolder[RTPATH_MAX];
7077 size_t cbFolder = sizeof(szFolder);
7078 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7079 if (RT_SUCCESS(vrc))
7080 aResult = szFolder;
7081
7082 return vrc;
7083}
7084
7085/**
7086 * Copies strSource to strTarget, making it relative to the machine folder
7087 * if it is a subdirectory thereof, or simply copying it otherwise.
7088 *
7089 * @param strSource Path to evaluate and copy.
7090 * @param strTarget Buffer to receive target path.
7091 *
7092 * @note Locks this object for reading.
7093 */
7094void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7095 Utf8Str &strTarget)
7096{
7097 AutoCaller autoCaller(this);
7098 AssertComRCReturn(autoCaller.rc(), (void)0);
7099
7100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7101
7102 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7103 // use strTarget as a temporary buffer to hold the machine settings dir
7104 strTarget = mData->m_strConfigFileFull;
7105 strTarget.stripFilename();
7106 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7107 {
7108 // is relative: then append what's left
7109 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7110 // for empty paths (only possible for subdirs) use "." to avoid
7111 // triggering default settings for not present config attributes.
7112 if (strTarget.isEmpty())
7113 strTarget = ".";
7114 }
7115 else
7116 // is not relative: then overwrite
7117 strTarget = strSource;
7118}
7119
7120/**
7121 * Returns the full path to the machine's log folder in the
7122 * \a aLogFolder argument.
7123 */
7124void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7125{
7126 AutoCaller autoCaller(this);
7127 AssertComRCReturnVoid(autoCaller.rc());
7128
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 char szTmp[RTPATH_MAX];
7132 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7133 if (RT_SUCCESS(vrc))
7134 {
7135 if (szTmp[0] && !mUserData.isNull())
7136 {
7137 char szTmp2[RTPATH_MAX];
7138 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7139 if (RT_SUCCESS(vrc))
7140 aLogFolder.printf("%s%c%s",
7141 szTmp2,
7142 RTPATH_DELIMITER,
7143 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7144 }
7145 else
7146 vrc = VERR_PATH_IS_RELATIVE;
7147 }
7148
7149 if (RT_FAILURE(vrc))
7150 {
7151 // fallback if VBOX_USER_LOGHOME is not set or invalid
7152 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7153 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7154 aLogFolder.append(RTPATH_DELIMITER);
7155 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7156 }
7157}
7158
7159/**
7160 * Returns the full path to the machine's log file for an given index.
7161 */
7162Utf8Str Machine::i_getLogFilename(ULONG idx)
7163{
7164 Utf8Str logFolder;
7165 getLogFolder(logFolder);
7166 Assert(logFolder.length());
7167
7168 Utf8Str log;
7169 if (idx == 0)
7170 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7171#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7172 else if (idx == 1)
7173 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7174 else
7175 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7176#else
7177 else
7178 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7179#endif
7180 return log;
7181}
7182
7183/**
7184 * Returns the full path to the machine's hardened log file.
7185 */
7186Utf8Str Machine::i_getHardeningLogFilename(void)
7187{
7188 Utf8Str strFilename;
7189 getLogFolder(strFilename);
7190 Assert(strFilename.length());
7191 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7192 return strFilename;
7193}
7194
7195/**
7196 * Returns the default NVRAM filename based on the location of the VM config.
7197 * Note that this is a relative path.
7198 */
7199Utf8Str Machine::i_getDefaultNVRAMFilename()
7200{
7201 AutoCaller autoCaller(this);
7202 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7203
7204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7205
7206 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7207 || i_isSnapshotMachine())
7208 return Utf8Str::Empty;
7209
7210 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7211 strNVRAMFilePath.stripPath();
7212 strNVRAMFilePath.stripSuffix();
7213 strNVRAMFilePath += ".nvram";
7214
7215 return strNVRAMFilePath;
7216}
7217
7218/**
7219 * Returns the NVRAM filename for a new snapshot. This intentionally works
7220 * similarly to the saved state file naming. Note that this is usually
7221 * a relative path, unless the snapshot folder is absolute.
7222 */
7223Utf8Str Machine::i_getSnapshotNVRAMFilename()
7224{
7225 AutoCaller autoCaller(this);
7226 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7227
7228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7229
7230 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7231 return Utf8Str::Empty;
7232
7233 RTTIMESPEC ts;
7234 RTTimeNow(&ts);
7235 RTTIME time;
7236 RTTimeExplode(&time, &ts);
7237
7238 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7239 strNVRAMFilePath += RTPATH_DELIMITER;
7240 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7241 time.i32Year, time.u8Month, time.u8MonthDay,
7242 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7243
7244 return strNVRAMFilePath;
7245}
7246
7247/**
7248 * Composes a unique saved state filename based on the current system time. The filename is
7249 * granular to the second so this will work so long as no more than one snapshot is taken on
7250 * a machine per second.
7251 *
7252 * Before version 4.1, we used this formula for saved state files:
7253 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7254 * which no longer works because saved state files can now be shared between the saved state of the
7255 * "saved" machine and an online snapshot, and the following would cause problems:
7256 * 1) save machine
7257 * 2) create online snapshot from that machine state --> reusing saved state file
7258 * 3) save machine again --> filename would be reused, breaking the online snapshot
7259 *
7260 * So instead we now use a timestamp.
7261 *
7262 * @param strStateFilePath
7263 */
7264
7265void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7266{
7267 AutoCaller autoCaller(this);
7268 AssertComRCReturnVoid(autoCaller.rc());
7269
7270 {
7271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7272 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7273 }
7274
7275 RTTIMESPEC ts;
7276 RTTimeNow(&ts);
7277 RTTIME time;
7278 RTTimeExplode(&time, &ts);
7279
7280 strStateFilePath += RTPATH_DELIMITER;
7281 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7282 time.i32Year, time.u8Month, time.u8MonthDay,
7283 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7284}
7285
7286/**
7287 * Returns whether at least one USB controller is present for the VM.
7288 */
7289bool Machine::i_isUSBControllerPresent()
7290{
7291 AutoCaller autoCaller(this);
7292 AssertComRCReturn(autoCaller.rc(), false);
7293
7294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7295
7296 return (mUSBControllers->size() > 0);
7297}
7298
7299
7300/**
7301 * @note Locks this object for writing, calls the client process
7302 * (inside the lock).
7303 */
7304HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7305 const Utf8Str &strFrontend,
7306 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7307 ProgressProxy *aProgress)
7308{
7309 LogFlowThisFuncEnter();
7310
7311 AssertReturn(aControl, E_FAIL);
7312 AssertReturn(aProgress, E_FAIL);
7313 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7314
7315 AutoCaller autoCaller(this);
7316 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7317
7318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7319
7320 if (!mData->mRegistered)
7321 return setError(E_UNEXPECTED,
7322 tr("The machine '%s' is not registered"),
7323 mUserData->s.strName.c_str());
7324
7325 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7326
7327 /* The process started when launching a VM with separate UI/VM processes is always
7328 * the UI process, i.e. needs special handling as it won't claim the session. */
7329 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7330
7331 if (fSeparate)
7332 {
7333 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7334 return setError(VBOX_E_INVALID_OBJECT_STATE,
7335 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7336 mUserData->s.strName.c_str());
7337 }
7338 else
7339 {
7340 if ( mData->mSession.mState == SessionState_Locked
7341 || mData->mSession.mState == SessionState_Spawning
7342 || mData->mSession.mState == SessionState_Unlocking)
7343 return setError(VBOX_E_INVALID_OBJECT_STATE,
7344 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7345 mUserData->s.strName.c_str());
7346
7347 /* may not be busy */
7348 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7349 }
7350
7351 /* Hardening logging */
7352#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7353 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7354 {
7355 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7356 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7357 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7358 {
7359 Utf8Str strStartupLogDir = strHardeningLogFile;
7360 strStartupLogDir.stripFilename();
7361 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7362 file without stripping the file. */
7363 }
7364 strSupHardeningLogArg.append(strHardeningLogFile);
7365
7366 /* Remove legacy log filename to avoid confusion. */
7367 Utf8Str strOldStartupLogFile;
7368 getLogFolder(strOldStartupLogFile);
7369 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7370 RTFileDelete(strOldStartupLogFile.c_str());
7371 }
7372#else
7373 Utf8Str strSupHardeningLogArg;
7374#endif
7375
7376 Utf8Str strAppOverride;
7377#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7378 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7379#endif
7380
7381 bool fUseVBoxSDS = false;
7382 Utf8Str strCanonicalName;
7383 if (false)
7384 { }
7385#ifdef VBOX_WITH_QTGUI
7386 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7387 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7388 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7389 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7390 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7391 {
7392 strCanonicalName = "GUI/Qt";
7393 fUseVBoxSDS = true;
7394 }
7395#endif
7396#ifdef VBOX_WITH_VBOXSDL
7397 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7398 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7399 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7400 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7401 {
7402 strCanonicalName = "GUI/SDL";
7403 fUseVBoxSDS = true;
7404 }
7405#endif
7406#ifdef VBOX_WITH_HEADLESS
7407 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7408 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7409 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7410 {
7411 strCanonicalName = "headless";
7412 }
7413#endif
7414 else
7415 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7416
7417 Utf8Str idStr = mData->mUuid.toString();
7418 Utf8Str const &strMachineName = mUserData->s.strName;
7419 RTPROCESS pid = NIL_RTPROCESS;
7420
7421#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7422 RT_NOREF(fUseVBoxSDS);
7423#else
7424 DWORD idCallerSession = ~(DWORD)0;
7425 if (fUseVBoxSDS)
7426 {
7427 /*
7428 * The VBoxSDS should be used for process launching the VM with
7429 * GUI only if the caller and the VBoxSDS are in different Windows
7430 * sessions and the caller in the interactive one.
7431 */
7432 fUseVBoxSDS = false;
7433
7434 /* Get windows session of the current process. The process token used
7435 due to several reasons:
7436 1. The token is absent for the current thread except someone set it
7437 for us.
7438 2. Needs to get the id of the session where the process is started.
7439 We only need to do this once, though. */
7440 static DWORD s_idCurrentSession = ~(DWORD)0;
7441 DWORD idCurrentSession = s_idCurrentSession;
7442 if (idCurrentSession == ~(DWORD)0)
7443 {
7444 HANDLE hCurrentProcessToken = NULL;
7445 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7446 {
7447 DWORD cbIgn = 0;
7448 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7449 s_idCurrentSession = idCurrentSession;
7450 else
7451 {
7452 idCurrentSession = ~(DWORD)0;
7453 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7454 }
7455 CloseHandle(hCurrentProcessToken);
7456 }
7457 else
7458 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7459 }
7460
7461 /* get the caller's session */
7462 HRESULT hrc = CoImpersonateClient();
7463 if (SUCCEEDED(hrc))
7464 {
7465 HANDLE hCallerThreadToken;
7466 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7467 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7468 &hCallerThreadToken))
7469 {
7470 SetLastError(NO_ERROR);
7471 DWORD cbIgn = 0;
7472 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7473 {
7474 /* Only need to use SDS if the session ID differs: */
7475 if (idCurrentSession != idCallerSession)
7476 {
7477 fUseVBoxSDS = false;
7478
7479 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7480 DWORD cbTokenGroups = 0;
7481 PTOKEN_GROUPS pTokenGroups = NULL;
7482 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7483 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7484 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7485 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7486 {
7487 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7488 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7489 PSID pInteractiveSid = NULL;
7490 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7491 {
7492 /* Iterate over the groups looking for the interactive SID: */
7493 fUseVBoxSDS = false;
7494 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7495 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7496 {
7497 fUseVBoxSDS = true;
7498 break;
7499 }
7500 FreeSid(pInteractiveSid);
7501 }
7502 }
7503 else
7504 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7505 RTMemTmpFree(pTokenGroups);
7506 }
7507 }
7508 else
7509 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7510 CloseHandle(hCallerThreadToken);
7511 }
7512 else
7513 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7514 CoRevertToSelf();
7515 }
7516 else
7517 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7518 }
7519 if (fUseVBoxSDS)
7520 {
7521 /* connect to VBoxSDS */
7522 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7523 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7524 if (FAILED(rc))
7525 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7526 strMachineName.c_str());
7527
7528 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7529 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7530 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7531 service to access the files. */
7532 rc = CoSetProxyBlanket(pVBoxSDS,
7533 RPC_C_AUTHN_DEFAULT,
7534 RPC_C_AUTHZ_DEFAULT,
7535 COLE_DEFAULT_PRINCIPAL,
7536 RPC_C_AUTHN_LEVEL_DEFAULT,
7537 RPC_C_IMP_LEVEL_IMPERSONATE,
7538 NULL,
7539 EOAC_DEFAULT);
7540 if (FAILED(rc))
7541 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7542
7543 size_t const cEnvVars = aEnvironmentChanges.size();
7544 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7545 for (size_t i = 0; i < cEnvVars; i++)
7546 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7547
7548 ULONG uPid = 0;
7549 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7550 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7551 idCallerSession, &uPid);
7552 if (FAILED(rc))
7553 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7554 pid = (RTPROCESS)uPid;
7555 }
7556 else
7557#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7558 {
7559 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7560 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7561 if (RT_FAILURE(vrc))
7562 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7563 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7564 }
7565
7566 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7567 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7568
7569 if (!fSeparate)
7570 {
7571 /*
7572 * Note that we don't release the lock here before calling the client,
7573 * because it doesn't need to call us back if called with a NULL argument.
7574 * Releasing the lock here is dangerous because we didn't prepare the
7575 * launch data yet, but the client we've just started may happen to be
7576 * too fast and call LockMachine() that will fail (because of PID, etc.),
7577 * so that the Machine will never get out of the Spawning session state.
7578 */
7579
7580 /* inform the session that it will be a remote one */
7581 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7582#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7583 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7584#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7585 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7586#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7587 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7588
7589 if (FAILED(rc))
7590 {
7591 /* restore the session state */
7592 mData->mSession.mState = SessionState_Unlocked;
7593 alock.release();
7594 mParent->i_addProcessToReap(pid);
7595 /* The failure may occur w/o any error info (from RPC), so provide one */
7596 return setError(VBOX_E_VM_ERROR,
7597 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7598 }
7599
7600 /* attach launch data to the machine */
7601 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7602 mData->mSession.mRemoteControls.push_back(aControl);
7603 mData->mSession.mProgress = aProgress;
7604 mData->mSession.mPID = pid;
7605 mData->mSession.mState = SessionState_Spawning;
7606 Assert(strCanonicalName.isNotEmpty());
7607 mData->mSession.mName = strCanonicalName;
7608 }
7609 else
7610 {
7611 /* For separate UI process we declare the launch as completed instantly, as the
7612 * actual headless VM start may or may not come. No point in remembering anything
7613 * yet, as what matters for us is when the headless VM gets started. */
7614 aProgress->i_notifyComplete(S_OK);
7615 }
7616
7617 alock.release();
7618 mParent->i_addProcessToReap(pid);
7619
7620 LogFlowThisFuncLeave();
7621 return S_OK;
7622}
7623
7624/**
7625 * Returns @c true if the given session machine instance has an open direct
7626 * session (and optionally also for direct sessions which are closing) and
7627 * returns the session control machine instance if so.
7628 *
7629 * Note that when the method returns @c false, the arguments remain unchanged.
7630 *
7631 * @param aMachine Session machine object.
7632 * @param aControl Direct session control object (optional).
7633 * @param aRequireVM If true then only allow VM sessions.
7634 * @param aAllowClosing If true then additionally a session which is currently
7635 * being closed will also be allowed.
7636 *
7637 * @note locks this object for reading.
7638 */
7639bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7640 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7641 bool aRequireVM /*= false*/,
7642 bool aAllowClosing /*= false*/)
7643{
7644 AutoLimitedCaller autoCaller(this);
7645 AssertComRCReturn(autoCaller.rc(), false);
7646
7647 /* just return false for inaccessible machines */
7648 if (getObjectState().getState() != ObjectState::Ready)
7649 return false;
7650
7651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7652
7653 if ( ( mData->mSession.mState == SessionState_Locked
7654 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7655 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7656 )
7657 {
7658 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7659
7660 aMachine = mData->mSession.mMachine;
7661
7662 if (aControl != NULL)
7663 *aControl = mData->mSession.mDirectControl;
7664
7665 return true;
7666 }
7667
7668 return false;
7669}
7670
7671/**
7672 * Returns @c true if the given machine has an spawning direct session.
7673 *
7674 * @note locks this object for reading.
7675 */
7676bool Machine::i_isSessionSpawning()
7677{
7678 AutoLimitedCaller autoCaller(this);
7679 AssertComRCReturn(autoCaller.rc(), false);
7680
7681 /* just return false for inaccessible machines */
7682 if (getObjectState().getState() != ObjectState::Ready)
7683 return false;
7684
7685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7686
7687 if (mData->mSession.mState == SessionState_Spawning)
7688 return true;
7689
7690 return false;
7691}
7692
7693/**
7694 * Called from the client watcher thread to check for unexpected client process
7695 * death during Session_Spawning state (e.g. before it successfully opened a
7696 * direct session).
7697 *
7698 * On Win32 and on OS/2, this method is called only when we've got the
7699 * direct client's process termination notification, so it always returns @c
7700 * true.
7701 *
7702 * On other platforms, this method returns @c true if the client process is
7703 * terminated and @c false if it's still alive.
7704 *
7705 * @note Locks this object for writing.
7706 */
7707bool Machine::i_checkForSpawnFailure()
7708{
7709 AutoCaller autoCaller(this);
7710 if (!autoCaller.isOk())
7711 {
7712 /* nothing to do */
7713 LogFlowThisFunc(("Already uninitialized!\n"));
7714 return true;
7715 }
7716
7717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7718
7719 if (mData->mSession.mState != SessionState_Spawning)
7720 {
7721 /* nothing to do */
7722 LogFlowThisFunc(("Not spawning any more!\n"));
7723 return true;
7724 }
7725
7726 HRESULT rc = S_OK;
7727
7728 /* PID not yet initialized, skip check. */
7729 if (mData->mSession.mPID == NIL_RTPROCESS)
7730 return false;
7731
7732 RTPROCSTATUS status;
7733 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7734
7735 if (vrc != VERR_PROCESS_RUNNING)
7736 {
7737 Utf8Str strExtraInfo;
7738
7739#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7740 /* If the startup logfile exists and is of non-zero length, tell the
7741 user to look there for more details to encourage them to attach it
7742 when reporting startup issues. */
7743 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7744 uint64_t cbStartupLogFile = 0;
7745 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7746 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7747 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7748#endif
7749
7750 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7751 rc = setError(E_FAIL,
7752 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7753 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7754 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7755 rc = setError(E_FAIL,
7756 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7757 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7758 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7759 rc = setError(E_FAIL,
7760 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7761 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7762 else
7763 rc = setErrorBoth(E_FAIL, vrc,
7764 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7765 i_getName().c_str(), vrc, strExtraInfo.c_str());
7766 }
7767
7768 if (FAILED(rc))
7769 {
7770 /* Close the remote session, remove the remote control from the list
7771 * and reset session state to Closed (@note keep the code in sync with
7772 * the relevant part in LockMachine()). */
7773
7774 Assert(mData->mSession.mRemoteControls.size() == 1);
7775 if (mData->mSession.mRemoteControls.size() == 1)
7776 {
7777 ErrorInfoKeeper eik;
7778 mData->mSession.mRemoteControls.front()->Uninitialize();
7779 }
7780
7781 mData->mSession.mRemoteControls.clear();
7782 mData->mSession.mState = SessionState_Unlocked;
7783
7784 /* finalize the progress after setting the state */
7785 if (!mData->mSession.mProgress.isNull())
7786 {
7787 mData->mSession.mProgress->notifyComplete(rc);
7788 mData->mSession.mProgress.setNull();
7789 }
7790
7791 mData->mSession.mPID = NIL_RTPROCESS;
7792
7793 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7794 return true;
7795 }
7796
7797 return false;
7798}
7799
7800/**
7801 * Checks whether the machine can be registered. If so, commits and saves
7802 * all settings.
7803 *
7804 * @note Must be called from mParent's write lock. Locks this object and
7805 * children for writing.
7806 */
7807HRESULT Machine::i_prepareRegister()
7808{
7809 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7810
7811 AutoLimitedCaller autoCaller(this);
7812 AssertComRCReturnRC(autoCaller.rc());
7813
7814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7815
7816 /* wait for state dependents to drop to zero */
7817 i_ensureNoStateDependencies();
7818
7819 if (!mData->mAccessible)
7820 return setError(VBOX_E_INVALID_OBJECT_STATE,
7821 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7822 mUserData->s.strName.c_str(),
7823 mData->mUuid.toString().c_str());
7824
7825 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7826
7827 if (mData->mRegistered)
7828 return setError(VBOX_E_INVALID_OBJECT_STATE,
7829 tr("The machine '%s' with UUID {%s} is already registered"),
7830 mUserData->s.strName.c_str(),
7831 mData->mUuid.toString().c_str());
7832
7833 HRESULT rc = S_OK;
7834
7835 // Ensure the settings are saved. If we are going to be registered and
7836 // no config file exists yet, create it by calling i_saveSettings() too.
7837 if ( (mData->flModifications)
7838 || (!mData->pMachineConfigFile->fileExists())
7839 )
7840 {
7841 rc = i_saveSettings(NULL);
7842 // no need to check whether VirtualBox.xml needs saving too since
7843 // we can't have a machine XML file rename pending
7844 if (FAILED(rc)) return rc;
7845 }
7846
7847 /* more config checking goes here */
7848
7849 if (SUCCEEDED(rc))
7850 {
7851 /* we may have had implicit modifications we want to fix on success */
7852 i_commit();
7853
7854 mData->mRegistered = true;
7855 }
7856 else
7857 {
7858 /* we may have had implicit modifications we want to cancel on failure*/
7859 i_rollback(false /* aNotify */);
7860 }
7861
7862 return rc;
7863}
7864
7865/**
7866 * Increases the number of objects dependent on the machine state or on the
7867 * registered state. Guarantees that these two states will not change at least
7868 * until #i_releaseStateDependency() is called.
7869 *
7870 * Depending on the @a aDepType value, additional state checks may be made.
7871 * These checks will set extended error info on failure. See
7872 * #i_checkStateDependency() for more info.
7873 *
7874 * If this method returns a failure, the dependency is not added and the caller
7875 * is not allowed to rely on any particular machine state or registration state
7876 * value and may return the failed result code to the upper level.
7877 *
7878 * @param aDepType Dependency type to add.
7879 * @param aState Current machine state (NULL if not interested).
7880 * @param aRegistered Current registered state (NULL if not interested).
7881 *
7882 * @note Locks this object for writing.
7883 */
7884HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7885 MachineState_T *aState /* = NULL */,
7886 BOOL *aRegistered /* = NULL */)
7887{
7888 AutoCaller autoCaller(this);
7889 AssertComRCReturnRC(autoCaller.rc());
7890
7891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7892
7893 HRESULT rc = i_checkStateDependency(aDepType);
7894 if (FAILED(rc)) return rc;
7895
7896 {
7897 if (mData->mMachineStateChangePending != 0)
7898 {
7899 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7900 * drop to zero so don't add more. It may make sense to wait a bit
7901 * and retry before reporting an error (since the pending state
7902 * transition should be really quick) but let's just assert for
7903 * now to see if it ever happens on practice. */
7904
7905 AssertFailed();
7906
7907 return setError(E_ACCESSDENIED,
7908 tr("Machine state change is in progress. Please retry the operation later."));
7909 }
7910
7911 ++mData->mMachineStateDeps;
7912 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7913 }
7914
7915 if (aState)
7916 *aState = mData->mMachineState;
7917 if (aRegistered)
7918 *aRegistered = mData->mRegistered;
7919
7920 return S_OK;
7921}
7922
7923/**
7924 * Decreases the number of objects dependent on the machine state.
7925 * Must always complete the #i_addStateDependency() call after the state
7926 * dependency is no more necessary.
7927 */
7928void Machine::i_releaseStateDependency()
7929{
7930 AutoCaller autoCaller(this);
7931 AssertComRCReturnVoid(autoCaller.rc());
7932
7933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7934
7935 /* releaseStateDependency() w/o addStateDependency()? */
7936 AssertReturnVoid(mData->mMachineStateDeps != 0);
7937 -- mData->mMachineStateDeps;
7938
7939 if (mData->mMachineStateDeps == 0)
7940 {
7941 /* inform i_ensureNoStateDependencies() that there are no more deps */
7942 if (mData->mMachineStateChangePending != 0)
7943 {
7944 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7945 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7946 }
7947 }
7948}
7949
7950Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7951{
7952 /* start with nothing found */
7953 Utf8Str strResult("");
7954
7955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7956
7957 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7958 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7959 // found:
7960 strResult = it->second; // source is a Utf8Str
7961
7962 return strResult;
7963}
7964
7965// protected methods
7966/////////////////////////////////////////////////////////////////////////////
7967
7968/**
7969 * Performs machine state checks based on the @a aDepType value. If a check
7970 * fails, this method will set extended error info, otherwise it will return
7971 * S_OK. It is supposed, that on failure, the caller will immediately return
7972 * the return value of this method to the upper level.
7973 *
7974 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7975 *
7976 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7977 * current state of this machine object allows to change settings of the
7978 * machine (i.e. the machine is not registered, or registered but not running
7979 * and not saved). It is useful to call this method from Machine setters
7980 * before performing any change.
7981 *
7982 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7983 * as for MutableStateDep except that if the machine is saved, S_OK is also
7984 * returned. This is useful in setters which allow changing machine
7985 * properties when it is in the saved state.
7986 *
7987 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7988 * if the current state of this machine object allows to change runtime
7989 * changeable settings of the machine (i.e. the machine is not registered, or
7990 * registered but either running or not running and not saved). It is useful
7991 * to call this method from Machine setters before performing any changes to
7992 * runtime changeable settings.
7993 *
7994 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7995 * the same as for MutableOrRunningStateDep except that if the machine is
7996 * saved, S_OK is also returned. This is useful in setters which allow
7997 * changing runtime and saved state changeable machine properties.
7998 *
7999 * @param aDepType Dependency type to check.
8000 *
8001 * @note Non Machine based classes should use #i_addStateDependency() and
8002 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8003 * template.
8004 *
8005 * @note This method must be called from under this object's read or write
8006 * lock.
8007 */
8008HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8009{
8010 switch (aDepType)
8011 {
8012 case AnyStateDep:
8013 {
8014 break;
8015 }
8016 case MutableStateDep:
8017 {
8018 if ( mData->mRegistered
8019 && ( !i_isSessionMachine()
8020 || ( mData->mMachineState != MachineState_Aborted
8021 && mData->mMachineState != MachineState_Teleported
8022 && mData->mMachineState != MachineState_PoweredOff
8023 )
8024 )
8025 )
8026 return setError(VBOX_E_INVALID_VM_STATE,
8027 tr("The machine is not mutable (state is %s)"),
8028 Global::stringifyMachineState(mData->mMachineState));
8029 break;
8030 }
8031 case MutableOrSavedStateDep:
8032 {
8033 if ( mData->mRegistered
8034 && ( !i_isSessionMachine()
8035 || ( mData->mMachineState != MachineState_Aborted
8036 && mData->mMachineState != MachineState_Teleported
8037 && mData->mMachineState != MachineState_Saved
8038 && mData->mMachineState != MachineState_PoweredOff
8039 )
8040 )
8041 )
8042 return setError(VBOX_E_INVALID_VM_STATE,
8043 tr("The machine is not mutable or saved (state is %s)"),
8044 Global::stringifyMachineState(mData->mMachineState));
8045 break;
8046 }
8047 case MutableOrRunningStateDep:
8048 {
8049 if ( mData->mRegistered
8050 && ( !i_isSessionMachine()
8051 || ( mData->mMachineState != MachineState_Aborted
8052 && mData->mMachineState != MachineState_Teleported
8053 && mData->mMachineState != MachineState_PoweredOff
8054 && !Global::IsOnline(mData->mMachineState)
8055 )
8056 )
8057 )
8058 return setError(VBOX_E_INVALID_VM_STATE,
8059 tr("The machine is not mutable or running (state is %s)"),
8060 Global::stringifyMachineState(mData->mMachineState));
8061 break;
8062 }
8063 case MutableOrSavedOrRunningStateDep:
8064 {
8065 if ( mData->mRegistered
8066 && ( !i_isSessionMachine()
8067 || ( mData->mMachineState != MachineState_Aborted
8068 && mData->mMachineState != MachineState_Teleported
8069 && mData->mMachineState != MachineState_Saved
8070 && mData->mMachineState != MachineState_PoweredOff
8071 && !Global::IsOnline(mData->mMachineState)
8072 )
8073 )
8074 )
8075 return setError(VBOX_E_INVALID_VM_STATE,
8076 tr("The machine is not mutable, saved or running (state is %s)"),
8077 Global::stringifyMachineState(mData->mMachineState));
8078 break;
8079 }
8080 }
8081
8082 return S_OK;
8083}
8084
8085/**
8086 * Helper to initialize all associated child objects and allocate data
8087 * structures.
8088 *
8089 * This method must be called as a part of the object's initialization procedure
8090 * (usually done in the #init() method).
8091 *
8092 * @note Must be called only from #init() or from #i_registeredInit().
8093 */
8094HRESULT Machine::initDataAndChildObjects()
8095{
8096 AutoCaller autoCaller(this);
8097 AssertComRCReturnRC(autoCaller.rc());
8098 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8099 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8100
8101 AssertReturn(!mData->mAccessible, E_FAIL);
8102
8103 /* allocate data structures */
8104 mSSData.allocate();
8105 mUserData.allocate();
8106 mHWData.allocate();
8107 mMediumAttachments.allocate();
8108 mStorageControllers.allocate();
8109 mUSBControllers.allocate();
8110
8111 /* initialize mOSTypeId */
8112 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8113
8114/** @todo r=bird: init() methods never fails, right? Why don't we make them
8115 * return void then! */
8116
8117 /* create associated BIOS settings object */
8118 unconst(mBIOSSettings).createObject();
8119 mBIOSSettings->init(this);
8120
8121 /* create associated record settings object */
8122 unconst(mRecordingSettings).createObject();
8123 mRecordingSettings->init(this);
8124
8125 /* create the graphics adapter object (always present) */
8126 unconst(mGraphicsAdapter).createObject();
8127 mGraphicsAdapter->init(this);
8128
8129 /* create an associated VRDE object (default is disabled) */
8130 unconst(mVRDEServer).createObject();
8131 mVRDEServer->init(this);
8132
8133 /* create associated serial port objects */
8134 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8135 {
8136 unconst(mSerialPorts[slot]).createObject();
8137 mSerialPorts[slot]->init(this, slot);
8138 }
8139
8140 /* create associated parallel port objects */
8141 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8142 {
8143 unconst(mParallelPorts[slot]).createObject();
8144 mParallelPorts[slot]->init(this, slot);
8145 }
8146
8147 /* create the audio adapter object (always present, default is disabled) */
8148 unconst(mAudioAdapter).createObject();
8149 mAudioAdapter->init(this);
8150
8151 /* create the USB device filters object (always present) */
8152 unconst(mUSBDeviceFilters).createObject();
8153 mUSBDeviceFilters->init(this);
8154
8155 /* create associated network adapter objects */
8156 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8157 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8158 {
8159 unconst(mNetworkAdapters[slot]).createObject();
8160 mNetworkAdapters[slot]->init(this, slot);
8161 }
8162
8163 /* create the bandwidth control */
8164 unconst(mBandwidthControl).createObject();
8165 mBandwidthControl->init(this);
8166
8167 return S_OK;
8168}
8169
8170/**
8171 * Helper to uninitialize all associated child objects and to free all data
8172 * structures.
8173 *
8174 * This method must be called as a part of the object's uninitialization
8175 * procedure (usually done in the #uninit() method).
8176 *
8177 * @note Must be called only from #uninit() or from #i_registeredInit().
8178 */
8179void Machine::uninitDataAndChildObjects()
8180{
8181 AutoCaller autoCaller(this);
8182 AssertComRCReturnVoid(autoCaller.rc());
8183 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8184 || getObjectState().getState() == ObjectState::Limited);
8185
8186 /* tell all our other child objects we've been uninitialized */
8187 if (mBandwidthControl)
8188 {
8189 mBandwidthControl->uninit();
8190 unconst(mBandwidthControl).setNull();
8191 }
8192
8193 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8194 {
8195 if (mNetworkAdapters[slot])
8196 {
8197 mNetworkAdapters[slot]->uninit();
8198 unconst(mNetworkAdapters[slot]).setNull();
8199 }
8200 }
8201
8202 if (mUSBDeviceFilters)
8203 {
8204 mUSBDeviceFilters->uninit();
8205 unconst(mUSBDeviceFilters).setNull();
8206 }
8207
8208 if (mAudioAdapter)
8209 {
8210 mAudioAdapter->uninit();
8211 unconst(mAudioAdapter).setNull();
8212 }
8213
8214 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8215 {
8216 if (mParallelPorts[slot])
8217 {
8218 mParallelPorts[slot]->uninit();
8219 unconst(mParallelPorts[slot]).setNull();
8220 }
8221 }
8222
8223 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8224 {
8225 if (mSerialPorts[slot])
8226 {
8227 mSerialPorts[slot]->uninit();
8228 unconst(mSerialPorts[slot]).setNull();
8229 }
8230 }
8231
8232 if (mVRDEServer)
8233 {
8234 mVRDEServer->uninit();
8235 unconst(mVRDEServer).setNull();
8236 }
8237
8238 if (mGraphicsAdapter)
8239 {
8240 mGraphicsAdapter->uninit();
8241 unconst(mGraphicsAdapter).setNull();
8242 }
8243
8244 if (mBIOSSettings)
8245 {
8246 mBIOSSettings->uninit();
8247 unconst(mBIOSSettings).setNull();
8248 }
8249
8250 if (mRecordingSettings)
8251 {
8252 mRecordingSettings->uninit();
8253 unconst(mRecordingSettings).setNull();
8254 }
8255
8256 /* Deassociate media (only when a real Machine or a SnapshotMachine
8257 * instance is uninitialized; SessionMachine instances refer to real
8258 * Machine media). This is necessary for a clean re-initialization of
8259 * the VM after successfully re-checking the accessibility state. Note
8260 * that in case of normal Machine or SnapshotMachine uninitialization (as
8261 * a result of unregistering or deleting the snapshot), outdated media
8262 * attachments will already be uninitialized and deleted, so this
8263 * code will not affect them. */
8264 if ( !mMediumAttachments.isNull()
8265 && !i_isSessionMachine()
8266 )
8267 {
8268 for (MediumAttachmentList::const_iterator
8269 it = mMediumAttachments->begin();
8270 it != mMediumAttachments->end();
8271 ++it)
8272 {
8273 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8274 if (pMedium.isNull())
8275 continue;
8276 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8277 AssertComRC(rc);
8278 }
8279 }
8280
8281 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8282 {
8283 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8284 if (mData->mFirstSnapshot)
8285 {
8286 // snapshots tree is protected by machine write lock; strictly
8287 // this isn't necessary here since we're deleting the entire
8288 // machine, but otherwise we assert in Snapshot::uninit()
8289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8290 mData->mFirstSnapshot->uninit();
8291 mData->mFirstSnapshot.setNull();
8292 }
8293
8294 mData->mCurrentSnapshot.setNull();
8295 }
8296
8297 /* free data structures (the essential mData structure is not freed here
8298 * since it may be still in use) */
8299 mMediumAttachments.free();
8300 mStorageControllers.free();
8301 mUSBControllers.free();
8302 mHWData.free();
8303 mUserData.free();
8304 mSSData.free();
8305}
8306
8307/**
8308 * Returns a pointer to the Machine object for this machine that acts like a
8309 * parent for complex machine data objects such as shared folders, etc.
8310 *
8311 * For primary Machine objects and for SnapshotMachine objects, returns this
8312 * object's pointer itself. For SessionMachine objects, returns the peer
8313 * (primary) machine pointer.
8314 */
8315Machine *Machine::i_getMachine()
8316{
8317 if (i_isSessionMachine())
8318 return (Machine*)mPeer;
8319 return this;
8320}
8321
8322/**
8323 * Makes sure that there are no machine state dependents. If necessary, waits
8324 * for the number of dependents to drop to zero.
8325 *
8326 * Make sure this method is called from under this object's write lock to
8327 * guarantee that no new dependents may be added when this method returns
8328 * control to the caller.
8329 *
8330 * @note Locks this object for writing. The lock will be released while waiting
8331 * (if necessary).
8332 *
8333 * @warning To be used only in methods that change the machine state!
8334 */
8335void Machine::i_ensureNoStateDependencies()
8336{
8337 AssertReturnVoid(isWriteLockOnCurrentThread());
8338
8339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8340
8341 /* Wait for all state dependents if necessary */
8342 if (mData->mMachineStateDeps != 0)
8343 {
8344 /* lazy semaphore creation */
8345 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8346 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8347
8348 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8349 mData->mMachineStateDeps));
8350
8351 ++mData->mMachineStateChangePending;
8352
8353 /* reset the semaphore before waiting, the last dependent will signal
8354 * it */
8355 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8356
8357 alock.release();
8358
8359 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8360
8361 alock.acquire();
8362
8363 -- mData->mMachineStateChangePending;
8364 }
8365}
8366
8367/**
8368 * Changes the machine state and informs callbacks.
8369 *
8370 * This method is not intended to fail so it either returns S_OK or asserts (and
8371 * returns a failure).
8372 *
8373 * @note Locks this object for writing.
8374 */
8375HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8376{
8377 LogFlowThisFuncEnter();
8378 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8379 Assert(aMachineState != MachineState_Null);
8380
8381 AutoCaller autoCaller(this);
8382 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8383
8384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8385
8386 /* wait for state dependents to drop to zero */
8387 i_ensureNoStateDependencies();
8388
8389 MachineState_T const enmOldState = mData->mMachineState;
8390 if (enmOldState != aMachineState)
8391 {
8392 mData->mMachineState = aMachineState;
8393 RTTimeNow(&mData->mLastStateChange);
8394
8395#ifdef VBOX_WITH_DTRACE_R3_MAIN
8396 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8397#endif
8398 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8399 }
8400
8401 LogFlowThisFuncLeave();
8402 return S_OK;
8403}
8404
8405/**
8406 * Searches for a shared folder with the given logical name
8407 * in the collection of shared folders.
8408 *
8409 * @param aName logical name of the shared folder
8410 * @param aSharedFolder where to return the found object
8411 * @param aSetError whether to set the error info if the folder is
8412 * not found
8413 * @return
8414 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8415 *
8416 * @note
8417 * must be called from under the object's lock!
8418 */
8419HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8420 ComObjPtr<SharedFolder> &aSharedFolder,
8421 bool aSetError /* = false */)
8422{
8423 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8424 for (HWData::SharedFolderList::const_iterator
8425 it = mHWData->mSharedFolders.begin();
8426 it != mHWData->mSharedFolders.end();
8427 ++it)
8428 {
8429 SharedFolder *pSF = *it;
8430 AutoCaller autoCaller(pSF);
8431 if (pSF->i_getName() == aName)
8432 {
8433 aSharedFolder = pSF;
8434 rc = S_OK;
8435 break;
8436 }
8437 }
8438
8439 if (aSetError && FAILED(rc))
8440 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8441
8442 return rc;
8443}
8444
8445/**
8446 * Initializes all machine instance data from the given settings structures
8447 * from XML. The exception is the machine UUID which needs special handling
8448 * depending on the caller's use case, so the caller needs to set that herself.
8449 *
8450 * This gets called in several contexts during machine initialization:
8451 *
8452 * -- When machine XML exists on disk already and needs to be loaded into memory,
8453 * for example, from #i_registeredInit() to load all registered machines on
8454 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8455 * attached to the machine should be part of some media registry already.
8456 *
8457 * -- During OVF import, when a machine config has been constructed from an
8458 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8459 * ensure that the media listed as attachments in the config (which have
8460 * been imported from the OVF) receive the correct registry ID.
8461 *
8462 * -- During VM cloning.
8463 *
8464 * @param config Machine settings from XML.
8465 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8466 * for each attached medium in the config.
8467 * @return
8468 */
8469HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8470 const Guid *puuidRegistry)
8471{
8472 // copy name, description, OS type, teleporter, UTC etc.
8473 mUserData->s = config.machineUserData;
8474
8475 // look up the object by Id to check it is valid
8476 ComObjPtr<GuestOSType> pGuestOSType;
8477 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8478 if (!pGuestOSType.isNull())
8479 mUserData->s.strOsType = pGuestOSType->i_id();
8480
8481 // stateFile (optional)
8482 if (config.strStateFile.isEmpty())
8483 mSSData->strStateFilePath.setNull();
8484 else
8485 {
8486 Utf8Str stateFilePathFull(config.strStateFile);
8487 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8488 if (RT_FAILURE(vrc))
8489 return setErrorBoth(E_FAIL, vrc,
8490 tr("Invalid saved state file path '%s' (%Rrc)"),
8491 config.strStateFile.c_str(),
8492 vrc);
8493 mSSData->strStateFilePath = stateFilePathFull;
8494 }
8495
8496 // snapshot folder needs special processing so set it again
8497 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8498 if (FAILED(rc)) return rc;
8499
8500 /* Copy the extra data items (config may or may not be the same as
8501 * mData->pMachineConfigFile) if necessary. When loading the XML files
8502 * from disk they are the same, but not for OVF import. */
8503 if (mData->pMachineConfigFile != &config)
8504 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8505
8506 /* currentStateModified (optional, default is true) */
8507 mData->mCurrentStateModified = config.fCurrentStateModified;
8508
8509 mData->mLastStateChange = config.timeLastStateChange;
8510
8511 /*
8512 * note: all mUserData members must be assigned prior this point because
8513 * we need to commit changes in order to let mUserData be shared by all
8514 * snapshot machine instances.
8515 */
8516 mUserData.commitCopy();
8517
8518 // machine registry, if present (must be loaded before snapshots)
8519 if (config.canHaveOwnMediaRegistry())
8520 {
8521 // determine machine folder
8522 Utf8Str strMachineFolder = i_getSettingsFileFull();
8523 strMachineFolder.stripFilename();
8524 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8525 config.mediaRegistry,
8526 strMachineFolder);
8527 if (FAILED(rc)) return rc;
8528 }
8529
8530 /* Snapshot node (optional) */
8531 size_t cRootSnapshots;
8532 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8533 {
8534 // there must be only one root snapshot
8535 Assert(cRootSnapshots == 1);
8536
8537 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8538
8539 rc = i_loadSnapshot(snap,
8540 config.uuidCurrentSnapshot,
8541 NULL); // no parent == first snapshot
8542 if (FAILED(rc)) return rc;
8543 }
8544
8545 // hardware data
8546 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8547 if (FAILED(rc)) return rc;
8548
8549 /*
8550 * NOTE: the assignment below must be the last thing to do,
8551 * otherwise it will be not possible to change the settings
8552 * somewhere in the code above because all setters will be
8553 * blocked by i_checkStateDependency(MutableStateDep).
8554 */
8555
8556 /* set the machine state to Aborted or Saved when appropriate */
8557 if (config.fAborted)
8558 {
8559 mSSData->strStateFilePath.setNull();
8560
8561 /* no need to use i_setMachineState() during init() */
8562 mData->mMachineState = MachineState_Aborted;
8563 }
8564 else if (!mSSData->strStateFilePath.isEmpty())
8565 {
8566 /* no need to use i_setMachineState() during init() */
8567 mData->mMachineState = MachineState_Saved;
8568 }
8569
8570 // after loading settings, we are no longer different from the XML on disk
8571 mData->flModifications = 0;
8572
8573 return S_OK;
8574}
8575
8576/**
8577 * Recursively loads all snapshots starting from the given.
8578 *
8579 * @param data snapshot settings.
8580 * @param aCurSnapshotId Current snapshot ID from the settings file.
8581 * @param aParentSnapshot Parent snapshot.
8582 */
8583HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8584 const Guid &aCurSnapshotId,
8585 Snapshot *aParentSnapshot)
8586{
8587 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8588 AssertReturn(!i_isSessionMachine(), E_FAIL);
8589
8590 HRESULT rc = S_OK;
8591
8592 Utf8Str strStateFile;
8593 if (!data.strStateFile.isEmpty())
8594 {
8595 /* optional */
8596 strStateFile = data.strStateFile;
8597 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8598 if (RT_FAILURE(vrc))
8599 return setErrorBoth(E_FAIL, vrc,
8600 tr("Invalid saved state file path '%s' (%Rrc)"),
8601 strStateFile.c_str(),
8602 vrc);
8603 }
8604
8605 /* create a snapshot machine object */
8606 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8607 pSnapshotMachine.createObject();
8608 rc = pSnapshotMachine->initFromSettings(this,
8609 data.hardware,
8610 &data.debugging,
8611 &data.autostart,
8612 data.uuid.ref(),
8613 strStateFile);
8614 if (FAILED(rc)) return rc;
8615
8616 /* create a snapshot object */
8617 ComObjPtr<Snapshot> pSnapshot;
8618 pSnapshot.createObject();
8619 /* initialize the snapshot */
8620 rc = pSnapshot->init(mParent, // VirtualBox object
8621 data.uuid,
8622 data.strName,
8623 data.strDescription,
8624 data.timestamp,
8625 pSnapshotMachine,
8626 aParentSnapshot);
8627 if (FAILED(rc)) return rc;
8628
8629 /* memorize the first snapshot if necessary */
8630 if (!mData->mFirstSnapshot)
8631 mData->mFirstSnapshot = pSnapshot;
8632
8633 /* memorize the current snapshot when appropriate */
8634 if ( !mData->mCurrentSnapshot
8635 && pSnapshot->i_getId() == aCurSnapshotId
8636 )
8637 mData->mCurrentSnapshot = pSnapshot;
8638
8639 // now create the children
8640 for (settings::SnapshotsList::const_iterator
8641 it = data.llChildSnapshots.begin();
8642 it != data.llChildSnapshots.end();
8643 ++it)
8644 {
8645 const settings::Snapshot &childData = *it;
8646 // recurse
8647 rc = i_loadSnapshot(childData,
8648 aCurSnapshotId,
8649 pSnapshot); // parent = the one we created above
8650 if (FAILED(rc)) return rc;
8651 }
8652
8653 return rc;
8654}
8655
8656/**
8657 * Loads settings into mHWData.
8658 *
8659 * @param puuidRegistry Registry ID.
8660 * @param puuidSnapshot Snapshot ID
8661 * @param data Reference to the hardware settings.
8662 * @param pDbg Pointer to the debugging settings.
8663 * @param pAutostart Pointer to the autostart settings.
8664 */
8665HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8666 const Guid *puuidSnapshot,
8667 const settings::Hardware &data,
8668 const settings::Debugging *pDbg,
8669 const settings::Autostart *pAutostart)
8670{
8671 AssertReturn(!i_isSessionMachine(), E_FAIL);
8672
8673 HRESULT rc = S_OK;
8674
8675 try
8676 {
8677 ComObjPtr<GuestOSType> pGuestOSType;
8678 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8679
8680 /* The hardware version attribute (optional). */
8681 mHWData->mHWVersion = data.strVersion;
8682 mHWData->mHardwareUUID = data.uuid;
8683
8684 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8685 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8686 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8687 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8688 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8689 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8690 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8691 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8692 mHWData->mPAEEnabled = data.fPAE;
8693 mHWData->mLongMode = data.enmLongMode;
8694 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8695 mHWData->mAPIC = data.fAPIC;
8696 mHWData->mX2APIC = data.fX2APIC;
8697 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8698 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8699 mHWData->mSpecCtrl = data.fSpecCtrl;
8700 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8701 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8702 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8703 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8704 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8705 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8706 mHWData->mCPUCount = data.cCPUs;
8707 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8708 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8709 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8710 mHWData->mCpuProfile = data.strCpuProfile;
8711
8712 // cpu
8713 if (mHWData->mCPUHotPlugEnabled)
8714 {
8715 for (settings::CpuList::const_iterator
8716 it = data.llCpus.begin();
8717 it != data.llCpus.end();
8718 ++it)
8719 {
8720 const settings::Cpu &cpu = *it;
8721
8722 mHWData->mCPUAttached[cpu.ulId] = true;
8723 }
8724 }
8725
8726 // cpuid leafs
8727 for (settings::CpuIdLeafsList::const_iterator
8728 it = data.llCpuIdLeafs.begin();
8729 it != data.llCpuIdLeafs.end();
8730 ++it)
8731 {
8732 const settings::CpuIdLeaf &rLeaf= *it;
8733 if ( rLeaf.idx < UINT32_C(0x20)
8734 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8735 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8736 mHWData->mCpuIdLeafList.push_back(rLeaf);
8737 /* else: just ignore */
8738 }
8739
8740 mHWData->mMemorySize = data.ulMemorySizeMB;
8741 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8742
8743 // boot order
8744 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8745 {
8746 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8747 if (it == data.mapBootOrder.end())
8748 mHWData->mBootOrder[i] = DeviceType_Null;
8749 else
8750 mHWData->mBootOrder[i] = it->second;
8751 }
8752
8753 mHWData->mFirmwareType = data.firmwareType;
8754 mHWData->mPointingHIDType = data.pointingHIDType;
8755 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8756 mHWData->mChipsetType = data.chipsetType;
8757 mHWData->mParavirtProvider = data.paravirtProvider;
8758 mHWData->mParavirtDebug = data.strParavirtDebug;
8759 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8760 mHWData->mHPETEnabled = data.fHPETEnabled;
8761
8762 /* GraphicsAdapter */
8763 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8764 if (FAILED(rc)) return rc;
8765
8766 /* VRDEServer */
8767 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8768 if (FAILED(rc)) return rc;
8769
8770 /* BIOS */
8771 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8772 if (FAILED(rc)) return rc;
8773
8774 /* Recording settings */
8775 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8776 if (FAILED(rc)) return rc;
8777
8778 // Bandwidth control (must come before network adapters)
8779 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8780 if (FAILED(rc)) return rc;
8781
8782 /* USB controllers */
8783 for (settings::USBControllerList::const_iterator
8784 it = data.usbSettings.llUSBControllers.begin();
8785 it != data.usbSettings.llUSBControllers.end();
8786 ++it)
8787 {
8788 const settings::USBController &settingsCtrl = *it;
8789 ComObjPtr<USBController> newCtrl;
8790
8791 newCtrl.createObject();
8792 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8793 mUSBControllers->push_back(newCtrl);
8794 }
8795
8796 /* USB device filters */
8797 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8798 if (FAILED(rc)) return rc;
8799
8800 // network adapters (establish array size first and apply defaults, to
8801 // ensure reading the same settings as we saved, since the list skips
8802 // adapters having defaults)
8803 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8804 size_t oldCount = mNetworkAdapters.size();
8805 if (newCount > oldCount)
8806 {
8807 mNetworkAdapters.resize(newCount);
8808 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8809 {
8810 unconst(mNetworkAdapters[slot]).createObject();
8811 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8812 }
8813 }
8814 else if (newCount < oldCount)
8815 mNetworkAdapters.resize(newCount);
8816 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8817 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8818 for (settings::NetworkAdaptersList::const_iterator
8819 it = data.llNetworkAdapters.begin();
8820 it != data.llNetworkAdapters.end();
8821 ++it)
8822 {
8823 const settings::NetworkAdapter &nic = *it;
8824
8825 /* slot uniqueness is guaranteed by XML Schema */
8826 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8827 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8828 if (FAILED(rc)) return rc;
8829 }
8830
8831 // serial ports (establish defaults first, to ensure reading the same
8832 // settings as we saved, since the list skips ports having defaults)
8833 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8834 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8835 for (settings::SerialPortsList::const_iterator
8836 it = data.llSerialPorts.begin();
8837 it != data.llSerialPorts.end();
8838 ++it)
8839 {
8840 const settings::SerialPort &s = *it;
8841
8842 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8843 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8844 if (FAILED(rc)) return rc;
8845 }
8846
8847 // parallel ports (establish defaults first, to ensure reading the same
8848 // settings as we saved, since the list skips ports having defaults)
8849 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8850 mParallelPorts[i]->i_applyDefaults();
8851 for (settings::ParallelPortsList::const_iterator
8852 it = data.llParallelPorts.begin();
8853 it != data.llParallelPorts.end();
8854 ++it)
8855 {
8856 const settings::ParallelPort &p = *it;
8857
8858 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8859 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8860 if (FAILED(rc)) return rc;
8861 }
8862
8863 /* AudioAdapter */
8864 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8865 if (FAILED(rc)) return rc;
8866
8867 /* storage controllers */
8868 rc = i_loadStorageControllers(data.storage,
8869 puuidRegistry,
8870 puuidSnapshot);
8871 if (FAILED(rc)) return rc;
8872
8873 /* Shared folders */
8874 for (settings::SharedFoldersList::const_iterator
8875 it = data.llSharedFolders.begin();
8876 it != data.llSharedFolders.end();
8877 ++it)
8878 {
8879 const settings::SharedFolder &sf = *it;
8880
8881 ComObjPtr<SharedFolder> sharedFolder;
8882 /* Check for double entries. Not allowed! */
8883 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8884 if (SUCCEEDED(rc))
8885 return setError(VBOX_E_OBJECT_IN_USE,
8886 tr("Shared folder named '%s' already exists"),
8887 sf.strName.c_str());
8888
8889 /* Create the new shared folder. Don't break on error. This will be
8890 * reported when the machine starts. */
8891 sharedFolder.createObject();
8892 rc = sharedFolder->init(i_getMachine(),
8893 sf.strName,
8894 sf.strHostPath,
8895 RT_BOOL(sf.fWritable),
8896 RT_BOOL(sf.fAutoMount),
8897 sf.strAutoMountPoint,
8898 false /* fFailOnError */);
8899 if (FAILED(rc)) return rc;
8900 mHWData->mSharedFolders.push_back(sharedFolder);
8901 }
8902
8903 // Clipboard
8904 mHWData->mClipboardMode = data.clipboardMode;
8905 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8906
8907 // drag'n'drop
8908 mHWData->mDnDMode = data.dndMode;
8909
8910 // guest settings
8911 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8912
8913 // IO settings
8914 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8915 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8916
8917 // Host PCI devices
8918 for (settings::HostPCIDeviceAttachmentList::const_iterator
8919 it = data.pciAttachments.begin();
8920 it != data.pciAttachments.end();
8921 ++it)
8922 {
8923 const settings::HostPCIDeviceAttachment &hpda = *it;
8924 ComObjPtr<PCIDeviceAttachment> pda;
8925
8926 pda.createObject();
8927 pda->i_loadSettings(this, hpda);
8928 mHWData->mPCIDeviceAssignments.push_back(pda);
8929 }
8930
8931 /*
8932 * (The following isn't really real hardware, but it lives in HWData
8933 * for reasons of convenience.)
8934 */
8935
8936#ifdef VBOX_WITH_GUEST_PROPS
8937 /* Guest properties (optional) */
8938
8939 /* Only load transient guest properties for configs which have saved
8940 * state, because there shouldn't be any for powered off VMs. The same
8941 * logic applies for snapshots, as offline snapshots shouldn't have
8942 * any such properties. They confuse the code in various places.
8943 * Note: can't rely on the machine state, as it isn't set yet. */
8944 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8945 /* apologies for the hacky unconst() usage, but this needs hacking
8946 * actually inconsistent settings into consistency, otherwise there
8947 * will be some corner cases where the inconsistency survives
8948 * surprisingly long without getting fixed, especially for snapshots
8949 * as there are no config changes. */
8950 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8951 for (settings::GuestPropertiesList::iterator
8952 it = llGuestProperties.begin();
8953 it != llGuestProperties.end();
8954 /*nothing*/)
8955 {
8956 const settings::GuestProperty &prop = *it;
8957 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8958 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8959 if ( fSkipTransientGuestProperties
8960 && ( fFlags & GUEST_PROP_F_TRANSIENT
8961 || fFlags & GUEST_PROP_F_TRANSRESET))
8962 {
8963 it = llGuestProperties.erase(it);
8964 continue;
8965 }
8966 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8967 mHWData->mGuestProperties[prop.strName] = property;
8968 ++it;
8969 }
8970#endif /* VBOX_WITH_GUEST_PROPS defined */
8971
8972 rc = i_loadDebugging(pDbg);
8973 if (FAILED(rc))
8974 return rc;
8975
8976 mHWData->mAutostart = *pAutostart;
8977
8978 /* default frontend */
8979 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8980 }
8981 catch (std::bad_alloc &)
8982 {
8983 return E_OUTOFMEMORY;
8984 }
8985
8986 AssertComRC(rc);
8987 return rc;
8988}
8989
8990/**
8991 * Called from i_loadHardware() to load the debugging settings of the
8992 * machine.
8993 *
8994 * @param pDbg Pointer to the settings.
8995 */
8996HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8997{
8998 mHWData->mDebugging = *pDbg;
8999 /* no more processing currently required, this will probably change. */
9000 return S_OK;
9001}
9002
9003/**
9004 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9005 *
9006 * @param data storage settings.
9007 * @param puuidRegistry media registry ID to set media to or NULL;
9008 * see Machine::i_loadMachineDataFromSettings()
9009 * @param puuidSnapshot snapshot ID
9010 * @return
9011 */
9012HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9013 const Guid *puuidRegistry,
9014 const Guid *puuidSnapshot)
9015{
9016 AssertReturn(!i_isSessionMachine(), E_FAIL);
9017
9018 HRESULT rc = S_OK;
9019
9020 for (settings::StorageControllersList::const_iterator
9021 it = data.llStorageControllers.begin();
9022 it != data.llStorageControllers.end();
9023 ++it)
9024 {
9025 const settings::StorageController &ctlData = *it;
9026
9027 ComObjPtr<StorageController> pCtl;
9028 /* Try to find one with the name first. */
9029 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9030 if (SUCCEEDED(rc))
9031 return setError(VBOX_E_OBJECT_IN_USE,
9032 tr("Storage controller named '%s' already exists"),
9033 ctlData.strName.c_str());
9034
9035 pCtl.createObject();
9036 rc = pCtl->init(this,
9037 ctlData.strName,
9038 ctlData.storageBus,
9039 ctlData.ulInstance,
9040 ctlData.fBootable);
9041 if (FAILED(rc)) return rc;
9042
9043 mStorageControllers->push_back(pCtl);
9044
9045 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9046 if (FAILED(rc)) return rc;
9047
9048 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9049 if (FAILED(rc)) return rc;
9050
9051 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9052 if (FAILED(rc)) return rc;
9053
9054 /* Load the attached devices now. */
9055 rc = i_loadStorageDevices(pCtl,
9056 ctlData,
9057 puuidRegistry,
9058 puuidSnapshot);
9059 if (FAILED(rc)) return rc;
9060 }
9061
9062 return S_OK;
9063}
9064
9065/**
9066 * Called from i_loadStorageControllers for a controller's devices.
9067 *
9068 * @param aStorageController
9069 * @param data
9070 * @param puuidRegistry media registry ID to set media to or NULL; see
9071 * Machine::i_loadMachineDataFromSettings()
9072 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9073 * @return
9074 */
9075HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9076 const settings::StorageController &data,
9077 const Guid *puuidRegistry,
9078 const Guid *puuidSnapshot)
9079{
9080 HRESULT rc = S_OK;
9081
9082 /* paranoia: detect duplicate attachments */
9083 for (settings::AttachedDevicesList::const_iterator
9084 it = data.llAttachedDevices.begin();
9085 it != data.llAttachedDevices.end();
9086 ++it)
9087 {
9088 const settings::AttachedDevice &ad = *it;
9089
9090 for (settings::AttachedDevicesList::const_iterator it2 = it;
9091 it2 != data.llAttachedDevices.end();
9092 ++it2)
9093 {
9094 if (it == it2)
9095 continue;
9096
9097 const settings::AttachedDevice &ad2 = *it2;
9098
9099 if ( ad.lPort == ad2.lPort
9100 && ad.lDevice == ad2.lDevice)
9101 {
9102 return setError(E_FAIL,
9103 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9104 aStorageController->i_getName().c_str(),
9105 ad.lPort,
9106 ad.lDevice,
9107 mUserData->s.strName.c_str());
9108 }
9109 }
9110 }
9111
9112 for (settings::AttachedDevicesList::const_iterator
9113 it = data.llAttachedDevices.begin();
9114 it != data.llAttachedDevices.end();
9115 ++it)
9116 {
9117 const settings::AttachedDevice &dev = *it;
9118 ComObjPtr<Medium> medium;
9119
9120 switch (dev.deviceType)
9121 {
9122 case DeviceType_Floppy:
9123 case DeviceType_DVD:
9124 if (dev.strHostDriveSrc.isNotEmpty())
9125 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9126 false /* fRefresh */, medium);
9127 else
9128 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9129 dev.uuid,
9130 false /* fRefresh */,
9131 false /* aSetError */,
9132 medium);
9133 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9134 // This is not an error. The host drive or UUID might have vanished, so just go
9135 // ahead without this removeable medium attachment
9136 rc = S_OK;
9137 break;
9138
9139 case DeviceType_HardDisk:
9140 {
9141 /* find a hard disk by UUID */
9142 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9143 if (FAILED(rc))
9144 {
9145 if (i_isSnapshotMachine())
9146 {
9147 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9148 // so the user knows that the bad disk is in a snapshot somewhere
9149 com::ErrorInfo info;
9150 return setError(E_FAIL,
9151 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9152 puuidSnapshot->raw(),
9153 info.getText().raw());
9154 }
9155 else
9156 return rc;
9157 }
9158
9159 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9160
9161 if (medium->i_getType() == MediumType_Immutable)
9162 {
9163 if (i_isSnapshotMachine())
9164 return setError(E_FAIL,
9165 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9166 "of the virtual machine '%s' ('%s')"),
9167 medium->i_getLocationFull().c_str(),
9168 dev.uuid.raw(),
9169 puuidSnapshot->raw(),
9170 mUserData->s.strName.c_str(),
9171 mData->m_strConfigFileFull.c_str());
9172
9173 return setError(E_FAIL,
9174 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9175 medium->i_getLocationFull().c_str(),
9176 dev.uuid.raw(),
9177 mUserData->s.strName.c_str(),
9178 mData->m_strConfigFileFull.c_str());
9179 }
9180
9181 if (medium->i_getType() == MediumType_MultiAttach)
9182 {
9183 if (i_isSnapshotMachine())
9184 return setError(E_FAIL,
9185 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9186 "of the virtual machine '%s' ('%s')"),
9187 medium->i_getLocationFull().c_str(),
9188 dev.uuid.raw(),
9189 puuidSnapshot->raw(),
9190 mUserData->s.strName.c_str(),
9191 mData->m_strConfigFileFull.c_str());
9192
9193 return setError(E_FAIL,
9194 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9195 medium->i_getLocationFull().c_str(),
9196 dev.uuid.raw(),
9197 mUserData->s.strName.c_str(),
9198 mData->m_strConfigFileFull.c_str());
9199 }
9200
9201 if ( !i_isSnapshotMachine()
9202 && medium->i_getChildren().size() != 0
9203 )
9204 return setError(E_FAIL,
9205 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9206 "because it has %d differencing child hard disks"),
9207 medium->i_getLocationFull().c_str(),
9208 dev.uuid.raw(),
9209 mUserData->s.strName.c_str(),
9210 mData->m_strConfigFileFull.c_str(),
9211 medium->i_getChildren().size());
9212
9213 if (i_findAttachment(*mMediumAttachments.data(),
9214 medium))
9215 return setError(E_FAIL,
9216 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9217 medium->i_getLocationFull().c_str(),
9218 dev.uuid.raw(),
9219 mUserData->s.strName.c_str(),
9220 mData->m_strConfigFileFull.c_str());
9221
9222 break;
9223 }
9224
9225 default:
9226 return setError(E_FAIL,
9227 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9228 medium->i_getLocationFull().c_str(),
9229 mUserData->s.strName.c_str(),
9230 mData->m_strConfigFileFull.c_str());
9231 }
9232
9233 if (FAILED(rc))
9234 break;
9235
9236 /* Bandwidth groups are loaded at this point. */
9237 ComObjPtr<BandwidthGroup> pBwGroup;
9238
9239 if (!dev.strBwGroup.isEmpty())
9240 {
9241 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9242 if (FAILED(rc))
9243 return setError(E_FAIL,
9244 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9245 medium->i_getLocationFull().c_str(),
9246 dev.strBwGroup.c_str(),
9247 mUserData->s.strName.c_str(),
9248 mData->m_strConfigFileFull.c_str());
9249 pBwGroup->i_reference();
9250 }
9251
9252 const Utf8Str controllerName = aStorageController->i_getName();
9253 ComObjPtr<MediumAttachment> pAttachment;
9254 pAttachment.createObject();
9255 rc = pAttachment->init(this,
9256 medium,
9257 controllerName,
9258 dev.lPort,
9259 dev.lDevice,
9260 dev.deviceType,
9261 false,
9262 dev.fPassThrough,
9263 dev.fTempEject,
9264 dev.fNonRotational,
9265 dev.fDiscard,
9266 dev.fHotPluggable,
9267 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9268 if (FAILED(rc)) break;
9269
9270 /* associate the medium with this machine and snapshot */
9271 if (!medium.isNull())
9272 {
9273 AutoCaller medCaller(medium);
9274 if (FAILED(medCaller.rc())) return medCaller.rc();
9275 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9276
9277 if (i_isSnapshotMachine())
9278 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9279 else
9280 rc = medium->i_addBackReference(mData->mUuid);
9281 /* If the medium->addBackReference fails it sets an appropriate
9282 * error message, so no need to do any guesswork here. */
9283
9284 if (puuidRegistry)
9285 // caller wants registry ID to be set on all attached media (OVF import case)
9286 medium->i_addRegistry(*puuidRegistry);
9287 }
9288
9289 if (FAILED(rc))
9290 break;
9291
9292 /* back up mMediumAttachments to let registeredInit() properly rollback
9293 * on failure (= limited accessibility) */
9294 i_setModified(IsModified_Storage);
9295 mMediumAttachments.backup();
9296 mMediumAttachments->push_back(pAttachment);
9297 }
9298
9299 return rc;
9300}
9301
9302/**
9303 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9304 *
9305 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9306 * @param aSnapshot where to return the found snapshot
9307 * @param aSetError true to set extended error info on failure
9308 */
9309HRESULT Machine::i_findSnapshotById(const Guid &aId,
9310 ComObjPtr<Snapshot> &aSnapshot,
9311 bool aSetError /* = false */)
9312{
9313 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9314
9315 if (!mData->mFirstSnapshot)
9316 {
9317 if (aSetError)
9318 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9319 return E_FAIL;
9320 }
9321
9322 if (aId.isZero())
9323 aSnapshot = mData->mFirstSnapshot;
9324 else
9325 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9326
9327 if (!aSnapshot)
9328 {
9329 if (aSetError)
9330 return setError(E_FAIL,
9331 tr("Could not find a snapshot with UUID {%s}"),
9332 aId.toString().c_str());
9333 return E_FAIL;
9334 }
9335
9336 return S_OK;
9337}
9338
9339/**
9340 * Returns the snapshot with the given name or fails of no such snapshot.
9341 *
9342 * @param strName snapshot name to find
9343 * @param aSnapshot where to return the found snapshot
9344 * @param aSetError true to set extended error info on failure
9345 */
9346HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9347 ComObjPtr<Snapshot> &aSnapshot,
9348 bool aSetError /* = false */)
9349{
9350 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9351
9352 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9353
9354 if (!mData->mFirstSnapshot)
9355 {
9356 if (aSetError)
9357 return setError(VBOX_E_OBJECT_NOT_FOUND,
9358 tr("This machine does not have any snapshots"));
9359 return VBOX_E_OBJECT_NOT_FOUND;
9360 }
9361
9362 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9363
9364 if (!aSnapshot)
9365 {
9366 if (aSetError)
9367 return setError(VBOX_E_OBJECT_NOT_FOUND,
9368 tr("Could not find a snapshot named '%s'"), strName.c_str());
9369 return VBOX_E_OBJECT_NOT_FOUND;
9370 }
9371
9372 return S_OK;
9373}
9374
9375/**
9376 * Returns a storage controller object with the given name.
9377 *
9378 * @param aName storage controller name to find
9379 * @param aStorageController where to return the found storage controller
9380 * @param aSetError true to set extended error info on failure
9381 */
9382HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9383 ComObjPtr<StorageController> &aStorageController,
9384 bool aSetError /* = false */)
9385{
9386 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9387
9388 for (StorageControllerList::const_iterator
9389 it = mStorageControllers->begin();
9390 it != mStorageControllers->end();
9391 ++it)
9392 {
9393 if ((*it)->i_getName() == aName)
9394 {
9395 aStorageController = (*it);
9396 return S_OK;
9397 }
9398 }
9399
9400 if (aSetError)
9401 return setError(VBOX_E_OBJECT_NOT_FOUND,
9402 tr("Could not find a storage controller named '%s'"),
9403 aName.c_str());
9404 return VBOX_E_OBJECT_NOT_FOUND;
9405}
9406
9407/**
9408 * Returns a USB controller object with the given name.
9409 *
9410 * @param aName USB controller name to find
9411 * @param aUSBController where to return the found USB controller
9412 * @param aSetError true to set extended error info on failure
9413 */
9414HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9415 ComObjPtr<USBController> &aUSBController,
9416 bool aSetError /* = false */)
9417{
9418 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9419
9420 for (USBControllerList::const_iterator
9421 it = mUSBControllers->begin();
9422 it != mUSBControllers->end();
9423 ++it)
9424 {
9425 if ((*it)->i_getName() == aName)
9426 {
9427 aUSBController = (*it);
9428 return S_OK;
9429 }
9430 }
9431
9432 if (aSetError)
9433 return setError(VBOX_E_OBJECT_NOT_FOUND,
9434 tr("Could not find a storage controller named '%s'"),
9435 aName.c_str());
9436 return VBOX_E_OBJECT_NOT_FOUND;
9437}
9438
9439/**
9440 * Returns the number of USB controller instance of the given type.
9441 *
9442 * @param enmType USB controller type.
9443 */
9444ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9445{
9446 ULONG cCtrls = 0;
9447
9448 for (USBControllerList::const_iterator
9449 it = mUSBControllers->begin();
9450 it != mUSBControllers->end();
9451 ++it)
9452 {
9453 if ((*it)->i_getControllerType() == enmType)
9454 cCtrls++;
9455 }
9456
9457 return cCtrls;
9458}
9459
9460HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9461 MediumAttachmentList &atts)
9462{
9463 AutoCaller autoCaller(this);
9464 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9465
9466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9467
9468 for (MediumAttachmentList::const_iterator
9469 it = mMediumAttachments->begin();
9470 it != mMediumAttachments->end();
9471 ++it)
9472 {
9473 const ComObjPtr<MediumAttachment> &pAtt = *it;
9474 // should never happen, but deal with NULL pointers in the list.
9475 AssertContinue(!pAtt.isNull());
9476
9477 // getControllerName() needs caller+read lock
9478 AutoCaller autoAttCaller(pAtt);
9479 if (FAILED(autoAttCaller.rc()))
9480 {
9481 atts.clear();
9482 return autoAttCaller.rc();
9483 }
9484 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9485
9486 if (pAtt->i_getControllerName() == aName)
9487 atts.push_back(pAtt);
9488 }
9489
9490 return S_OK;
9491}
9492
9493
9494/**
9495 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9496 * file if the machine name was changed and about creating a new settings file
9497 * if this is a new machine.
9498 *
9499 * @note Must be never called directly but only from #saveSettings().
9500 */
9501HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9502{
9503 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9504
9505 HRESULT rc = S_OK;
9506
9507 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9508
9509 /// @todo need to handle primary group change, too
9510
9511 /* attempt to rename the settings file if machine name is changed */
9512 if ( mUserData->s.fNameSync
9513 && mUserData.isBackedUp()
9514 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9515 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9516 )
9517 {
9518 bool dirRenamed = false;
9519 bool fileRenamed = false;
9520
9521 Utf8Str configFile, newConfigFile;
9522 Utf8Str configFilePrev, newConfigFilePrev;
9523 Utf8Str NVRAMFile, newNVRAMFile;
9524 Utf8Str configDir, newConfigDir;
9525
9526 do
9527 {
9528 int vrc = VINF_SUCCESS;
9529
9530 Utf8Str name = mUserData.backedUpData()->s.strName;
9531 Utf8Str newName = mUserData->s.strName;
9532 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9533 if (group == "/")
9534 group.setNull();
9535 Utf8Str newGroup = mUserData->s.llGroups.front();
9536 if (newGroup == "/")
9537 newGroup.setNull();
9538
9539 configFile = mData->m_strConfigFileFull;
9540
9541 /* first, rename the directory if it matches the group and machine name */
9542 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9543 /** @todo hack, make somehow use of ComposeMachineFilename */
9544 if (mUserData->s.fDirectoryIncludesUUID)
9545 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9546 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9547 /** @todo hack, make somehow use of ComposeMachineFilename */
9548 if (mUserData->s.fDirectoryIncludesUUID)
9549 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9550 configDir = configFile;
9551 configDir.stripFilename();
9552 newConfigDir = configDir;
9553 if ( configDir.length() >= groupPlusName.length()
9554 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9555 groupPlusName.c_str()))
9556 {
9557 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9558 Utf8Str newConfigBaseDir(newConfigDir);
9559 newConfigDir.append(newGroupPlusName);
9560 /* consistency: use \ if appropriate on the platform */
9561 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9562 /* new dir and old dir cannot be equal here because of 'if'
9563 * above and because name != newName */
9564 Assert(configDir != newConfigDir);
9565 if (!fSettingsFileIsNew)
9566 {
9567 /* perform real rename only if the machine is not new */
9568 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9569 if ( vrc == VERR_FILE_NOT_FOUND
9570 || vrc == VERR_PATH_NOT_FOUND)
9571 {
9572 /* create the parent directory, then retry renaming */
9573 Utf8Str parent(newConfigDir);
9574 parent.stripFilename();
9575 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9576 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9577 }
9578 if (RT_FAILURE(vrc))
9579 {
9580 rc = setErrorBoth(E_FAIL, vrc,
9581 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9582 configDir.c_str(),
9583 newConfigDir.c_str(),
9584 vrc);
9585 break;
9586 }
9587 /* delete subdirectories which are no longer needed */
9588 Utf8Str dir(configDir);
9589 dir.stripFilename();
9590 while (dir != newConfigBaseDir && dir != ".")
9591 {
9592 vrc = RTDirRemove(dir.c_str());
9593 if (RT_FAILURE(vrc))
9594 break;
9595 dir.stripFilename();
9596 }
9597 dirRenamed = true;
9598 }
9599 }
9600
9601 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9602
9603 /* then try to rename the settings file itself */
9604 if (newConfigFile != configFile)
9605 {
9606 /* get the path to old settings file in renamed directory */
9607 Assert(mData->m_strConfigFileFull == configFile);
9608 configFile.printf("%s%c%s",
9609 newConfigDir.c_str(),
9610 RTPATH_DELIMITER,
9611 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9612 if (!fSettingsFileIsNew)
9613 {
9614 /* perform real rename only if the machine is not new */
9615 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9616 if (RT_FAILURE(vrc))
9617 {
9618 rc = setErrorBoth(E_FAIL, vrc,
9619 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9620 configFile.c_str(),
9621 newConfigFile.c_str(),
9622 vrc);
9623 break;
9624 }
9625 fileRenamed = true;
9626 configFilePrev = configFile;
9627 configFilePrev += "-prev";
9628 newConfigFilePrev = newConfigFile;
9629 newConfigFilePrev += "-prev";
9630 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9631 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9632 if (NVRAMFile.isNotEmpty())
9633 {
9634 // in the NVRAM file path, replace the old directory with the new directory
9635 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9636 {
9637 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9638 NVRAMFile = newConfigDir + strNVRAMFile;
9639 }
9640 newNVRAMFile = newConfigFile;
9641 newNVRAMFile.stripSuffix();
9642 newNVRAMFile += ".nvram";
9643 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9644 }
9645 }
9646 }
9647
9648 // update m_strConfigFileFull amd mConfigFile
9649 mData->m_strConfigFileFull = newConfigFile;
9650 // compute the relative path too
9651 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9652
9653 // store the old and new so that VirtualBox::i_saveSettings() can update
9654 // the media registry
9655 if ( mData->mRegistered
9656 && (configDir != newConfigDir || configFile != newConfigFile))
9657 {
9658 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9659
9660 if (pfNeedsGlobalSaveSettings)
9661 *pfNeedsGlobalSaveSettings = true;
9662 }
9663
9664 // in the saved state file path, replace the old directory with the new directory
9665 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9666 {
9667 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9668 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9669 }
9670 if (newNVRAMFile.isNotEmpty())
9671 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9672
9673 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9674 if (mData->mFirstSnapshot)
9675 {
9676 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9677 newConfigDir.c_str());
9678 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9679 newConfigDir.c_str());
9680 }
9681 }
9682 while (0);
9683
9684 if (FAILED(rc))
9685 {
9686 /* silently try to rename everything back */
9687 if (fileRenamed)
9688 {
9689 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9690 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9691 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9692 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9693 }
9694 if (dirRenamed)
9695 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9696 }
9697
9698 if (FAILED(rc)) return rc;
9699 }
9700
9701 if (fSettingsFileIsNew)
9702 {
9703 /* create a virgin config file */
9704 int vrc = VINF_SUCCESS;
9705
9706 /* ensure the settings directory exists */
9707 Utf8Str path(mData->m_strConfigFileFull);
9708 path.stripFilename();
9709 if (!RTDirExists(path.c_str()))
9710 {
9711 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9712 if (RT_FAILURE(vrc))
9713 {
9714 return setErrorBoth(E_FAIL, vrc,
9715 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9716 path.c_str(),
9717 vrc);
9718 }
9719 }
9720
9721 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9722 path = Utf8Str(mData->m_strConfigFileFull);
9723 RTFILE f = NIL_RTFILE;
9724 vrc = RTFileOpen(&f, path.c_str(),
9725 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9726 if (RT_FAILURE(vrc))
9727 return setErrorBoth(E_FAIL, vrc,
9728 tr("Could not create the settings file '%s' (%Rrc)"),
9729 path.c_str(),
9730 vrc);
9731 RTFileClose(f);
9732 }
9733
9734 return rc;
9735}
9736
9737/**
9738 * Saves and commits machine data, user data and hardware data.
9739 *
9740 * Note that on failure, the data remains uncommitted.
9741 *
9742 * @a aFlags may combine the following flags:
9743 *
9744 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9745 * Used when saving settings after an operation that makes them 100%
9746 * correspond to the settings from the current snapshot.
9747 * - SaveS_Force: settings will be saved without doing a deep compare of the
9748 * settings structures. This is used when this is called because snapshots
9749 * have changed to avoid the overhead of the deep compare.
9750 *
9751 * @note Must be called from under this object's write lock. Locks children for
9752 * writing.
9753 *
9754 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9755 * initialized to false and that will be set to true by this function if
9756 * the caller must invoke VirtualBox::i_saveSettings() because the global
9757 * settings have changed. This will happen if a machine rename has been
9758 * saved and the global machine and media registries will therefore need
9759 * updating.
9760 * @param aFlags Flags.
9761 */
9762HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9763 int aFlags /*= 0*/)
9764{
9765 LogFlowThisFuncEnter();
9766
9767 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9768
9769 /* make sure child objects are unable to modify the settings while we are
9770 * saving them */
9771 i_ensureNoStateDependencies();
9772
9773 AssertReturn(!i_isSnapshotMachine(),
9774 E_FAIL);
9775
9776 if (!mData->mAccessible)
9777 return setError(VBOX_E_INVALID_VM_STATE,
9778 tr("The machine is not accessible, so cannot save settings"));
9779
9780 HRESULT rc = S_OK;
9781 bool fNeedsWrite = false;
9782
9783 /* First, prepare to save settings. It will care about renaming the
9784 * settings directory and file if the machine name was changed and about
9785 * creating a new settings file if this is a new machine. */
9786 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9787 if (FAILED(rc)) return rc;
9788
9789 // keep a pointer to the current settings structures
9790 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9791 settings::MachineConfigFile *pNewConfig = NULL;
9792
9793 try
9794 {
9795 // make a fresh one to have everyone write stuff into
9796 pNewConfig = new settings::MachineConfigFile(NULL);
9797 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9798
9799 // now go and copy all the settings data from COM to the settings structures
9800 // (this calls i_saveSettings() on all the COM objects in the machine)
9801 i_copyMachineDataToSettings(*pNewConfig);
9802
9803 if (aFlags & SaveS_ResetCurStateModified)
9804 {
9805 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9806 mData->mCurrentStateModified = FALSE;
9807 fNeedsWrite = true; // always, no need to compare
9808 }
9809 else if (aFlags & SaveS_Force)
9810 {
9811 fNeedsWrite = true; // always, no need to compare
9812 }
9813 else
9814 {
9815 if (!mData->mCurrentStateModified)
9816 {
9817 // do a deep compare of the settings that we just saved with the settings
9818 // previously stored in the config file; this invokes MachineConfigFile::operator==
9819 // which does a deep compare of all the settings, which is expensive but less expensive
9820 // than writing out XML in vain
9821 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9822
9823 // could still be modified if any settings changed
9824 mData->mCurrentStateModified = fAnySettingsChanged;
9825
9826 fNeedsWrite = fAnySettingsChanged;
9827 }
9828 else
9829 fNeedsWrite = true;
9830 }
9831
9832 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9833
9834 if (fNeedsWrite)
9835 // now spit it all out!
9836 pNewConfig->write(mData->m_strConfigFileFull);
9837
9838 mData->pMachineConfigFile = pNewConfig;
9839 delete pOldConfig;
9840 i_commit();
9841
9842 // after saving settings, we are no longer different from the XML on disk
9843 mData->flModifications = 0;
9844 }
9845 catch (HRESULT err)
9846 {
9847 // we assume that error info is set by the thrower
9848 rc = err;
9849
9850 // restore old config
9851 delete pNewConfig;
9852 mData->pMachineConfigFile = pOldConfig;
9853 }
9854 catch (...)
9855 {
9856 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9857 }
9858
9859 if (fNeedsWrite)
9860 {
9861 /* Fire the data change event, even on failure (since we've already
9862 * committed all data). This is done only for SessionMachines because
9863 * mutable Machine instances are always not registered (i.e. private
9864 * to the client process that creates them) and thus don't need to
9865 * inform callbacks. */
9866 if (i_isSessionMachine())
9867 mParent->i_onMachineDataChanged(mData->mUuid);
9868 }
9869
9870 LogFlowThisFunc(("rc=%08X\n", rc));
9871 LogFlowThisFuncLeave();
9872 return rc;
9873}
9874
9875/**
9876 * Implementation for saving the machine settings into the given
9877 * settings::MachineConfigFile instance. This copies machine extradata
9878 * from the previous machine config file in the instance data, if any.
9879 *
9880 * This gets called from two locations:
9881 *
9882 * -- Machine::i_saveSettings(), during the regular XML writing;
9883 *
9884 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9885 * exported to OVF and we write the VirtualBox proprietary XML
9886 * into a <vbox:Machine> tag.
9887 *
9888 * This routine fills all the fields in there, including snapshots, *except*
9889 * for the following:
9890 *
9891 * -- fCurrentStateModified. There is some special logic associated with that.
9892 *
9893 * The caller can then call MachineConfigFile::write() or do something else
9894 * with it.
9895 *
9896 * Caller must hold the machine lock!
9897 *
9898 * This throws XML errors and HRESULT, so the caller must have a catch block!
9899 */
9900void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9901{
9902 // deep copy extradata, being extra careful with self assignment (the STL
9903 // map assignment on Mac OS X clang based Xcode isn't checking)
9904 if (&config != mData->pMachineConfigFile)
9905 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9906
9907 config.uuid = mData->mUuid;
9908
9909 // copy name, description, OS type, teleport, UTC etc.
9910 config.machineUserData = mUserData->s;
9911
9912 if ( mData->mMachineState == MachineState_Saved
9913 || mData->mMachineState == MachineState_Restoring
9914 // when doing certain snapshot operations we may or may not have
9915 // a saved state in the current state, so keep everything as is
9916 || ( ( mData->mMachineState == MachineState_Snapshotting
9917 || mData->mMachineState == MachineState_DeletingSnapshot
9918 || mData->mMachineState == MachineState_RestoringSnapshot)
9919 && (!mSSData->strStateFilePath.isEmpty())
9920 )
9921 )
9922 {
9923 Assert(!mSSData->strStateFilePath.isEmpty());
9924 /* try to make the file name relative to the settings file dir */
9925 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9926 }
9927 else
9928 {
9929 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9930 config.strStateFile.setNull();
9931 }
9932
9933 if (mData->mCurrentSnapshot)
9934 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9935 else
9936 config.uuidCurrentSnapshot.clear();
9937
9938 config.timeLastStateChange = mData->mLastStateChange;
9939 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9940 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9941
9942 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9943 if (FAILED(rc)) throw rc;
9944
9945 // save machine's media registry if this is VirtualBox 4.0 or later
9946 if (config.canHaveOwnMediaRegistry())
9947 {
9948 // determine machine folder
9949 Utf8Str strMachineFolder = i_getSettingsFileFull();
9950 strMachineFolder.stripFilename();
9951 mParent->i_saveMediaRegistry(config.mediaRegistry,
9952 i_getId(), // only media with registry ID == machine UUID
9953 strMachineFolder);
9954 // this throws HRESULT
9955 }
9956
9957 // save snapshots
9958 rc = i_saveAllSnapshots(config);
9959 if (FAILED(rc)) throw rc;
9960}
9961
9962/**
9963 * Saves all snapshots of the machine into the given machine config file. Called
9964 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9965 * @param config
9966 * @return
9967 */
9968HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9969{
9970 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9971
9972 HRESULT rc = S_OK;
9973
9974 try
9975 {
9976 config.llFirstSnapshot.clear();
9977
9978 if (mData->mFirstSnapshot)
9979 {
9980 // the settings use a list for "the first snapshot"
9981 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9982
9983 // get reference to the snapshot on the list and work on that
9984 // element straight in the list to avoid excessive copying later
9985 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9986 if (FAILED(rc)) throw rc;
9987 }
9988
9989// if (mType == IsSessionMachine)
9990// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9991
9992 }
9993 catch (HRESULT err)
9994 {
9995 /* we assume that error info is set by the thrower */
9996 rc = err;
9997 }
9998 catch (...)
9999 {
10000 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10001 }
10002
10003 return rc;
10004}
10005
10006/**
10007 * Saves the VM hardware configuration. It is assumed that the
10008 * given node is empty.
10009 *
10010 * @param data Reference to the settings object for the hardware config.
10011 * @param pDbg Pointer to the settings object for the debugging config
10012 * which happens to live in mHWData.
10013 * @param pAutostart Pointer to the settings object for the autostart config
10014 * which happens to live in mHWData.
10015 */
10016HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10017 settings::Autostart *pAutostart)
10018{
10019 HRESULT rc = S_OK;
10020
10021 try
10022 {
10023 /* The hardware version attribute (optional).
10024 Automatically upgrade from 1 to current default hardware version
10025 when there is no saved state. (ugly!) */
10026 if ( mHWData->mHWVersion == "1"
10027 && mSSData->strStateFilePath.isEmpty()
10028 )
10029 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10030
10031 data.strVersion = mHWData->mHWVersion;
10032 data.uuid = mHWData->mHardwareUUID;
10033
10034 // CPU
10035 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10036 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10037 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10038 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10039 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10040 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10041 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10042 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10043 data.fPAE = !!mHWData->mPAEEnabled;
10044 data.enmLongMode = mHWData->mLongMode;
10045 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10046 data.fAPIC = !!mHWData->mAPIC;
10047 data.fX2APIC = !!mHWData->mX2APIC;
10048 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10049 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10050 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10051 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10052 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10053 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10054 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10055 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10056 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10057 data.cCPUs = mHWData->mCPUCount;
10058 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10059 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10060 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10061 data.strCpuProfile = mHWData->mCpuProfile;
10062
10063 data.llCpus.clear();
10064 if (data.fCpuHotPlug)
10065 {
10066 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10067 {
10068 if (mHWData->mCPUAttached[idx])
10069 {
10070 settings::Cpu cpu;
10071 cpu.ulId = idx;
10072 data.llCpus.push_back(cpu);
10073 }
10074 }
10075 }
10076
10077 /* Standard and Extended CPUID leafs. */
10078 data.llCpuIdLeafs.clear();
10079 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10080
10081 // memory
10082 data.ulMemorySizeMB = mHWData->mMemorySize;
10083 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10084
10085 // firmware
10086 data.firmwareType = mHWData->mFirmwareType;
10087
10088 // HID
10089 data.pointingHIDType = mHWData->mPointingHIDType;
10090 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10091
10092 // chipset
10093 data.chipsetType = mHWData->mChipsetType;
10094
10095 // paravirt
10096 data.paravirtProvider = mHWData->mParavirtProvider;
10097 data.strParavirtDebug = mHWData->mParavirtDebug;
10098
10099 // emulated USB card reader
10100 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10101
10102 // HPET
10103 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10104
10105 // boot order
10106 data.mapBootOrder.clear();
10107 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10108 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10109
10110 /* VRDEServer settings (optional) */
10111 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10112 if (FAILED(rc)) throw rc;
10113
10114 /* BIOS settings (required) */
10115 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10116 if (FAILED(rc)) throw rc;
10117
10118 /* Recording settings (required) */
10119 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10120 if (FAILED(rc)) throw rc;
10121
10122 /* GraphicsAdapter settings (required) */
10123 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10124 if (FAILED(rc)) throw rc;
10125
10126 /* USB Controller (required) */
10127 data.usbSettings.llUSBControllers.clear();
10128 for (USBControllerList::const_iterator
10129 it = mUSBControllers->begin();
10130 it != mUSBControllers->end();
10131 ++it)
10132 {
10133 ComObjPtr<USBController> ctrl = *it;
10134 settings::USBController settingsCtrl;
10135
10136 settingsCtrl.strName = ctrl->i_getName();
10137 settingsCtrl.enmType = ctrl->i_getControllerType();
10138
10139 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10140 }
10141
10142 /* USB device filters (required) */
10143 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10144 if (FAILED(rc)) throw rc;
10145
10146 /* Network adapters (required) */
10147 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10148 data.llNetworkAdapters.clear();
10149 /* Write out only the nominal number of network adapters for this
10150 * chipset type. Since Machine::commit() hasn't been called there
10151 * may be extra NIC settings in the vector. */
10152 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10153 {
10154 settings::NetworkAdapter nic;
10155 nic.ulSlot = (uint32_t)slot;
10156 /* paranoia check... must not be NULL, but must not crash either. */
10157 if (mNetworkAdapters[slot])
10158 {
10159 if (mNetworkAdapters[slot]->i_hasDefaults())
10160 continue;
10161
10162 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10163 if (FAILED(rc)) throw rc;
10164
10165 data.llNetworkAdapters.push_back(nic);
10166 }
10167 }
10168
10169 /* Serial ports */
10170 data.llSerialPorts.clear();
10171 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10172 {
10173 if (mSerialPorts[slot]->i_hasDefaults())
10174 continue;
10175
10176 settings::SerialPort s;
10177 s.ulSlot = slot;
10178 rc = mSerialPorts[slot]->i_saveSettings(s);
10179 if (FAILED(rc)) return rc;
10180
10181 data.llSerialPorts.push_back(s);
10182 }
10183
10184 /* Parallel ports */
10185 data.llParallelPorts.clear();
10186 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10187 {
10188 if (mParallelPorts[slot]->i_hasDefaults())
10189 continue;
10190
10191 settings::ParallelPort p;
10192 p.ulSlot = slot;
10193 rc = mParallelPorts[slot]->i_saveSettings(p);
10194 if (FAILED(rc)) return rc;
10195
10196 data.llParallelPorts.push_back(p);
10197 }
10198
10199 /* Audio adapter */
10200 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10201 if (FAILED(rc)) return rc;
10202
10203 rc = i_saveStorageControllers(data.storage);
10204 if (FAILED(rc)) return rc;
10205
10206 /* Shared folders */
10207 data.llSharedFolders.clear();
10208 for (HWData::SharedFolderList::const_iterator
10209 it = mHWData->mSharedFolders.begin();
10210 it != mHWData->mSharedFolders.end();
10211 ++it)
10212 {
10213 SharedFolder *pSF = *it;
10214 AutoCaller sfCaller(pSF);
10215 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10216 settings::SharedFolder sf;
10217 sf.strName = pSF->i_getName();
10218 sf.strHostPath = pSF->i_getHostPath();
10219 sf.fWritable = !!pSF->i_isWritable();
10220 sf.fAutoMount = !!pSF->i_isAutoMounted();
10221 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10222
10223 data.llSharedFolders.push_back(sf);
10224 }
10225
10226 // clipboard
10227 data.clipboardMode = mHWData->mClipboardMode;
10228 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10229
10230 // drag'n'drop
10231 data.dndMode = mHWData->mDnDMode;
10232
10233 /* Guest */
10234 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10235
10236 // IO settings
10237 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10238 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10239
10240 /* BandwidthControl (required) */
10241 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10242 if (FAILED(rc)) throw rc;
10243
10244 /* Host PCI devices */
10245 data.pciAttachments.clear();
10246 for (HWData::PCIDeviceAssignmentList::const_iterator
10247 it = mHWData->mPCIDeviceAssignments.begin();
10248 it != mHWData->mPCIDeviceAssignments.end();
10249 ++it)
10250 {
10251 ComObjPtr<PCIDeviceAttachment> pda = *it;
10252 settings::HostPCIDeviceAttachment hpda;
10253
10254 rc = pda->i_saveSettings(hpda);
10255 if (FAILED(rc)) throw rc;
10256
10257 data.pciAttachments.push_back(hpda);
10258 }
10259
10260 // guest properties
10261 data.llGuestProperties.clear();
10262#ifdef VBOX_WITH_GUEST_PROPS
10263 for (HWData::GuestPropertyMap::const_iterator
10264 it = mHWData->mGuestProperties.begin();
10265 it != mHWData->mGuestProperties.end();
10266 ++it)
10267 {
10268 HWData::GuestProperty property = it->second;
10269
10270 /* Remove transient guest properties at shutdown unless we
10271 * are saving state. Note that restoring snapshot intentionally
10272 * keeps them, they will be removed if appropriate once the final
10273 * machine state is set (as crashes etc. need to work). */
10274 if ( ( mData->mMachineState == MachineState_PoweredOff
10275 || mData->mMachineState == MachineState_Aborted
10276 || mData->mMachineState == MachineState_Teleported)
10277 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10278 continue;
10279 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10280 prop.strName = it->first;
10281 prop.strValue = property.strValue;
10282 prop.timestamp = (uint64_t)property.mTimestamp;
10283 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10284 GuestPropWriteFlags(property.mFlags, szFlags);
10285 prop.strFlags = szFlags;
10286
10287 data.llGuestProperties.push_back(prop);
10288 }
10289
10290 /* I presume this doesn't require a backup(). */
10291 mData->mGuestPropertiesModified = FALSE;
10292#endif /* VBOX_WITH_GUEST_PROPS defined */
10293
10294 *pDbg = mHWData->mDebugging;
10295 *pAutostart = mHWData->mAutostart;
10296
10297 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10298 }
10299 catch (std::bad_alloc &)
10300 {
10301 return E_OUTOFMEMORY;
10302 }
10303
10304 AssertComRC(rc);
10305 return rc;
10306}
10307
10308/**
10309 * Saves the storage controller configuration.
10310 *
10311 * @param data storage settings.
10312 */
10313HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10314{
10315 data.llStorageControllers.clear();
10316
10317 for (StorageControllerList::const_iterator
10318 it = mStorageControllers->begin();
10319 it != mStorageControllers->end();
10320 ++it)
10321 {
10322 HRESULT rc;
10323 ComObjPtr<StorageController> pCtl = *it;
10324
10325 settings::StorageController ctl;
10326 ctl.strName = pCtl->i_getName();
10327 ctl.controllerType = pCtl->i_getControllerType();
10328 ctl.storageBus = pCtl->i_getStorageBus();
10329 ctl.ulInstance = pCtl->i_getInstance();
10330 ctl.fBootable = pCtl->i_getBootable();
10331
10332 /* Save the port count. */
10333 ULONG portCount;
10334 rc = pCtl->COMGETTER(PortCount)(&portCount);
10335 ComAssertComRCRet(rc, rc);
10336 ctl.ulPortCount = portCount;
10337
10338 /* Save fUseHostIOCache */
10339 BOOL fUseHostIOCache;
10340 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10341 ComAssertComRCRet(rc, rc);
10342 ctl.fUseHostIOCache = !!fUseHostIOCache;
10343
10344 /* save the devices now. */
10345 rc = i_saveStorageDevices(pCtl, ctl);
10346 ComAssertComRCRet(rc, rc);
10347
10348 data.llStorageControllers.push_back(ctl);
10349 }
10350
10351 return S_OK;
10352}
10353
10354/**
10355 * Saves the hard disk configuration.
10356 */
10357HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10358 settings::StorageController &data)
10359{
10360 MediumAttachmentList atts;
10361
10362 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10363 if (FAILED(rc)) return rc;
10364
10365 data.llAttachedDevices.clear();
10366 for (MediumAttachmentList::const_iterator
10367 it = atts.begin();
10368 it != atts.end();
10369 ++it)
10370 {
10371 settings::AttachedDevice dev;
10372 IMediumAttachment *iA = *it;
10373 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10374 Medium *pMedium = pAttach->i_getMedium();
10375
10376 dev.deviceType = pAttach->i_getType();
10377 dev.lPort = pAttach->i_getPort();
10378 dev.lDevice = pAttach->i_getDevice();
10379 dev.fPassThrough = pAttach->i_getPassthrough();
10380 dev.fHotPluggable = pAttach->i_getHotPluggable();
10381 if (pMedium)
10382 {
10383 if (pMedium->i_isHostDrive())
10384 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10385 else
10386 dev.uuid = pMedium->i_getId();
10387 dev.fTempEject = pAttach->i_getTempEject();
10388 dev.fNonRotational = pAttach->i_getNonRotational();
10389 dev.fDiscard = pAttach->i_getDiscard();
10390 }
10391
10392 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10393
10394 data.llAttachedDevices.push_back(dev);
10395 }
10396
10397 return S_OK;
10398}
10399
10400/**
10401 * Saves machine state settings as defined by aFlags
10402 * (SaveSTS_* values).
10403 *
10404 * @param aFlags Combination of SaveSTS_* flags.
10405 *
10406 * @note Locks objects for writing.
10407 */
10408HRESULT Machine::i_saveStateSettings(int aFlags)
10409{
10410 if (aFlags == 0)
10411 return S_OK;
10412
10413 AutoCaller autoCaller(this);
10414 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10415
10416 /* This object's write lock is also necessary to serialize file access
10417 * (prevent concurrent reads and writes) */
10418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10419
10420 HRESULT rc = S_OK;
10421
10422 Assert(mData->pMachineConfigFile);
10423
10424 try
10425 {
10426 if (aFlags & SaveSTS_CurStateModified)
10427 mData->pMachineConfigFile->fCurrentStateModified = true;
10428
10429 if (aFlags & SaveSTS_StateFilePath)
10430 {
10431 if (!mSSData->strStateFilePath.isEmpty())
10432 /* try to make the file name relative to the settings file dir */
10433 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10434 else
10435 mData->pMachineConfigFile->strStateFile.setNull();
10436 }
10437
10438 if (aFlags & SaveSTS_StateTimeStamp)
10439 {
10440 Assert( mData->mMachineState != MachineState_Aborted
10441 || mSSData->strStateFilePath.isEmpty());
10442
10443 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10444
10445 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10446/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10447 }
10448
10449 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10450 }
10451 catch (...)
10452 {
10453 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10454 }
10455
10456 return rc;
10457}
10458
10459/**
10460 * Ensures that the given medium is added to a media registry. If this machine
10461 * was created with 4.0 or later, then the machine registry is used. Otherwise
10462 * the global VirtualBox media registry is used.
10463 *
10464 * Caller must NOT hold machine lock, media tree or any medium locks!
10465 *
10466 * @param pMedium
10467 */
10468void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10469{
10470 /* Paranoia checks: do not hold machine or media tree locks. */
10471 AssertReturnVoid(!isWriteLockOnCurrentThread());
10472 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10473
10474 ComObjPtr<Medium> pBase;
10475 {
10476 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10477 pBase = pMedium->i_getBase();
10478 }
10479
10480 /* Paranoia checks: do not hold medium locks. */
10481 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10482 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10483
10484 // decide which medium registry to use now that the medium is attached:
10485 Guid uuid;
10486 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10487 if (fCanHaveOwnMediaRegistry)
10488 // machine XML is VirtualBox 4.0 or higher:
10489 uuid = i_getId(); // machine UUID
10490 else
10491 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10492
10493 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10494 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10495 if (pMedium->i_addRegistry(uuid))
10496 mParent->i_markRegistryModified(uuid);
10497
10498 /* For more complex hard disk structures it can happen that the base
10499 * medium isn't yet associated with any medium registry. Do that now. */
10500 if (pMedium != pBase)
10501 {
10502 /* Tree lock needed by Medium::addRegistry when recursing. */
10503 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10504 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10505 {
10506 treeLock.release();
10507 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10508 treeLock.acquire();
10509 }
10510 if (pBase->i_addRegistryRecursive(uuid))
10511 {
10512 treeLock.release();
10513 mParent->i_markRegistryModified(uuid);
10514 }
10515 }
10516}
10517
10518/**
10519 * Creates differencing hard disks for all normal hard disks attached to this
10520 * machine and a new set of attachments to refer to created disks.
10521 *
10522 * Used when taking a snapshot or when deleting the current state. Gets called
10523 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10524 *
10525 * This method assumes that mMediumAttachments contains the original hard disk
10526 * attachments it needs to create diffs for. On success, these attachments will
10527 * be replaced with the created diffs.
10528 *
10529 * Attachments with non-normal hard disks are left as is.
10530 *
10531 * If @a aOnline is @c false then the original hard disks that require implicit
10532 * diffs will be locked for reading. Otherwise it is assumed that they are
10533 * already locked for writing (when the VM was started). Note that in the latter
10534 * case it is responsibility of the caller to lock the newly created diffs for
10535 * writing if this method succeeds.
10536 *
10537 * @param aProgress Progress object to run (must contain at least as
10538 * many operations left as the number of hard disks
10539 * attached).
10540 * @param aWeight Weight of this operation.
10541 * @param aOnline Whether the VM was online prior to this operation.
10542 *
10543 * @note The progress object is not marked as completed, neither on success nor
10544 * on failure. This is a responsibility of the caller.
10545 *
10546 * @note Locks this object and the media tree for writing.
10547 */
10548HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10549 ULONG aWeight,
10550 bool aOnline)
10551{
10552 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10553
10554 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10555 AssertReturn(!!pProgressControl, E_INVALIDARG);
10556
10557 AutoCaller autoCaller(this);
10558 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10559
10560 AutoMultiWriteLock2 alock(this->lockHandle(),
10561 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10562
10563 /* must be in a protective state because we release the lock below */
10564 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10565 || mData->mMachineState == MachineState_OnlineSnapshotting
10566 || mData->mMachineState == MachineState_LiveSnapshotting
10567 || mData->mMachineState == MachineState_RestoringSnapshot
10568 || mData->mMachineState == MachineState_DeletingSnapshot
10569 , E_FAIL);
10570
10571 HRESULT rc = S_OK;
10572
10573 // use appropriate locked media map (online or offline)
10574 MediumLockListMap lockedMediaOffline;
10575 MediumLockListMap *lockedMediaMap;
10576 if (aOnline)
10577 lockedMediaMap = &mData->mSession.mLockedMedia;
10578 else
10579 lockedMediaMap = &lockedMediaOffline;
10580
10581 try
10582 {
10583 if (!aOnline)
10584 {
10585 /* lock all attached hard disks early to detect "in use"
10586 * situations before creating actual diffs */
10587 for (MediumAttachmentList::const_iterator
10588 it = mMediumAttachments->begin();
10589 it != mMediumAttachments->end();
10590 ++it)
10591 {
10592 MediumAttachment *pAtt = *it;
10593 if (pAtt->i_getType() == DeviceType_HardDisk)
10594 {
10595 Medium *pMedium = pAtt->i_getMedium();
10596 Assert(pMedium);
10597
10598 MediumLockList *pMediumLockList(new MediumLockList());
10599 alock.release();
10600 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10601 NULL /* pToLockWrite */,
10602 false /* fMediumLockWriteAll */,
10603 NULL,
10604 *pMediumLockList);
10605 alock.acquire();
10606 if (FAILED(rc))
10607 {
10608 delete pMediumLockList;
10609 throw rc;
10610 }
10611 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10612 if (FAILED(rc))
10613 {
10614 throw setError(rc,
10615 tr("Collecting locking information for all attached media failed"));
10616 }
10617 }
10618 }
10619
10620 /* Now lock all media. If this fails, nothing is locked. */
10621 alock.release();
10622 rc = lockedMediaMap->Lock();
10623 alock.acquire();
10624 if (FAILED(rc))
10625 {
10626 throw setError(rc,
10627 tr("Locking of attached media failed"));
10628 }
10629 }
10630
10631 /* remember the current list (note that we don't use backup() since
10632 * mMediumAttachments may be already backed up) */
10633 MediumAttachmentList atts = *mMediumAttachments.data();
10634
10635 /* start from scratch */
10636 mMediumAttachments->clear();
10637
10638 /* go through remembered attachments and create diffs for normal hard
10639 * disks and attach them */
10640 for (MediumAttachmentList::const_iterator
10641 it = atts.begin();
10642 it != atts.end();
10643 ++it)
10644 {
10645 MediumAttachment *pAtt = *it;
10646
10647 DeviceType_T devType = pAtt->i_getType();
10648 Medium *pMedium = pAtt->i_getMedium();
10649
10650 if ( devType != DeviceType_HardDisk
10651 || pMedium == NULL
10652 || pMedium->i_getType() != MediumType_Normal)
10653 {
10654 /* copy the attachment as is */
10655
10656 /** @todo the progress object created in SessionMachine::TakeSnaphot
10657 * only expects operations for hard disks. Later other
10658 * device types need to show up in the progress as well. */
10659 if (devType == DeviceType_HardDisk)
10660 {
10661 if (pMedium == NULL)
10662 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10663 aWeight); // weight
10664 else
10665 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10666 pMedium->i_getBase()->i_getName().c_str()).raw(),
10667 aWeight); // weight
10668 }
10669
10670 mMediumAttachments->push_back(pAtt);
10671 continue;
10672 }
10673
10674 /* need a diff */
10675 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10676 pMedium->i_getBase()->i_getName().c_str()).raw(),
10677 aWeight); // weight
10678
10679 Utf8Str strFullSnapshotFolder;
10680 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10681
10682 ComObjPtr<Medium> diff;
10683 diff.createObject();
10684 // store the diff in the same registry as the parent
10685 // (this cannot fail here because we can't create implicit diffs for
10686 // unregistered images)
10687 Guid uuidRegistryParent;
10688 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10689 Assert(fInRegistry); NOREF(fInRegistry);
10690 rc = diff->init(mParent,
10691 pMedium->i_getPreferredDiffFormat(),
10692 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10693 uuidRegistryParent,
10694 DeviceType_HardDisk);
10695 if (FAILED(rc)) throw rc;
10696
10697 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10698 * the push_back? Looks like we're going to release medium with the
10699 * wrong kind of lock (general issue with if we fail anywhere at all)
10700 * and an orphaned VDI in the snapshots folder. */
10701
10702 /* update the appropriate lock list */
10703 MediumLockList *pMediumLockList;
10704 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10705 AssertComRCThrowRC(rc);
10706 if (aOnline)
10707 {
10708 alock.release();
10709 /* The currently attached medium will be read-only, change
10710 * the lock type to read. */
10711 rc = pMediumLockList->Update(pMedium, false);
10712 alock.acquire();
10713 AssertComRCThrowRC(rc);
10714 }
10715
10716 /* release the locks before the potentially lengthy operation */
10717 alock.release();
10718 rc = pMedium->i_createDiffStorage(diff,
10719 pMedium->i_getPreferredDiffVariant(),
10720 pMediumLockList,
10721 NULL /* aProgress */,
10722 true /* aWait */,
10723 false /* aNotify */);
10724 alock.acquire();
10725 if (FAILED(rc)) throw rc;
10726
10727 /* actual lock list update is done in Machine::i_commitMedia */
10728
10729 rc = diff->i_addBackReference(mData->mUuid);
10730 AssertComRCThrowRC(rc);
10731
10732 /* add a new attachment */
10733 ComObjPtr<MediumAttachment> attachment;
10734 attachment.createObject();
10735 rc = attachment->init(this,
10736 diff,
10737 pAtt->i_getControllerName(),
10738 pAtt->i_getPort(),
10739 pAtt->i_getDevice(),
10740 DeviceType_HardDisk,
10741 true /* aImplicit */,
10742 false /* aPassthrough */,
10743 false /* aTempEject */,
10744 pAtt->i_getNonRotational(),
10745 pAtt->i_getDiscard(),
10746 pAtt->i_getHotPluggable(),
10747 pAtt->i_getBandwidthGroup());
10748 if (FAILED(rc)) throw rc;
10749
10750 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10751 AssertComRCThrowRC(rc);
10752 mMediumAttachments->push_back(attachment);
10753 }
10754 }
10755 catch (HRESULT aRC) { rc = aRC; }
10756
10757 /* unlock all hard disks we locked when there is no VM */
10758 if (!aOnline)
10759 {
10760 ErrorInfoKeeper eik;
10761
10762 HRESULT rc1 = lockedMediaMap->Clear();
10763 AssertComRC(rc1);
10764 }
10765
10766 return rc;
10767}
10768
10769/**
10770 * Deletes implicit differencing hard disks created either by
10771 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10772 * mMediumAttachments.
10773 *
10774 * Note that to delete hard disks created by #attachDevice() this method is
10775 * called from #i_rollbackMedia() when the changes are rolled back.
10776 *
10777 * @note Locks this object and the media tree for writing.
10778 */
10779HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10780{
10781 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10782
10783 AutoCaller autoCaller(this);
10784 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10785
10786 AutoMultiWriteLock2 alock(this->lockHandle(),
10787 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10788
10789 /* We absolutely must have backed up state. */
10790 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10791
10792 /* Check if there are any implicitly created diff images. */
10793 bool fImplicitDiffs = false;
10794 for (MediumAttachmentList::const_iterator
10795 it = mMediumAttachments->begin();
10796 it != mMediumAttachments->end();
10797 ++it)
10798 {
10799 const ComObjPtr<MediumAttachment> &pAtt = *it;
10800 if (pAtt->i_isImplicit())
10801 {
10802 fImplicitDiffs = true;
10803 break;
10804 }
10805 }
10806 /* If there is nothing to do, leave early. This saves lots of image locking
10807 * effort. It also avoids a MachineStateChanged event without real reason.
10808 * This is important e.g. when loading a VM config, because there should be
10809 * no events. Otherwise API clients can become thoroughly confused for
10810 * inaccessible VMs (the code for loading VM configs uses this method for
10811 * cleanup if the config makes no sense), as they take such events as an
10812 * indication that the VM is alive, and they would force the VM config to
10813 * be reread, leading to an endless loop. */
10814 if (!fImplicitDiffs)
10815 return S_OK;
10816
10817 HRESULT rc = S_OK;
10818 MachineState_T oldState = mData->mMachineState;
10819
10820 /* will release the lock before the potentially lengthy operation,
10821 * so protect with the special state (unless already protected) */
10822 if ( oldState != MachineState_Snapshotting
10823 && oldState != MachineState_OnlineSnapshotting
10824 && oldState != MachineState_LiveSnapshotting
10825 && oldState != MachineState_RestoringSnapshot
10826 && oldState != MachineState_DeletingSnapshot
10827 && oldState != MachineState_DeletingSnapshotOnline
10828 && oldState != MachineState_DeletingSnapshotPaused
10829 )
10830 i_setMachineState(MachineState_SettingUp);
10831
10832 // use appropriate locked media map (online or offline)
10833 MediumLockListMap lockedMediaOffline;
10834 MediumLockListMap *lockedMediaMap;
10835 if (aOnline)
10836 lockedMediaMap = &mData->mSession.mLockedMedia;
10837 else
10838 lockedMediaMap = &lockedMediaOffline;
10839
10840 try
10841 {
10842 if (!aOnline)
10843 {
10844 /* lock all attached hard disks early to detect "in use"
10845 * situations before deleting actual diffs */
10846 for (MediumAttachmentList::const_iterator
10847 it = mMediumAttachments->begin();
10848 it != mMediumAttachments->end();
10849 ++it)
10850 {
10851 MediumAttachment *pAtt = *it;
10852 if (pAtt->i_getType() == DeviceType_HardDisk)
10853 {
10854 Medium *pMedium = pAtt->i_getMedium();
10855 Assert(pMedium);
10856
10857 MediumLockList *pMediumLockList(new MediumLockList());
10858 alock.release();
10859 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10860 NULL /* pToLockWrite */,
10861 false /* fMediumLockWriteAll */,
10862 NULL,
10863 *pMediumLockList);
10864 alock.acquire();
10865
10866 if (FAILED(rc))
10867 {
10868 delete pMediumLockList;
10869 throw rc;
10870 }
10871
10872 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10873 if (FAILED(rc))
10874 throw rc;
10875 }
10876 }
10877
10878 if (FAILED(rc))
10879 throw rc;
10880 } // end of offline
10881
10882 /* Lock lists are now up to date and include implicitly created media */
10883
10884 /* Go through remembered attachments and delete all implicitly created
10885 * diffs and fix up the attachment information */
10886 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10887 MediumAttachmentList implicitAtts;
10888 for (MediumAttachmentList::const_iterator
10889 it = mMediumAttachments->begin();
10890 it != mMediumAttachments->end();
10891 ++it)
10892 {
10893 ComObjPtr<MediumAttachment> pAtt = *it;
10894 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10895 if (pMedium.isNull())
10896 continue;
10897
10898 // Implicit attachments go on the list for deletion and back references are removed.
10899 if (pAtt->i_isImplicit())
10900 {
10901 /* Deassociate and mark for deletion */
10902 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10903 rc = pMedium->i_removeBackReference(mData->mUuid);
10904 if (FAILED(rc))
10905 throw rc;
10906 implicitAtts.push_back(pAtt);
10907 continue;
10908 }
10909
10910 /* Was this medium attached before? */
10911 if (!i_findAttachment(oldAtts, pMedium))
10912 {
10913 /* no: de-associate */
10914 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10915 rc = pMedium->i_removeBackReference(mData->mUuid);
10916 if (FAILED(rc))
10917 throw rc;
10918 continue;
10919 }
10920 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10921 }
10922
10923 /* If there are implicit attachments to delete, throw away the lock
10924 * map contents (which will unlock all media) since the medium
10925 * attachments will be rolled back. Below we need to completely
10926 * recreate the lock map anyway since it is infinitely complex to
10927 * do this incrementally (would need reconstructing each attachment
10928 * change, which would be extremely hairy). */
10929 if (implicitAtts.size() != 0)
10930 {
10931 ErrorInfoKeeper eik;
10932
10933 HRESULT rc1 = lockedMediaMap->Clear();
10934 AssertComRC(rc1);
10935 }
10936
10937 /* rollback hard disk changes */
10938 mMediumAttachments.rollback();
10939
10940 MultiResult mrc(S_OK);
10941
10942 // Delete unused implicit diffs.
10943 if (implicitAtts.size() != 0)
10944 {
10945 alock.release();
10946
10947 for (MediumAttachmentList::const_iterator
10948 it = implicitAtts.begin();
10949 it != implicitAtts.end();
10950 ++it)
10951 {
10952 // Remove medium associated with this attachment.
10953 ComObjPtr<MediumAttachment> pAtt = *it;
10954 Assert(pAtt);
10955 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10956 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10957 Assert(pMedium);
10958
10959 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10960 // continue on delete failure, just collect error messages
10961 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10962 pMedium->i_getLocationFull().c_str() ));
10963 mrc = rc;
10964 }
10965 // Clear the list of deleted implicit attachments now, while not
10966 // holding the lock, as it will ultimately trigger Medium::uninit()
10967 // calls which assume that the media tree lock isn't held.
10968 implicitAtts.clear();
10969
10970 alock.acquire();
10971
10972 /* if there is a VM recreate media lock map as mentioned above,
10973 * otherwise it is a waste of time and we leave things unlocked */
10974 if (aOnline)
10975 {
10976 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10977 /* must never be NULL, but better safe than sorry */
10978 if (!pMachine.isNull())
10979 {
10980 alock.release();
10981 rc = mData->mSession.mMachine->i_lockMedia();
10982 alock.acquire();
10983 if (FAILED(rc))
10984 throw rc;
10985 }
10986 }
10987 }
10988 }
10989 catch (HRESULT aRC) {rc = aRC;}
10990
10991 if (mData->mMachineState == MachineState_SettingUp)
10992 i_setMachineState(oldState);
10993
10994 /* unlock all hard disks we locked when there is no VM */
10995 if (!aOnline)
10996 {
10997 ErrorInfoKeeper eik;
10998
10999 HRESULT rc1 = lockedMediaMap->Clear();
11000 AssertComRC(rc1);
11001 }
11002
11003 return rc;
11004}
11005
11006
11007/**
11008 * Looks through the given list of media attachments for one with the given parameters
11009 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11010 * can be searched as well if needed.
11011 *
11012 * @param ll
11013 * @param aControllerName
11014 * @param aControllerPort
11015 * @param aDevice
11016 * @return
11017 */
11018MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11019 const Utf8Str &aControllerName,
11020 LONG aControllerPort,
11021 LONG aDevice)
11022{
11023 for (MediumAttachmentList::const_iterator
11024 it = ll.begin();
11025 it != ll.end();
11026 ++it)
11027 {
11028 MediumAttachment *pAttach = *it;
11029 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11030 return pAttach;
11031 }
11032
11033 return NULL;
11034}
11035
11036/**
11037 * Looks through the given list of media attachments for one with the given parameters
11038 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11039 * can be searched as well if needed.
11040 *
11041 * @param ll
11042 * @param pMedium
11043 * @return
11044 */
11045MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11046 ComObjPtr<Medium> pMedium)
11047{
11048 for (MediumAttachmentList::const_iterator
11049 it = ll.begin();
11050 it != ll.end();
11051 ++it)
11052 {
11053 MediumAttachment *pAttach = *it;
11054 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11055 if (pMediumThis == pMedium)
11056 return pAttach;
11057 }
11058
11059 return NULL;
11060}
11061
11062/**
11063 * Looks through the given list of media attachments for one with the given parameters
11064 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11065 * can be searched as well if needed.
11066 *
11067 * @param ll
11068 * @param id
11069 * @return
11070 */
11071MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11072 Guid &id)
11073{
11074 for (MediumAttachmentList::const_iterator
11075 it = ll.begin();
11076 it != ll.end();
11077 ++it)
11078 {
11079 MediumAttachment *pAttach = *it;
11080 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11081 if (pMediumThis->i_getId() == id)
11082 return pAttach;
11083 }
11084
11085 return NULL;
11086}
11087
11088/**
11089 * Main implementation for Machine::DetachDevice. This also gets called
11090 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11091 *
11092 * @param pAttach Medium attachment to detach.
11093 * @param writeLock Machine write lock which the caller must have locked once.
11094 * This may be released temporarily in here.
11095 * @param pSnapshot If NULL, then the detachment is for the current machine.
11096 * Otherwise this is for a SnapshotMachine, and this must be
11097 * its snapshot.
11098 * @return
11099 */
11100HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11101 AutoWriteLock &writeLock,
11102 Snapshot *pSnapshot)
11103{
11104 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11105 DeviceType_T mediumType = pAttach->i_getType();
11106
11107 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11108
11109 if (pAttach->i_isImplicit())
11110 {
11111 /* attempt to implicitly delete the implicitly created diff */
11112
11113 /// @todo move the implicit flag from MediumAttachment to Medium
11114 /// and forbid any hard disk operation when it is implicit. Or maybe
11115 /// a special media state for it to make it even more simple.
11116
11117 Assert(mMediumAttachments.isBackedUp());
11118
11119 /* will release the lock before the potentially lengthy operation, so
11120 * protect with the special state */
11121 MachineState_T oldState = mData->mMachineState;
11122 i_setMachineState(MachineState_SettingUp);
11123
11124 writeLock.release();
11125
11126 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11127 true /*aWait*/,
11128 false /*aNotify*/);
11129
11130 writeLock.acquire();
11131
11132 i_setMachineState(oldState);
11133
11134 if (FAILED(rc)) return rc;
11135 }
11136
11137 i_setModified(IsModified_Storage);
11138 mMediumAttachments.backup();
11139 mMediumAttachments->remove(pAttach);
11140
11141 if (!oldmedium.isNull())
11142 {
11143 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11144 if (pSnapshot)
11145 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11146 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11147 else if (mediumType != DeviceType_HardDisk)
11148 oldmedium->i_removeBackReference(mData->mUuid);
11149 }
11150
11151 return S_OK;
11152}
11153
11154/**
11155 * Goes thru all media of the given list and
11156 *
11157 * 1) calls i_detachDevice() on each of them for this machine and
11158 * 2) adds all Medium objects found in the process to the given list,
11159 * depending on cleanupMode.
11160 *
11161 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11162 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11163 * media to the list.
11164 *
11165 * This gets called from Machine::Unregister, both for the actual Machine and
11166 * the SnapshotMachine objects that might be found in the snapshots.
11167 *
11168 * Requires caller and locking. The machine lock must be passed in because it
11169 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11170 *
11171 * @param writeLock Machine lock from top-level caller; this gets passed to
11172 * i_detachDevice.
11173 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11174 * object if called for a SnapshotMachine.
11175 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11176 * added to llMedia; if Full, then all media get added;
11177 * otherwise no media get added.
11178 * @param llMedia Caller's list to receive Medium objects which got detached so
11179 * caller can close() them, depending on cleanupMode.
11180 * @return
11181 */
11182HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11183 Snapshot *pSnapshot,
11184 CleanupMode_T cleanupMode,
11185 MediaList &llMedia)
11186{
11187 Assert(isWriteLockOnCurrentThread());
11188
11189 HRESULT rc;
11190
11191 // make a temporary list because i_detachDevice invalidates iterators into
11192 // mMediumAttachments
11193 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11194
11195 for (MediumAttachmentList::iterator
11196 it = llAttachments2.begin();
11197 it != llAttachments2.end();
11198 ++it)
11199 {
11200 ComObjPtr<MediumAttachment> &pAttach = *it;
11201 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11202
11203 if (!pMedium.isNull())
11204 {
11205 AutoCaller mac(pMedium);
11206 if (FAILED(mac.rc())) return mac.rc();
11207 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11208 DeviceType_T devType = pMedium->i_getDeviceType();
11209 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11210 && devType == DeviceType_HardDisk)
11211 || (cleanupMode == CleanupMode_Full)
11212 )
11213 {
11214 llMedia.push_back(pMedium);
11215 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11216 /* Not allowed to keep this lock as below we need the parent
11217 * medium lock, and the lock order is parent to child. */
11218 lock.release();
11219 /*
11220 * Search for medias which are not attached to any machine, but
11221 * in the chain to an attached disk. Mediums are only consided
11222 * if they are:
11223 * - have only one child
11224 * - no references to any machines
11225 * - are of normal medium type
11226 */
11227 while (!pParent.isNull())
11228 {
11229 AutoCaller mac1(pParent);
11230 if (FAILED(mac1.rc())) return mac1.rc();
11231 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11232 if (pParent->i_getChildren().size() == 1)
11233 {
11234 if ( pParent->i_getMachineBackRefCount() == 0
11235 && pParent->i_getType() == MediumType_Normal
11236 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11237 llMedia.push_back(pParent);
11238 }
11239 else
11240 break;
11241 pParent = pParent->i_getParent();
11242 }
11243 }
11244 }
11245
11246 // real machine: then we need to use the proper method
11247 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11248
11249 if (FAILED(rc))
11250 return rc;
11251 }
11252
11253 return S_OK;
11254}
11255
11256/**
11257 * Perform deferred hard disk detachments.
11258 *
11259 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11260 * changed (not backed up).
11261 *
11262 * If @a aOnline is @c true then this method will also unlock the old hard
11263 * disks for which the new implicit diffs were created and will lock these new
11264 * diffs for writing.
11265 *
11266 * @param aOnline Whether the VM was online prior to this operation.
11267 *
11268 * @note Locks this object for writing!
11269 */
11270void Machine::i_commitMedia(bool aOnline /*= false*/)
11271{
11272 AutoCaller autoCaller(this);
11273 AssertComRCReturnVoid(autoCaller.rc());
11274
11275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11276
11277 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11278
11279 HRESULT rc = S_OK;
11280
11281 /* no attach/detach operations -- nothing to do */
11282 if (!mMediumAttachments.isBackedUp())
11283 return;
11284
11285 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11286 bool fMediaNeedsLocking = false;
11287
11288 /* enumerate new attachments */
11289 for (MediumAttachmentList::const_iterator
11290 it = mMediumAttachments->begin();
11291 it != mMediumAttachments->end();
11292 ++it)
11293 {
11294 MediumAttachment *pAttach = *it;
11295
11296 pAttach->i_commit();
11297
11298 Medium *pMedium = pAttach->i_getMedium();
11299 bool fImplicit = pAttach->i_isImplicit();
11300
11301 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11302 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11303 fImplicit));
11304
11305 /** @todo convert all this Machine-based voodoo to MediumAttachment
11306 * based commit logic. */
11307 if (fImplicit)
11308 {
11309 /* convert implicit attachment to normal */
11310 pAttach->i_setImplicit(false);
11311
11312 if ( aOnline
11313 && pMedium
11314 && pAttach->i_getType() == DeviceType_HardDisk
11315 )
11316 {
11317 /* update the appropriate lock list */
11318 MediumLockList *pMediumLockList;
11319 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11320 AssertComRC(rc);
11321 if (pMediumLockList)
11322 {
11323 /* unlock if there's a need to change the locking */
11324 if (!fMediaNeedsLocking)
11325 {
11326 rc = mData->mSession.mLockedMedia.Unlock();
11327 AssertComRC(rc);
11328 fMediaNeedsLocking = true;
11329 }
11330 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11331 AssertComRC(rc);
11332 rc = pMediumLockList->Append(pMedium, true);
11333 AssertComRC(rc);
11334 }
11335 }
11336
11337 continue;
11338 }
11339
11340 if (pMedium)
11341 {
11342 /* was this medium attached before? */
11343 for (MediumAttachmentList::iterator
11344 oldIt = oldAtts.begin();
11345 oldIt != oldAtts.end();
11346 ++oldIt)
11347 {
11348 MediumAttachment *pOldAttach = *oldIt;
11349 if (pOldAttach->i_getMedium() == pMedium)
11350 {
11351 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11352
11353 /* yes: remove from old to avoid de-association */
11354 oldAtts.erase(oldIt);
11355 break;
11356 }
11357 }
11358 }
11359 }
11360
11361 /* enumerate remaining old attachments and de-associate from the
11362 * current machine state */
11363 for (MediumAttachmentList::const_iterator
11364 it = oldAtts.begin();
11365 it != oldAtts.end();
11366 ++it)
11367 {
11368 MediumAttachment *pAttach = *it;
11369 Medium *pMedium = pAttach->i_getMedium();
11370
11371 /* Detach only hard disks, since DVD/floppy media is detached
11372 * instantly in MountMedium. */
11373 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11374 {
11375 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11376
11377 /* now de-associate from the current machine state */
11378 rc = pMedium->i_removeBackReference(mData->mUuid);
11379 AssertComRC(rc);
11380
11381 if (aOnline)
11382 {
11383 /* unlock since medium is not used anymore */
11384 MediumLockList *pMediumLockList;
11385 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11386 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11387 {
11388 /* this happens for online snapshots, there the attachment
11389 * is changing, but only to a diff image created under
11390 * the old one, so there is no separate lock list */
11391 Assert(!pMediumLockList);
11392 }
11393 else
11394 {
11395 AssertComRC(rc);
11396 if (pMediumLockList)
11397 {
11398 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11399 AssertComRC(rc);
11400 }
11401 }
11402 }
11403 }
11404 }
11405
11406 /* take media locks again so that the locking state is consistent */
11407 if (fMediaNeedsLocking)
11408 {
11409 Assert(aOnline);
11410 rc = mData->mSession.mLockedMedia.Lock();
11411 AssertComRC(rc);
11412 }
11413
11414 /* commit the hard disk changes */
11415 mMediumAttachments.commit();
11416
11417 if (i_isSessionMachine())
11418 {
11419 /*
11420 * Update the parent machine to point to the new owner.
11421 * This is necessary because the stored parent will point to the
11422 * session machine otherwise and cause crashes or errors later
11423 * when the session machine gets invalid.
11424 */
11425 /** @todo Change the MediumAttachment class to behave like any other
11426 * class in this regard by creating peer MediumAttachment
11427 * objects for session machines and share the data with the peer
11428 * machine.
11429 */
11430 for (MediumAttachmentList::const_iterator
11431 it = mMediumAttachments->begin();
11432 it != mMediumAttachments->end();
11433 ++it)
11434 (*it)->i_updateParentMachine(mPeer);
11435
11436 /* attach new data to the primary machine and reshare it */
11437 mPeer->mMediumAttachments.attach(mMediumAttachments);
11438 }
11439
11440 return;
11441}
11442
11443/**
11444 * Perform deferred deletion of implicitly created diffs.
11445 *
11446 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11447 * changed (not backed up).
11448 *
11449 * @note Locks this object for writing!
11450 */
11451void Machine::i_rollbackMedia()
11452{
11453 AutoCaller autoCaller(this);
11454 AssertComRCReturnVoid(autoCaller.rc());
11455
11456 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11457 LogFlowThisFunc(("Entering rollbackMedia\n"));
11458
11459 HRESULT rc = S_OK;
11460
11461 /* no attach/detach operations -- nothing to do */
11462 if (!mMediumAttachments.isBackedUp())
11463 return;
11464
11465 /* enumerate new attachments */
11466 for (MediumAttachmentList::const_iterator
11467 it = mMediumAttachments->begin();
11468 it != mMediumAttachments->end();
11469 ++it)
11470 {
11471 MediumAttachment *pAttach = *it;
11472 /* Fix up the backrefs for DVD/floppy media. */
11473 if (pAttach->i_getType() != DeviceType_HardDisk)
11474 {
11475 Medium *pMedium = pAttach->i_getMedium();
11476 if (pMedium)
11477 {
11478 rc = pMedium->i_removeBackReference(mData->mUuid);
11479 AssertComRC(rc);
11480 }
11481 }
11482
11483 (*it)->i_rollback();
11484
11485 pAttach = *it;
11486 /* Fix up the backrefs for DVD/floppy media. */
11487 if (pAttach->i_getType() != DeviceType_HardDisk)
11488 {
11489 Medium *pMedium = pAttach->i_getMedium();
11490 if (pMedium)
11491 {
11492 rc = pMedium->i_addBackReference(mData->mUuid);
11493 AssertComRC(rc);
11494 }
11495 }
11496 }
11497
11498 /** @todo convert all this Machine-based voodoo to MediumAttachment
11499 * based rollback logic. */
11500 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11501
11502 return;
11503}
11504
11505/**
11506 * Returns true if the settings file is located in the directory named exactly
11507 * as the machine; this means, among other things, that the machine directory
11508 * should be auto-renamed.
11509 *
11510 * @param aSettingsDir if not NULL, the full machine settings file directory
11511 * name will be assigned there.
11512 *
11513 * @note Doesn't lock anything.
11514 * @note Not thread safe (must be called from this object's lock).
11515 */
11516bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11517{
11518 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11519 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11520 if (aSettingsDir)
11521 *aSettingsDir = strMachineDirName;
11522 strMachineDirName.stripPath(); // vmname
11523 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11524 strConfigFileOnly.stripPath() // vmname.vbox
11525 .stripSuffix(); // vmname
11526 /** @todo hack, make somehow use of ComposeMachineFilename */
11527 if (mUserData->s.fDirectoryIncludesUUID)
11528 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11529
11530 AssertReturn(!strMachineDirName.isEmpty(), false);
11531 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11532
11533 return strMachineDirName == strConfigFileOnly;
11534}
11535
11536/**
11537 * Discards all changes to machine settings.
11538 *
11539 * @param aNotify Whether to notify the direct session about changes or not.
11540 *
11541 * @note Locks objects for writing!
11542 */
11543void Machine::i_rollback(bool aNotify)
11544{
11545 AutoCaller autoCaller(this);
11546 AssertComRCReturn(autoCaller.rc(), (void)0);
11547
11548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11549
11550 if (!mStorageControllers.isNull())
11551 {
11552 if (mStorageControllers.isBackedUp())
11553 {
11554 /* unitialize all new devices (absent in the backed up list). */
11555 StorageControllerList *backedList = mStorageControllers.backedUpData();
11556 for (StorageControllerList::const_iterator
11557 it = mStorageControllers->begin();
11558 it != mStorageControllers->end();
11559 ++it)
11560 {
11561 if ( std::find(backedList->begin(), backedList->end(), *it)
11562 == backedList->end()
11563 )
11564 {
11565 (*it)->uninit();
11566 }
11567 }
11568
11569 /* restore the list */
11570 mStorageControllers.rollback();
11571 }
11572
11573 /* rollback any changes to devices after restoring the list */
11574 if (mData->flModifications & IsModified_Storage)
11575 {
11576 for (StorageControllerList::const_iterator
11577 it = mStorageControllers->begin();
11578 it != mStorageControllers->end();
11579 ++it)
11580 {
11581 (*it)->i_rollback();
11582 }
11583 }
11584 }
11585
11586 if (!mUSBControllers.isNull())
11587 {
11588 if (mUSBControllers.isBackedUp())
11589 {
11590 /* unitialize all new devices (absent in the backed up list). */
11591 USBControllerList *backedList = mUSBControllers.backedUpData();
11592 for (USBControllerList::const_iterator
11593 it = mUSBControllers->begin();
11594 it != mUSBControllers->end();
11595 ++it)
11596 {
11597 if ( std::find(backedList->begin(), backedList->end(), *it)
11598 == backedList->end()
11599 )
11600 {
11601 (*it)->uninit();
11602 }
11603 }
11604
11605 /* restore the list */
11606 mUSBControllers.rollback();
11607 }
11608
11609 /* rollback any changes to devices after restoring the list */
11610 if (mData->flModifications & IsModified_USB)
11611 {
11612 for (USBControllerList::const_iterator
11613 it = mUSBControllers->begin();
11614 it != mUSBControllers->end();
11615 ++it)
11616 {
11617 (*it)->i_rollback();
11618 }
11619 }
11620 }
11621
11622 mUserData.rollback();
11623
11624 mHWData.rollback();
11625
11626 if (mData->flModifications & IsModified_Storage)
11627 i_rollbackMedia();
11628
11629 if (mBIOSSettings)
11630 mBIOSSettings->i_rollback();
11631
11632 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11633 mRecordingSettings->i_rollback();
11634
11635 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11636 mGraphicsAdapter->i_rollback();
11637
11638 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11639 mVRDEServer->i_rollback();
11640
11641 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11642 mAudioAdapter->i_rollback();
11643
11644 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11645 mUSBDeviceFilters->i_rollback();
11646
11647 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11648 mBandwidthControl->i_rollback();
11649
11650 if (!mHWData.isNull())
11651 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11652 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11653 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11654 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11655
11656 if (mData->flModifications & IsModified_NetworkAdapters)
11657 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11658 if ( mNetworkAdapters[slot]
11659 && mNetworkAdapters[slot]->i_isModified())
11660 {
11661 mNetworkAdapters[slot]->i_rollback();
11662 networkAdapters[slot] = mNetworkAdapters[slot];
11663 }
11664
11665 if (mData->flModifications & IsModified_SerialPorts)
11666 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11667 if ( mSerialPorts[slot]
11668 && mSerialPorts[slot]->i_isModified())
11669 {
11670 mSerialPorts[slot]->i_rollback();
11671 serialPorts[slot] = mSerialPorts[slot];
11672 }
11673
11674 if (mData->flModifications & IsModified_ParallelPorts)
11675 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11676 if ( mParallelPorts[slot]
11677 && mParallelPorts[slot]->i_isModified())
11678 {
11679 mParallelPorts[slot]->i_rollback();
11680 parallelPorts[slot] = mParallelPorts[slot];
11681 }
11682
11683 if (aNotify)
11684 {
11685 /* inform the direct session about changes */
11686
11687 ComObjPtr<Machine> that = this;
11688 uint32_t flModifications = mData->flModifications;
11689 alock.release();
11690
11691 if (flModifications & IsModified_SharedFolders)
11692 that->i_onSharedFolderChange();
11693
11694 if (flModifications & IsModified_VRDEServer)
11695 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11696 if (flModifications & IsModified_USB)
11697 that->i_onUSBControllerChange();
11698
11699 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11700 if (networkAdapters[slot])
11701 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11702 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11703 if (serialPorts[slot])
11704 that->i_onSerialPortChange(serialPorts[slot]);
11705 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11706 if (parallelPorts[slot])
11707 that->i_onParallelPortChange(parallelPorts[slot]);
11708
11709 if (flModifications & IsModified_Storage)
11710 {
11711 for (StorageControllerList::const_iterator
11712 it = mStorageControllers->begin();
11713 it != mStorageControllers->end();
11714 ++it)
11715 {
11716 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11717 }
11718 }
11719
11720
11721#if 0
11722 if (flModifications & IsModified_BandwidthControl)
11723 that->onBandwidthControlChange();
11724#endif
11725 }
11726}
11727
11728/**
11729 * Commits all the changes to machine settings.
11730 *
11731 * Note that this operation is supposed to never fail.
11732 *
11733 * @note Locks this object and children for writing.
11734 */
11735void Machine::i_commit()
11736{
11737 AutoCaller autoCaller(this);
11738 AssertComRCReturnVoid(autoCaller.rc());
11739
11740 AutoCaller peerCaller(mPeer);
11741 AssertComRCReturnVoid(peerCaller.rc());
11742
11743 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11744
11745 /*
11746 * use safe commit to ensure Snapshot machines (that share mUserData)
11747 * will still refer to a valid memory location
11748 */
11749 mUserData.commitCopy();
11750
11751 mHWData.commit();
11752
11753 if (mMediumAttachments.isBackedUp())
11754 i_commitMedia(Global::IsOnline(mData->mMachineState));
11755
11756 mBIOSSettings->i_commit();
11757 mRecordingSettings->i_commit();
11758 mGraphicsAdapter->i_commit();
11759 mVRDEServer->i_commit();
11760 mAudioAdapter->i_commit();
11761 mUSBDeviceFilters->i_commit();
11762 mBandwidthControl->i_commit();
11763
11764 /* Since mNetworkAdapters is a list which might have been changed (resized)
11765 * without using the Backupable<> template we need to handle the copying
11766 * of the list entries manually, including the creation of peers for the
11767 * new objects. */
11768 bool commitNetworkAdapters = false;
11769 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11770 if (mPeer)
11771 {
11772 /* commit everything, even the ones which will go away */
11773 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11774 mNetworkAdapters[slot]->i_commit();
11775 /* copy over the new entries, creating a peer and uninit the original */
11776 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11777 for (size_t slot = 0; slot < newSize; slot++)
11778 {
11779 /* look if this adapter has a peer device */
11780 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11781 if (!peer)
11782 {
11783 /* no peer means the adapter is a newly created one;
11784 * create a peer owning data this data share it with */
11785 peer.createObject();
11786 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11787 }
11788 mPeer->mNetworkAdapters[slot] = peer;
11789 }
11790 /* uninit any no longer needed network adapters */
11791 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11792 mNetworkAdapters[slot]->uninit();
11793 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11794 {
11795 if (mPeer->mNetworkAdapters[slot])
11796 mPeer->mNetworkAdapters[slot]->uninit();
11797 }
11798 /* Keep the original network adapter count until this point, so that
11799 * discarding a chipset type change will not lose settings. */
11800 mNetworkAdapters.resize(newSize);
11801 mPeer->mNetworkAdapters.resize(newSize);
11802 }
11803 else
11804 {
11805 /* we have no peer (our parent is the newly created machine);
11806 * just commit changes to the network adapters */
11807 commitNetworkAdapters = true;
11808 }
11809 if (commitNetworkAdapters)
11810 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11811 mNetworkAdapters[slot]->i_commit();
11812
11813 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11814 mSerialPorts[slot]->i_commit();
11815 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11816 mParallelPorts[slot]->i_commit();
11817
11818 bool commitStorageControllers = false;
11819
11820 if (mStorageControllers.isBackedUp())
11821 {
11822 mStorageControllers.commit();
11823
11824 if (mPeer)
11825 {
11826 /* Commit all changes to new controllers (this will reshare data with
11827 * peers for those who have peers) */
11828 StorageControllerList *newList = new StorageControllerList();
11829 for (StorageControllerList::const_iterator
11830 it = mStorageControllers->begin();
11831 it != mStorageControllers->end();
11832 ++it)
11833 {
11834 (*it)->i_commit();
11835
11836 /* look if this controller has a peer device */
11837 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11838 if (!peer)
11839 {
11840 /* no peer means the device is a newly created one;
11841 * create a peer owning data this device share it with */
11842 peer.createObject();
11843 peer->init(mPeer, *it, true /* aReshare */);
11844 }
11845 else
11846 {
11847 /* remove peer from the old list */
11848 mPeer->mStorageControllers->remove(peer);
11849 }
11850 /* and add it to the new list */
11851 newList->push_back(peer);
11852 }
11853
11854 /* uninit old peer's controllers that are left */
11855 for (StorageControllerList::const_iterator
11856 it = mPeer->mStorageControllers->begin();
11857 it != mPeer->mStorageControllers->end();
11858 ++it)
11859 {
11860 (*it)->uninit();
11861 }
11862
11863 /* attach new list of controllers to our peer */
11864 mPeer->mStorageControllers.attach(newList);
11865 }
11866 else
11867 {
11868 /* we have no peer (our parent is the newly created machine);
11869 * just commit changes to devices */
11870 commitStorageControllers = true;
11871 }
11872 }
11873 else
11874 {
11875 /* the list of controllers itself is not changed,
11876 * just commit changes to controllers themselves */
11877 commitStorageControllers = true;
11878 }
11879
11880 if (commitStorageControllers)
11881 {
11882 for (StorageControllerList::const_iterator
11883 it = mStorageControllers->begin();
11884 it != mStorageControllers->end();
11885 ++it)
11886 {
11887 (*it)->i_commit();
11888 }
11889 }
11890
11891 bool commitUSBControllers = false;
11892
11893 if (mUSBControllers.isBackedUp())
11894 {
11895 mUSBControllers.commit();
11896
11897 if (mPeer)
11898 {
11899 /* Commit all changes to new controllers (this will reshare data with
11900 * peers for those who have peers) */
11901 USBControllerList *newList = new USBControllerList();
11902 for (USBControllerList::const_iterator
11903 it = mUSBControllers->begin();
11904 it != mUSBControllers->end();
11905 ++it)
11906 {
11907 (*it)->i_commit();
11908
11909 /* look if this controller has a peer device */
11910 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11911 if (!peer)
11912 {
11913 /* no peer means the device is a newly created one;
11914 * create a peer owning data this device share it with */
11915 peer.createObject();
11916 peer->init(mPeer, *it, true /* aReshare */);
11917 }
11918 else
11919 {
11920 /* remove peer from the old list */
11921 mPeer->mUSBControllers->remove(peer);
11922 }
11923 /* and add it to the new list */
11924 newList->push_back(peer);
11925 }
11926
11927 /* uninit old peer's controllers that are left */
11928 for (USBControllerList::const_iterator
11929 it = mPeer->mUSBControllers->begin();
11930 it != mPeer->mUSBControllers->end();
11931 ++it)
11932 {
11933 (*it)->uninit();
11934 }
11935
11936 /* attach new list of controllers to our peer */
11937 mPeer->mUSBControllers.attach(newList);
11938 }
11939 else
11940 {
11941 /* we have no peer (our parent is the newly created machine);
11942 * just commit changes to devices */
11943 commitUSBControllers = true;
11944 }
11945 }
11946 else
11947 {
11948 /* the list of controllers itself is not changed,
11949 * just commit changes to controllers themselves */
11950 commitUSBControllers = true;
11951 }
11952
11953 if (commitUSBControllers)
11954 {
11955 for (USBControllerList::const_iterator
11956 it = mUSBControllers->begin();
11957 it != mUSBControllers->end();
11958 ++it)
11959 {
11960 (*it)->i_commit();
11961 }
11962 }
11963
11964 if (i_isSessionMachine())
11965 {
11966 /* attach new data to the primary machine and reshare it */
11967 mPeer->mUserData.attach(mUserData);
11968 mPeer->mHWData.attach(mHWData);
11969 /* mmMediumAttachments is reshared by fixupMedia */
11970 // mPeer->mMediumAttachments.attach(mMediumAttachments);
11971 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
11972 }
11973}
11974
11975/**
11976 * Copies all the hardware data from the given machine.
11977 *
11978 * Currently, only called when the VM is being restored from a snapshot. In
11979 * particular, this implies that the VM is not running during this method's
11980 * call.
11981 *
11982 * @note This method must be called from under this object's lock.
11983 *
11984 * @note This method doesn't call #i_commit(), so all data remains backed up and
11985 * unsaved.
11986 */
11987void Machine::i_copyFrom(Machine *aThat)
11988{
11989 AssertReturnVoid(!i_isSnapshotMachine());
11990 AssertReturnVoid(aThat->i_isSnapshotMachine());
11991
11992 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11993
11994 mHWData.assignCopy(aThat->mHWData);
11995
11996 // create copies of all shared folders (mHWData after attaching a copy
11997 // contains just references to original objects)
11998 for (HWData::SharedFolderList::iterator
11999 it = mHWData->mSharedFolders.begin();
12000 it != mHWData->mSharedFolders.end();
12001 ++it)
12002 {
12003 ComObjPtr<SharedFolder> folder;
12004 folder.createObject();
12005 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12006 AssertComRC(rc);
12007 *it = folder;
12008 }
12009
12010 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12011 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12012 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12013 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12014 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12015 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12016 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12017
12018 /* create private copies of all controllers */
12019 mStorageControllers.backup();
12020 mStorageControllers->clear();
12021 for (StorageControllerList::const_iterator
12022 it = aThat->mStorageControllers->begin();
12023 it != aThat->mStorageControllers->end();
12024 ++it)
12025 {
12026 ComObjPtr<StorageController> ctrl;
12027 ctrl.createObject();
12028 ctrl->initCopy(this, *it);
12029 mStorageControllers->push_back(ctrl);
12030 }
12031
12032 /* create private copies of all USB controllers */
12033 mUSBControllers.backup();
12034 mUSBControllers->clear();
12035 for (USBControllerList::const_iterator
12036 it = aThat->mUSBControllers->begin();
12037 it != aThat->mUSBControllers->end();
12038 ++it)
12039 {
12040 ComObjPtr<USBController> ctrl;
12041 ctrl.createObject();
12042 ctrl->initCopy(this, *it);
12043 mUSBControllers->push_back(ctrl);
12044 }
12045
12046 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12047 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12048 {
12049 if (mNetworkAdapters[slot].isNotNull())
12050 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12051 else
12052 {
12053 unconst(mNetworkAdapters[slot]).createObject();
12054 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12055 }
12056 }
12057 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12058 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12059 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12060 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12061}
12062
12063/**
12064 * Returns whether the given storage controller is hotplug capable.
12065 *
12066 * @returns true if the controller supports hotplugging
12067 * false otherwise.
12068 * @param enmCtrlType The controller type to check for.
12069 */
12070bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12071{
12072 ComPtr<ISystemProperties> systemProperties;
12073 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12074 if (FAILED(rc))
12075 return false;
12076
12077 BOOL aHotplugCapable = FALSE;
12078 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12079
12080 return RT_BOOL(aHotplugCapable);
12081}
12082
12083#ifdef VBOX_WITH_RESOURCE_USAGE_API
12084
12085void Machine::i_getDiskList(MediaList &list)
12086{
12087 for (MediumAttachmentList::const_iterator
12088 it = mMediumAttachments->begin();
12089 it != mMediumAttachments->end();
12090 ++it)
12091 {
12092 MediumAttachment *pAttach = *it;
12093 /* just in case */
12094 AssertContinue(pAttach);
12095
12096 AutoCaller localAutoCallerA(pAttach);
12097 if (FAILED(localAutoCallerA.rc())) continue;
12098
12099 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12100
12101 if (pAttach->i_getType() == DeviceType_HardDisk)
12102 list.push_back(pAttach->i_getMedium());
12103 }
12104}
12105
12106void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12107{
12108 AssertReturnVoid(isWriteLockOnCurrentThread());
12109 AssertPtrReturnVoid(aCollector);
12110
12111 pm::CollectorHAL *hal = aCollector->getHAL();
12112 /* Create sub metrics */
12113 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12114 "Percentage of processor time spent in user mode by the VM process.");
12115 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12116 "Percentage of processor time spent in kernel mode by the VM process.");
12117 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12118 "Size of resident portion of VM process in memory.");
12119 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12120 "Actual size of all VM disks combined.");
12121 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12122 "Network receive rate.");
12123 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12124 "Network transmit rate.");
12125 /* Create and register base metrics */
12126 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12127 cpuLoadUser, cpuLoadKernel);
12128 aCollector->registerBaseMetric(cpuLoad);
12129 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12130 ramUsageUsed);
12131 aCollector->registerBaseMetric(ramUsage);
12132 MediaList disks;
12133 i_getDiskList(disks);
12134 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12135 diskUsageUsed);
12136 aCollector->registerBaseMetric(diskUsage);
12137
12138 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12139 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12140 new pm::AggregateAvg()));
12141 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12142 new pm::AggregateMin()));
12143 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12144 new pm::AggregateMax()));
12145 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12146 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12147 new pm::AggregateAvg()));
12148 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12149 new pm::AggregateMin()));
12150 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12151 new pm::AggregateMax()));
12152
12153 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12154 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12155 new pm::AggregateAvg()));
12156 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12157 new pm::AggregateMin()));
12158 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12159 new pm::AggregateMax()));
12160
12161 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12162 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12163 new pm::AggregateAvg()));
12164 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12165 new pm::AggregateMin()));
12166 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12167 new pm::AggregateMax()));
12168
12169
12170 /* Guest metrics collector */
12171 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12172 aCollector->registerGuest(mCollectorGuest);
12173 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12174
12175 /* Create sub metrics */
12176 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12177 "Percentage of processor time spent in user mode as seen by the guest.");
12178 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12179 "Percentage of processor time spent in kernel mode as seen by the guest.");
12180 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12181 "Percentage of processor time spent idling as seen by the guest.");
12182
12183 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12184 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12185 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12186 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12187 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12188 pm::SubMetric *guestMemCache = new pm::SubMetric(
12189 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12190
12191 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12192 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12193
12194 /* Create and register base metrics */
12195 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12196 machineNetRx, machineNetTx);
12197 aCollector->registerBaseMetric(machineNetRate);
12198
12199 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12200 guestLoadUser, guestLoadKernel, guestLoadIdle);
12201 aCollector->registerBaseMetric(guestCpuLoad);
12202
12203 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12204 guestMemTotal, guestMemFree,
12205 guestMemBalloon, guestMemShared,
12206 guestMemCache, guestPagedTotal);
12207 aCollector->registerBaseMetric(guestCpuMem);
12208
12209 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12210 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12211 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12212 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12213
12214 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12215 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12216 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12217 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12218
12219 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12220 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12221 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12222 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12223
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12225 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12226 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12227 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12228
12229 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12230 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12231 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12232 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12233
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12238
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12243
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12246 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12248
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12250 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12251 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12253
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12256 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12258
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12261 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12262 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12263}
12264
12265void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12266{
12267 AssertReturnVoid(isWriteLockOnCurrentThread());
12268
12269 if (aCollector)
12270 {
12271 aCollector->unregisterMetricsFor(aMachine);
12272 aCollector->unregisterBaseMetricsFor(aMachine);
12273 }
12274}
12275
12276#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12277
12278
12279////////////////////////////////////////////////////////////////////////////////
12280
12281DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12282
12283HRESULT SessionMachine::FinalConstruct()
12284{
12285 LogFlowThisFunc(("\n"));
12286
12287 mClientToken = NULL;
12288
12289 return BaseFinalConstruct();
12290}
12291
12292void SessionMachine::FinalRelease()
12293{
12294 LogFlowThisFunc(("\n"));
12295
12296 Assert(!mClientToken);
12297 /* paranoia, should not hang around any more */
12298 if (mClientToken)
12299 {
12300 delete mClientToken;
12301 mClientToken = NULL;
12302 }
12303
12304 uninit(Uninit::Unexpected);
12305
12306 BaseFinalRelease();
12307}
12308
12309/**
12310 * @note Must be called only by Machine::LockMachine() from its own write lock.
12311 */
12312HRESULT SessionMachine::init(Machine *aMachine)
12313{
12314 LogFlowThisFuncEnter();
12315 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12316
12317 AssertReturn(aMachine, E_INVALIDARG);
12318
12319 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12320
12321 /* Enclose the state transition NotReady->InInit->Ready */
12322 AutoInitSpan autoInitSpan(this);
12323 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12324
12325 HRESULT rc = S_OK;
12326
12327 RT_ZERO(mAuthLibCtx);
12328
12329 /* create the machine client token */
12330 try
12331 {
12332 mClientToken = new ClientToken(aMachine, this);
12333 if (!mClientToken->isReady())
12334 {
12335 delete mClientToken;
12336 mClientToken = NULL;
12337 rc = E_FAIL;
12338 }
12339 }
12340 catch (std::bad_alloc &)
12341 {
12342 rc = E_OUTOFMEMORY;
12343 }
12344 if (FAILED(rc))
12345 return rc;
12346
12347 /* memorize the peer Machine */
12348 unconst(mPeer) = aMachine;
12349 /* share the parent pointer */
12350 unconst(mParent) = aMachine->mParent;
12351
12352 /* take the pointers to data to share */
12353 mData.share(aMachine->mData);
12354 mSSData.share(aMachine->mSSData);
12355
12356 mUserData.share(aMachine->mUserData);
12357 mHWData.share(aMachine->mHWData);
12358 mMediumAttachments.share(aMachine->mMediumAttachments);
12359
12360 mStorageControllers.allocate();
12361 for (StorageControllerList::const_iterator
12362 it = aMachine->mStorageControllers->begin();
12363 it != aMachine->mStorageControllers->end();
12364 ++it)
12365 {
12366 ComObjPtr<StorageController> ctl;
12367 ctl.createObject();
12368 ctl->init(this, *it);
12369 mStorageControllers->push_back(ctl);
12370 }
12371
12372 mUSBControllers.allocate();
12373 for (USBControllerList::const_iterator
12374 it = aMachine->mUSBControllers->begin();
12375 it != aMachine->mUSBControllers->end();
12376 ++it)
12377 {
12378 ComObjPtr<USBController> ctl;
12379 ctl.createObject();
12380 ctl->init(this, *it);
12381 mUSBControllers->push_back(ctl);
12382 }
12383
12384 unconst(mBIOSSettings).createObject();
12385 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12386 unconst(mRecordingSettings).createObject();
12387 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12388 /* create another GraphicsAdapter object that will be mutable */
12389 unconst(mGraphicsAdapter).createObject();
12390 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12391 /* create another VRDEServer object that will be mutable */
12392 unconst(mVRDEServer).createObject();
12393 mVRDEServer->init(this, aMachine->mVRDEServer);
12394 /* create another audio adapter object that will be mutable */
12395 unconst(mAudioAdapter).createObject();
12396 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12397 /* create a list of serial ports that will be mutable */
12398 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12399 {
12400 unconst(mSerialPorts[slot]).createObject();
12401 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12402 }
12403 /* create a list of parallel ports that will be mutable */
12404 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12405 {
12406 unconst(mParallelPorts[slot]).createObject();
12407 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12408 }
12409
12410 /* create another USB device filters object that will be mutable */
12411 unconst(mUSBDeviceFilters).createObject();
12412 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12413
12414 /* create a list of network adapters that will be mutable */
12415 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12416 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12417 {
12418 unconst(mNetworkAdapters[slot]).createObject();
12419 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12420 }
12421
12422 /* create another bandwidth control object that will be mutable */
12423 unconst(mBandwidthControl).createObject();
12424 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12425
12426 /* default is to delete saved state on Saved -> PoweredOff transition */
12427 mRemoveSavedState = true;
12428
12429 /* Confirm a successful initialization when it's the case */
12430 autoInitSpan.setSucceeded();
12431
12432 miNATNetworksStarted = 0;
12433
12434 LogFlowThisFuncLeave();
12435 return rc;
12436}
12437
12438/**
12439 * Uninitializes this session object. If the reason is other than
12440 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12441 * or the client watcher code.
12442 *
12443 * @param aReason uninitialization reason
12444 *
12445 * @note Locks mParent + this object for writing.
12446 */
12447void SessionMachine::uninit(Uninit::Reason aReason)
12448{
12449 LogFlowThisFuncEnter();
12450 LogFlowThisFunc(("reason=%d\n", aReason));
12451
12452 /*
12453 * Strongly reference ourselves to prevent this object deletion after
12454 * mData->mSession.mMachine.setNull() below (which can release the last
12455 * reference and call the destructor). Important: this must be done before
12456 * accessing any members (and before AutoUninitSpan that does it as well).
12457 * This self reference will be released as the very last step on return.
12458 */
12459 ComObjPtr<SessionMachine> selfRef;
12460 if (aReason != Uninit::Unexpected)
12461 selfRef = this;
12462
12463 /* Enclose the state transition Ready->InUninit->NotReady */
12464 AutoUninitSpan autoUninitSpan(this);
12465 if (autoUninitSpan.uninitDone())
12466 {
12467 LogFlowThisFunc(("Already uninitialized\n"));
12468 LogFlowThisFuncLeave();
12469 return;
12470 }
12471
12472 if (autoUninitSpan.initFailed())
12473 {
12474 /* We've been called by init() because it's failed. It's not really
12475 * necessary (nor it's safe) to perform the regular uninit sequence
12476 * below, the following is enough.
12477 */
12478 LogFlowThisFunc(("Initialization failed.\n"));
12479 /* destroy the machine client token */
12480 if (mClientToken)
12481 {
12482 delete mClientToken;
12483 mClientToken = NULL;
12484 }
12485 uninitDataAndChildObjects();
12486 mData.free();
12487 unconst(mParent) = NULL;
12488 unconst(mPeer) = NULL;
12489 LogFlowThisFuncLeave();
12490 return;
12491 }
12492
12493 MachineState_T lastState;
12494 {
12495 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12496 lastState = mData->mMachineState;
12497 }
12498 NOREF(lastState);
12499
12500#ifdef VBOX_WITH_USB
12501 // release all captured USB devices, but do this before requesting the locks below
12502 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12503 {
12504 /* Console::captureUSBDevices() is called in the VM process only after
12505 * setting the machine state to Starting or Restoring.
12506 * Console::detachAllUSBDevices() will be called upon successful
12507 * termination. So, we need to release USB devices only if there was
12508 * an abnormal termination of a running VM.
12509 *
12510 * This is identical to SessionMachine::DetachAllUSBDevices except
12511 * for the aAbnormal argument. */
12512 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12513 AssertComRC(rc);
12514 NOREF(rc);
12515
12516 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12517 if (service)
12518 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12519 }
12520#endif /* VBOX_WITH_USB */
12521
12522 // we need to lock this object in uninit() because the lock is shared
12523 // with mPeer (as well as data we modify below). mParent lock is needed
12524 // by several calls to it.
12525 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12526
12527#ifdef VBOX_WITH_RESOURCE_USAGE_API
12528 /*
12529 * It is safe to call Machine::i_unregisterMetrics() here because
12530 * PerformanceCollector::samplerCallback no longer accesses guest methods
12531 * holding the lock.
12532 */
12533 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12534 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12535 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12536 if (mCollectorGuest)
12537 {
12538 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12539 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12540 mCollectorGuest = NULL;
12541 }
12542#endif
12543
12544 if (aReason == Uninit::Abnormal)
12545 {
12546 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12547
12548 /* reset the state to Aborted */
12549 if (mData->mMachineState != MachineState_Aborted)
12550 i_setMachineState(MachineState_Aborted);
12551 }
12552
12553 // any machine settings modified?
12554 if (mData->flModifications)
12555 {
12556 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12557 i_rollback(false /* aNotify */);
12558 }
12559
12560 mData->mSession.mPID = NIL_RTPROCESS;
12561
12562 if (aReason == Uninit::Unexpected)
12563 {
12564 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12565 * client watcher thread to update the set of machines that have open
12566 * sessions. */
12567 mParent->i_updateClientWatcher();
12568 }
12569
12570 /* uninitialize all remote controls */
12571 if (mData->mSession.mRemoteControls.size())
12572 {
12573 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12574 mData->mSession.mRemoteControls.size()));
12575
12576 /* Always restart a the beginning, since the iterator is invalidated
12577 * by using erase(). */
12578 for (Data::Session::RemoteControlList::iterator
12579 it = mData->mSession.mRemoteControls.begin();
12580 it != mData->mSession.mRemoteControls.end();
12581 it = mData->mSession.mRemoteControls.begin())
12582 {
12583 ComPtr<IInternalSessionControl> pControl = *it;
12584 mData->mSession.mRemoteControls.erase(it);
12585 multilock.release();
12586 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12587 HRESULT rc = pControl->Uninitialize();
12588 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12589 if (FAILED(rc))
12590 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12591 multilock.acquire();
12592 }
12593 mData->mSession.mRemoteControls.clear();
12594 }
12595
12596 /* Remove all references to the NAT network service. The service will stop
12597 * if all references (also from other VMs) are removed. */
12598 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12599 {
12600 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12601 {
12602 BOOL enabled;
12603 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12604 if ( FAILED(hrc)
12605 || !enabled)
12606 continue;
12607
12608 NetworkAttachmentType_T type;
12609 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12610 if ( SUCCEEDED(hrc)
12611 && type == NetworkAttachmentType_NATNetwork)
12612 {
12613 Bstr name;
12614 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12615 if (SUCCEEDED(hrc))
12616 {
12617 multilock.release();
12618 Utf8Str strName(name);
12619 LogRel(("VM '%s' stops using NAT network '%s'\n",
12620 mUserData->s.strName.c_str(), strName.c_str()));
12621 mParent->i_natNetworkRefDec(strName);
12622 multilock.acquire();
12623 }
12624 }
12625 }
12626 }
12627
12628 /*
12629 * An expected uninitialization can come only from #i_checkForDeath().
12630 * Otherwise it means that something's gone really wrong (for example,
12631 * the Session implementation has released the VirtualBox reference
12632 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12633 * etc). However, it's also possible, that the client releases the IPC
12634 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12635 * but the VirtualBox release event comes first to the server process.
12636 * This case is practically possible, so we should not assert on an
12637 * unexpected uninit, just log a warning.
12638 */
12639
12640 if (aReason == Uninit::Unexpected)
12641 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12642
12643 if (aReason != Uninit::Normal)
12644 {
12645 mData->mSession.mDirectControl.setNull();
12646 }
12647 else
12648 {
12649 /* this must be null here (see #OnSessionEnd()) */
12650 Assert(mData->mSession.mDirectControl.isNull());
12651 Assert(mData->mSession.mState == SessionState_Unlocking);
12652 Assert(!mData->mSession.mProgress.isNull());
12653 }
12654 if (mData->mSession.mProgress)
12655 {
12656 if (aReason == Uninit::Normal)
12657 mData->mSession.mProgress->i_notifyComplete(S_OK);
12658 else
12659 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12660 COM_IIDOF(ISession),
12661 getComponentName(),
12662 tr("The VM session was aborted"));
12663 mData->mSession.mProgress.setNull();
12664 }
12665
12666 if (mConsoleTaskData.mProgress)
12667 {
12668 Assert(aReason == Uninit::Abnormal);
12669 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12670 COM_IIDOF(ISession),
12671 getComponentName(),
12672 tr("The VM session was aborted"));
12673 mConsoleTaskData.mProgress.setNull();
12674 }
12675
12676 /* remove the association between the peer machine and this session machine */
12677 Assert( (SessionMachine*)mData->mSession.mMachine == this
12678 || aReason == Uninit::Unexpected);
12679
12680 /* reset the rest of session data */
12681 mData->mSession.mLockType = LockType_Null;
12682 mData->mSession.mMachine.setNull();
12683 mData->mSession.mState = SessionState_Unlocked;
12684 mData->mSession.mName.setNull();
12685
12686 /* destroy the machine client token before leaving the exclusive lock */
12687 if (mClientToken)
12688 {
12689 delete mClientToken;
12690 mClientToken = NULL;
12691 }
12692
12693 /* fire an event */
12694 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12695
12696 uninitDataAndChildObjects();
12697
12698 /* free the essential data structure last */
12699 mData.free();
12700
12701 /* release the exclusive lock before setting the below two to NULL */
12702 multilock.release();
12703
12704 unconst(mParent) = NULL;
12705 unconst(mPeer) = NULL;
12706
12707 AuthLibUnload(&mAuthLibCtx);
12708
12709 LogFlowThisFuncLeave();
12710}
12711
12712// util::Lockable interface
12713////////////////////////////////////////////////////////////////////////////////
12714
12715/**
12716 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12717 * with the primary Machine instance (mPeer).
12718 */
12719RWLockHandle *SessionMachine::lockHandle() const
12720{
12721 AssertReturn(mPeer != NULL, NULL);
12722 return mPeer->lockHandle();
12723}
12724
12725// IInternalMachineControl methods
12726////////////////////////////////////////////////////////////////////////////////
12727
12728/**
12729 * Passes collected guest statistics to performance collector object
12730 */
12731HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12732 ULONG aCpuKernel, ULONG aCpuIdle,
12733 ULONG aMemTotal, ULONG aMemFree,
12734 ULONG aMemBalloon, ULONG aMemShared,
12735 ULONG aMemCache, ULONG aPageTotal,
12736 ULONG aAllocVMM, ULONG aFreeVMM,
12737 ULONG aBalloonedVMM, ULONG aSharedVMM,
12738 ULONG aVmNetRx, ULONG aVmNetTx)
12739{
12740#ifdef VBOX_WITH_RESOURCE_USAGE_API
12741 if (mCollectorGuest)
12742 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12743 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12744 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12745 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12746
12747 return S_OK;
12748#else
12749 NOREF(aValidStats);
12750 NOREF(aCpuUser);
12751 NOREF(aCpuKernel);
12752 NOREF(aCpuIdle);
12753 NOREF(aMemTotal);
12754 NOREF(aMemFree);
12755 NOREF(aMemBalloon);
12756 NOREF(aMemShared);
12757 NOREF(aMemCache);
12758 NOREF(aPageTotal);
12759 NOREF(aAllocVMM);
12760 NOREF(aFreeVMM);
12761 NOREF(aBalloonedVMM);
12762 NOREF(aSharedVMM);
12763 NOREF(aVmNetRx);
12764 NOREF(aVmNetTx);
12765 return E_NOTIMPL;
12766#endif
12767}
12768
12769////////////////////////////////////////////////////////////////////////////////
12770//
12771// SessionMachine task records
12772//
12773////////////////////////////////////////////////////////////////////////////////
12774
12775/**
12776 * Task record for saving the machine state.
12777 */
12778class SessionMachine::SaveStateTask
12779 : public Machine::Task
12780{
12781public:
12782 SaveStateTask(SessionMachine *m,
12783 Progress *p,
12784 const Utf8Str &t,
12785 Reason_T enmReason,
12786 const Utf8Str &strStateFilePath)
12787 : Task(m, p, t),
12788 m_enmReason(enmReason),
12789 m_strStateFilePath(strStateFilePath)
12790 {}
12791
12792private:
12793 void handler()
12794 {
12795 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12796 }
12797
12798 Reason_T m_enmReason;
12799 Utf8Str m_strStateFilePath;
12800
12801 friend class SessionMachine;
12802};
12803
12804/**
12805 * Task thread implementation for SessionMachine::SaveState(), called from
12806 * SessionMachine::taskHandler().
12807 *
12808 * @note Locks this object for writing.
12809 *
12810 * @param task
12811 * @return
12812 */
12813void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12814{
12815 LogFlowThisFuncEnter();
12816
12817 AutoCaller autoCaller(this);
12818 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12819 if (FAILED(autoCaller.rc()))
12820 {
12821 /* we might have been uninitialized because the session was accidentally
12822 * closed by the client, so don't assert */
12823 HRESULT rc = setError(E_FAIL,
12824 tr("The session has been accidentally closed"));
12825 task.m_pProgress->i_notifyComplete(rc);
12826 LogFlowThisFuncLeave();
12827 return;
12828 }
12829
12830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12831
12832 HRESULT rc = S_OK;
12833
12834 try
12835 {
12836 ComPtr<IInternalSessionControl> directControl;
12837 if (mData->mSession.mLockType == LockType_VM)
12838 directControl = mData->mSession.mDirectControl;
12839 if (directControl.isNull())
12840 throw setError(VBOX_E_INVALID_VM_STATE,
12841 tr("Trying to save state without a running VM"));
12842 alock.release();
12843 BOOL fSuspendedBySave;
12844 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12845 Assert(!fSuspendedBySave);
12846 alock.acquire();
12847
12848 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12849 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12850 throw E_FAIL);
12851
12852 if (SUCCEEDED(rc))
12853 {
12854 mSSData->strStateFilePath = task.m_strStateFilePath;
12855
12856 /* save all VM settings */
12857 rc = i_saveSettings(NULL);
12858 // no need to check whether VirtualBox.xml needs saving also since
12859 // we can't have a name change pending at this point
12860 }
12861 else
12862 {
12863 // On failure, set the state to the state we had at the beginning.
12864 i_setMachineState(task.m_machineStateBackup);
12865 i_updateMachineStateOnClient();
12866
12867 // Delete the saved state file (might have been already created).
12868 // No need to check whether this is shared with a snapshot here
12869 // because we certainly created a fresh saved state file here.
12870 RTFileDelete(task.m_strStateFilePath.c_str());
12871 }
12872 }
12873 catch (HRESULT aRC) { rc = aRC; }
12874
12875 task.m_pProgress->i_notifyComplete(rc);
12876
12877 LogFlowThisFuncLeave();
12878}
12879
12880/**
12881 * @note Locks this object for writing.
12882 */
12883HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12884{
12885 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12886}
12887
12888HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12889{
12890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12891
12892 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12893 if (FAILED(rc)) return rc;
12894
12895 if ( mData->mMachineState != MachineState_Running
12896 && mData->mMachineState != MachineState_Paused
12897 )
12898 return setError(VBOX_E_INVALID_VM_STATE,
12899 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12900 Global::stringifyMachineState(mData->mMachineState));
12901
12902 ComObjPtr<Progress> pProgress;
12903 pProgress.createObject();
12904 rc = pProgress->init(i_getVirtualBox(),
12905 static_cast<IMachine *>(this) /* aInitiator */,
12906 tr("Saving the execution state of the virtual machine"),
12907 FALSE /* aCancelable */);
12908 if (FAILED(rc))
12909 return rc;
12910
12911 Utf8Str strStateFilePath;
12912 i_composeSavedStateFilename(strStateFilePath);
12913
12914 /* create and start the task on a separate thread (note that it will not
12915 * start working until we release alock) */
12916 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12917 rc = pTask->createThread();
12918 if (FAILED(rc))
12919 return rc;
12920
12921 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12922 i_setMachineState(MachineState_Saving);
12923 i_updateMachineStateOnClient();
12924
12925 pProgress.queryInterfaceTo(aProgress.asOutParam());
12926
12927 return S_OK;
12928}
12929
12930/**
12931 * @note Locks this object for writing.
12932 */
12933HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12934{
12935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12936
12937 HRESULT rc = i_checkStateDependency(MutableStateDep);
12938 if (FAILED(rc)) return rc;
12939
12940 if ( mData->mMachineState != MachineState_PoweredOff
12941 && mData->mMachineState != MachineState_Teleported
12942 && mData->mMachineState != MachineState_Aborted
12943 )
12944 return setError(VBOX_E_INVALID_VM_STATE,
12945 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12946 Global::stringifyMachineState(mData->mMachineState));
12947
12948 com::Utf8Str stateFilePathFull;
12949 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12950 if (RT_FAILURE(vrc))
12951 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12952 tr("Invalid saved state file path '%s' (%Rrc)"),
12953 aSavedStateFile.c_str(),
12954 vrc);
12955
12956 mSSData->strStateFilePath = stateFilePathFull;
12957
12958 /* The below i_setMachineState() will detect the state transition and will
12959 * update the settings file */
12960
12961 return i_setMachineState(MachineState_Saved);
12962}
12963
12964/**
12965 * @note Locks this object for writing.
12966 */
12967HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12968{
12969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12970
12971 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12972 if (FAILED(rc)) return rc;
12973
12974 if (mData->mMachineState != MachineState_Saved)
12975 return setError(VBOX_E_INVALID_VM_STATE,
12976 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12977 Global::stringifyMachineState(mData->mMachineState));
12978
12979 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12980
12981 /*
12982 * Saved -> PoweredOff transition will be detected in the SessionMachine
12983 * and properly handled.
12984 */
12985 rc = i_setMachineState(MachineState_PoweredOff);
12986 return rc;
12987}
12988
12989
12990/**
12991 * @note Locks the same as #i_setMachineState() does.
12992 */
12993HRESULT SessionMachine::updateState(MachineState_T aState)
12994{
12995 return i_setMachineState(aState);
12996}
12997
12998/**
12999 * @note Locks this object for writing.
13000 */
13001HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13002{
13003 IProgress *pProgress(aProgress);
13004
13005 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13006
13007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13008
13009 if (mData->mSession.mState != SessionState_Locked)
13010 return VBOX_E_INVALID_OBJECT_STATE;
13011
13012 if (!mData->mSession.mProgress.isNull())
13013 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13014
13015 /* If we didn't reference the NAT network service yet, add a reference to
13016 * force a start */
13017 if (miNATNetworksStarted < 1)
13018 {
13019 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13020 {
13021 BOOL enabled;
13022 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13023 if ( FAILED(hrc)
13024 || !enabled)
13025 continue;
13026
13027 NetworkAttachmentType_T type;
13028 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13029 if ( SUCCEEDED(hrc)
13030 && type == NetworkAttachmentType_NATNetwork)
13031 {
13032 Bstr name;
13033 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13034 if (SUCCEEDED(hrc))
13035 {
13036 Utf8Str strName(name);
13037 LogRel(("VM '%s' starts using NAT network '%s'\n",
13038 mUserData->s.strName.c_str(), strName.c_str()));
13039 mPeer->lockHandle()->unlockWrite();
13040 mParent->i_natNetworkRefInc(strName);
13041#ifdef RT_LOCK_STRICT
13042 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13043#else
13044 mPeer->lockHandle()->lockWrite();
13045#endif
13046 }
13047 }
13048 }
13049 miNATNetworksStarted++;
13050 }
13051
13052 LogFlowThisFunc(("returns S_OK.\n"));
13053 return S_OK;
13054}
13055
13056/**
13057 * @note Locks this object for writing.
13058 */
13059HRESULT SessionMachine::endPowerUp(LONG aResult)
13060{
13061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13062
13063 if (mData->mSession.mState != SessionState_Locked)
13064 return VBOX_E_INVALID_OBJECT_STATE;
13065
13066 /* Finalize the LaunchVMProcess progress object. */
13067 if (mData->mSession.mProgress)
13068 {
13069 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13070 mData->mSession.mProgress.setNull();
13071 }
13072
13073 if (SUCCEEDED((HRESULT)aResult))
13074 {
13075#ifdef VBOX_WITH_RESOURCE_USAGE_API
13076 /* The VM has been powered up successfully, so it makes sense
13077 * now to offer the performance metrics for a running machine
13078 * object. Doing it earlier wouldn't be safe. */
13079 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13080 mData->mSession.mPID);
13081#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13082 }
13083
13084 return S_OK;
13085}
13086
13087/**
13088 * @note Locks this object for writing.
13089 */
13090HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13091{
13092 LogFlowThisFuncEnter();
13093
13094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13095
13096 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13097 E_FAIL);
13098
13099 /* create a progress object to track operation completion */
13100 ComObjPtr<Progress> pProgress;
13101 pProgress.createObject();
13102 pProgress->init(i_getVirtualBox(),
13103 static_cast<IMachine *>(this) /* aInitiator */,
13104 tr("Stopping the virtual machine"),
13105 FALSE /* aCancelable */);
13106
13107 /* fill in the console task data */
13108 mConsoleTaskData.mLastState = mData->mMachineState;
13109 mConsoleTaskData.mProgress = pProgress;
13110
13111 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13112 i_setMachineState(MachineState_Stopping);
13113
13114 pProgress.queryInterfaceTo(aProgress.asOutParam());
13115
13116 return S_OK;
13117}
13118
13119/**
13120 * @note Locks this object for writing.
13121 */
13122HRESULT SessionMachine::endPoweringDown(LONG aResult,
13123 const com::Utf8Str &aErrMsg)
13124{
13125 HRESULT const hrcResult = (HRESULT)aResult;
13126 LogFlowThisFuncEnter();
13127
13128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13129
13130 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13131 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13132 && mConsoleTaskData.mLastState != MachineState_Null,
13133 E_FAIL);
13134
13135 /*
13136 * On failure, set the state to the state we had when BeginPoweringDown()
13137 * was called (this is expected by Console::PowerDown() and the associated
13138 * task). On success the VM process already changed the state to
13139 * MachineState_PoweredOff, so no need to do anything.
13140 */
13141 if (FAILED(hrcResult))
13142 i_setMachineState(mConsoleTaskData.mLastState);
13143
13144 /* notify the progress object about operation completion */
13145 Assert(mConsoleTaskData.mProgress);
13146 if (SUCCEEDED(hrcResult))
13147 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13148 else
13149 {
13150 if (aErrMsg.length())
13151 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13152 COM_IIDOF(ISession),
13153 getComponentName(),
13154 aErrMsg.c_str());
13155 else
13156 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13157 }
13158
13159 /* clear out the temporary saved state data */
13160 mConsoleTaskData.mLastState = MachineState_Null;
13161 mConsoleTaskData.mProgress.setNull();
13162
13163 LogFlowThisFuncLeave();
13164 return S_OK;
13165}
13166
13167
13168/**
13169 * Goes through the USB filters of the given machine to see if the given
13170 * device matches any filter or not.
13171 *
13172 * @note Locks the same as USBController::hasMatchingFilter() does.
13173 */
13174HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13175 BOOL *aMatched,
13176 ULONG *aMaskedInterfaces)
13177{
13178 LogFlowThisFunc(("\n"));
13179
13180#ifdef VBOX_WITH_USB
13181 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13182#else
13183 NOREF(aDevice);
13184 NOREF(aMaskedInterfaces);
13185 *aMatched = FALSE;
13186#endif
13187
13188 return S_OK;
13189}
13190
13191/**
13192 * @note Locks the same as Host::captureUSBDevice() does.
13193 */
13194HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13195{
13196 LogFlowThisFunc(("\n"));
13197
13198#ifdef VBOX_WITH_USB
13199 /* if captureDeviceForVM() fails, it must have set extended error info */
13200 clearError();
13201 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13202 if (FAILED(rc)) return rc;
13203
13204 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13205 AssertReturn(service, E_FAIL);
13206 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13207#else
13208 RT_NOREF(aId, aCaptureFilename);
13209 return E_NOTIMPL;
13210#endif
13211}
13212
13213/**
13214 * @note Locks the same as Host::detachUSBDevice() does.
13215 */
13216HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13217 BOOL aDone)
13218{
13219 LogFlowThisFunc(("\n"));
13220
13221#ifdef VBOX_WITH_USB
13222 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13223 AssertReturn(service, E_FAIL);
13224 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13225#else
13226 NOREF(aId);
13227 NOREF(aDone);
13228 return E_NOTIMPL;
13229#endif
13230}
13231
13232/**
13233 * Inserts all machine filters to the USB proxy service and then calls
13234 * Host::autoCaptureUSBDevices().
13235 *
13236 * Called by Console from the VM process upon VM startup.
13237 *
13238 * @note Locks what called methods lock.
13239 */
13240HRESULT SessionMachine::autoCaptureUSBDevices()
13241{
13242 LogFlowThisFunc(("\n"));
13243
13244#ifdef VBOX_WITH_USB
13245 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13246 AssertComRC(rc);
13247 NOREF(rc);
13248
13249 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13250 AssertReturn(service, E_FAIL);
13251 return service->autoCaptureDevicesForVM(this);
13252#else
13253 return S_OK;
13254#endif
13255}
13256
13257/**
13258 * Removes all machine filters from the USB proxy service and then calls
13259 * Host::detachAllUSBDevices().
13260 *
13261 * Called by Console from the VM process upon normal VM termination or by
13262 * SessionMachine::uninit() upon abnormal VM termination (from under the
13263 * Machine/SessionMachine lock).
13264 *
13265 * @note Locks what called methods lock.
13266 */
13267HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13268{
13269 LogFlowThisFunc(("\n"));
13270
13271#ifdef VBOX_WITH_USB
13272 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13273 AssertComRC(rc);
13274 NOREF(rc);
13275
13276 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13277 AssertReturn(service, E_FAIL);
13278 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13279#else
13280 NOREF(aDone);
13281 return S_OK;
13282#endif
13283}
13284
13285/**
13286 * @note Locks this object for writing.
13287 */
13288HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13289 ComPtr<IProgress> &aProgress)
13290{
13291 LogFlowThisFuncEnter();
13292
13293 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13294 /*
13295 * We don't assert below because it might happen that a non-direct session
13296 * informs us it is closed right after we've been uninitialized -- it's ok.
13297 */
13298
13299 /* get IInternalSessionControl interface */
13300 ComPtr<IInternalSessionControl> control(aSession);
13301
13302 ComAssertRet(!control.isNull(), E_INVALIDARG);
13303
13304 /* Creating a Progress object requires the VirtualBox lock, and
13305 * thus locking it here is required by the lock order rules. */
13306 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13307
13308 if (control == mData->mSession.mDirectControl)
13309 {
13310 /* The direct session is being normally closed by the client process
13311 * ----------------------------------------------------------------- */
13312
13313 /* go to the closing state (essential for all open*Session() calls and
13314 * for #i_checkForDeath()) */
13315 Assert(mData->mSession.mState == SessionState_Locked);
13316 mData->mSession.mState = SessionState_Unlocking;
13317
13318 /* set direct control to NULL to release the remote instance */
13319 mData->mSession.mDirectControl.setNull();
13320 LogFlowThisFunc(("Direct control is set to NULL\n"));
13321
13322 if (mData->mSession.mProgress)
13323 {
13324 /* finalize the progress, someone might wait if a frontend
13325 * closes the session before powering on the VM. */
13326 mData->mSession.mProgress->notifyComplete(E_FAIL,
13327 COM_IIDOF(ISession),
13328 getComponentName(),
13329 tr("The VM session was closed before any attempt to power it on"));
13330 mData->mSession.mProgress.setNull();
13331 }
13332
13333 /* Create the progress object the client will use to wait until
13334 * #i_checkForDeath() is called to uninitialize this session object after
13335 * it releases the IPC semaphore.
13336 * Note! Because we're "reusing" mProgress here, this must be a proxy
13337 * object just like for LaunchVMProcess. */
13338 Assert(mData->mSession.mProgress.isNull());
13339 ComObjPtr<ProgressProxy> progress;
13340 progress.createObject();
13341 ComPtr<IUnknown> pPeer(mPeer);
13342 progress->init(mParent, pPeer,
13343 Bstr(tr("Closing session")).raw(),
13344 FALSE /* aCancelable */);
13345 progress.queryInterfaceTo(aProgress.asOutParam());
13346 mData->mSession.mProgress = progress;
13347 }
13348 else
13349 {
13350 /* the remote session is being normally closed */
13351 bool found = false;
13352 for (Data::Session::RemoteControlList::iterator
13353 it = mData->mSession.mRemoteControls.begin();
13354 it != mData->mSession.mRemoteControls.end();
13355 ++it)
13356 {
13357 if (control == *it)
13358 {
13359 found = true;
13360 // This MUST be erase(it), not remove(*it) as the latter
13361 // triggers a very nasty use after free due to the place where
13362 // the value "lives".
13363 mData->mSession.mRemoteControls.erase(it);
13364 break;
13365 }
13366 }
13367 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13368 E_INVALIDARG);
13369 }
13370
13371 /* signal the client watcher thread, because the client is going away */
13372 mParent->i_updateClientWatcher();
13373
13374 LogFlowThisFuncLeave();
13375 return S_OK;
13376}
13377
13378HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13379{
13380#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13381 ULONG uID;
13382 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13383 if (RT_SUCCESS(rc))
13384 {
13385 if (aID)
13386 *aID = uID;
13387 return S_OK;
13388 }
13389 return E_FAIL;
13390#else
13391 RT_NOREF(aParms, aID);
13392 ReturnComNotImplemented();
13393#endif
13394}
13395
13396HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13397{
13398#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13399 return mParent->i_onClipboardAreaUnregister(aID);
13400#else
13401 RT_NOREF(aID);
13402 ReturnComNotImplemented();
13403#endif
13404}
13405
13406HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13407{
13408#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13409 return mParent->i_onClipboardAreaAttach(aID);
13410#else
13411 RT_NOREF(aID);
13412 ReturnComNotImplemented();
13413#endif
13414}
13415HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13416{
13417#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13418 return mParent->i_onClipboardAreaDetach(aID);
13419#else
13420 RT_NOREF(aID);
13421 ReturnComNotImplemented();
13422#endif
13423}
13424
13425HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13426{
13427#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13428 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13429 if (aID)
13430 *aID = uID;
13431 return S_OK;
13432#else
13433 RT_NOREF(aID);
13434 ReturnComNotImplemented();
13435#endif
13436}
13437
13438HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13439{
13440#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13441 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13442 if (aRefCount)
13443 *aRefCount = uRefCount;
13444 return S_OK;
13445#else
13446 RT_NOREF(aID, aRefCount);
13447 ReturnComNotImplemented();
13448#endif
13449}
13450
13451HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13452 std::vector<com::Utf8Str> &aValues,
13453 std::vector<LONG64> &aTimestamps,
13454 std::vector<com::Utf8Str> &aFlags)
13455{
13456 LogFlowThisFunc(("\n"));
13457
13458#ifdef VBOX_WITH_GUEST_PROPS
13459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13460
13461 size_t cEntries = mHWData->mGuestProperties.size();
13462 aNames.resize(cEntries);
13463 aValues.resize(cEntries);
13464 aTimestamps.resize(cEntries);
13465 aFlags.resize(cEntries);
13466
13467 size_t i = 0;
13468 for (HWData::GuestPropertyMap::const_iterator
13469 it = mHWData->mGuestProperties.begin();
13470 it != mHWData->mGuestProperties.end();
13471 ++it, ++i)
13472 {
13473 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13474 aNames[i] = it->first;
13475 aValues[i] = it->second.strValue;
13476 aTimestamps[i] = it->second.mTimestamp;
13477
13478 /* If it is NULL, keep it NULL. */
13479 if (it->second.mFlags)
13480 {
13481 GuestPropWriteFlags(it->second.mFlags, szFlags);
13482 aFlags[i] = szFlags;
13483 }
13484 else
13485 aFlags[i] = "";
13486 }
13487 return S_OK;
13488#else
13489 ReturnComNotImplemented();
13490#endif
13491}
13492
13493HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13494 const com::Utf8Str &aValue,
13495 LONG64 aTimestamp,
13496 const com::Utf8Str &aFlags)
13497{
13498 LogFlowThisFunc(("\n"));
13499
13500#ifdef VBOX_WITH_GUEST_PROPS
13501 try
13502 {
13503 /*
13504 * Convert input up front.
13505 */
13506 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13507 if (aFlags.length())
13508 {
13509 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13510 AssertRCReturn(vrc, E_INVALIDARG);
13511 }
13512
13513 /*
13514 * Now grab the object lock, validate the state and do the update.
13515 */
13516
13517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13518
13519 if (!Global::IsOnline(mData->mMachineState))
13520 {
13521 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13522 VBOX_E_INVALID_VM_STATE);
13523 }
13524
13525 i_setModified(IsModified_MachineData);
13526 mHWData.backup();
13527
13528 bool fDelete = !aValue.length();
13529 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13530 if (it != mHWData->mGuestProperties.end())
13531 {
13532 if (!fDelete)
13533 {
13534 it->second.strValue = aValue;
13535 it->second.mTimestamp = aTimestamp;
13536 it->second.mFlags = fFlags;
13537 }
13538 else
13539 mHWData->mGuestProperties.erase(it);
13540
13541 mData->mGuestPropertiesModified = TRUE;
13542 }
13543 else if (!fDelete)
13544 {
13545 HWData::GuestProperty prop;
13546 prop.strValue = aValue;
13547 prop.mTimestamp = aTimestamp;
13548 prop.mFlags = fFlags;
13549
13550 mHWData->mGuestProperties[aName] = prop;
13551 mData->mGuestPropertiesModified = TRUE;
13552 }
13553
13554 alock.release();
13555
13556 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13557 }
13558 catch (...)
13559 {
13560 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13561 }
13562 return S_OK;
13563#else
13564 ReturnComNotImplemented();
13565#endif
13566}
13567
13568
13569HRESULT SessionMachine::lockMedia()
13570{
13571 AutoMultiWriteLock2 alock(this->lockHandle(),
13572 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13573
13574 AssertReturn( mData->mMachineState == MachineState_Starting
13575 || mData->mMachineState == MachineState_Restoring
13576 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13577
13578 clearError();
13579 alock.release();
13580 return i_lockMedia();
13581}
13582
13583HRESULT SessionMachine::unlockMedia()
13584{
13585 HRESULT hrc = i_unlockMedia();
13586 return hrc;
13587}
13588
13589HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13590 ComPtr<IMediumAttachment> &aNewAttachment)
13591{
13592 // request the host lock first, since might be calling Host methods for getting host drives;
13593 // next, protect the media tree all the while we're in here, as well as our member variables
13594 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13595 this->lockHandle(),
13596 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13597
13598 IMediumAttachment *iAttach = aAttachment;
13599 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13600
13601 Utf8Str ctrlName;
13602 LONG lPort;
13603 LONG lDevice;
13604 bool fTempEject;
13605 {
13606 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13607
13608 /* Need to query the details first, as the IMediumAttachment reference
13609 * might be to the original settings, which we are going to change. */
13610 ctrlName = pAttach->i_getControllerName();
13611 lPort = pAttach->i_getPort();
13612 lDevice = pAttach->i_getDevice();
13613 fTempEject = pAttach->i_getTempEject();
13614 }
13615
13616 if (!fTempEject)
13617 {
13618 /* Remember previously mounted medium. The medium before taking the
13619 * backup is not necessarily the same thing. */
13620 ComObjPtr<Medium> oldmedium;
13621 oldmedium = pAttach->i_getMedium();
13622
13623 i_setModified(IsModified_Storage);
13624 mMediumAttachments.backup();
13625
13626 // The backup operation makes the pAttach reference point to the
13627 // old settings. Re-get the correct reference.
13628 pAttach = i_findAttachment(*mMediumAttachments.data(),
13629 ctrlName,
13630 lPort,
13631 lDevice);
13632
13633 {
13634 AutoCaller autoAttachCaller(this);
13635 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13636
13637 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13638 if (!oldmedium.isNull())
13639 oldmedium->i_removeBackReference(mData->mUuid);
13640
13641 pAttach->i_updateMedium(NULL);
13642 pAttach->i_updateEjected();
13643 }
13644
13645 i_setModified(IsModified_Storage);
13646 }
13647 else
13648 {
13649 {
13650 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13651 pAttach->i_updateEjected();
13652 }
13653 }
13654
13655 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13656
13657 return S_OK;
13658}
13659
13660HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13661 com::Utf8Str &aResult)
13662{
13663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13664
13665 HRESULT hr = S_OK;
13666
13667 if (!mAuthLibCtx.hAuthLibrary)
13668 {
13669 /* Load the external authentication library. */
13670 Bstr authLibrary;
13671 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13672
13673 Utf8Str filename = authLibrary;
13674
13675 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13676 if (RT_FAILURE(vrc))
13677 hr = setErrorBoth(E_FAIL, vrc,
13678 tr("Could not load the external authentication library '%s' (%Rrc)"),
13679 filename.c_str(), vrc);
13680 }
13681
13682 /* The auth library might need the machine lock. */
13683 alock.release();
13684
13685 if (FAILED(hr))
13686 return hr;
13687
13688 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13689 {
13690 enum VRDEAuthParams
13691 {
13692 parmUuid = 1,
13693 parmGuestJudgement,
13694 parmUser,
13695 parmPassword,
13696 parmDomain,
13697 parmClientId
13698 };
13699
13700 AuthResult result = AuthResultAccessDenied;
13701
13702 Guid uuid(aAuthParams[parmUuid]);
13703 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13704 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13705
13706 result = AuthLibAuthenticate(&mAuthLibCtx,
13707 uuid.raw(), guestJudgement,
13708 aAuthParams[parmUser].c_str(),
13709 aAuthParams[parmPassword].c_str(),
13710 aAuthParams[parmDomain].c_str(),
13711 u32ClientId);
13712
13713 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13714 size_t cbPassword = aAuthParams[parmPassword].length();
13715 if (cbPassword)
13716 {
13717 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13718 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13719 }
13720
13721 if (result == AuthResultAccessGranted)
13722 aResult = "granted";
13723 else
13724 aResult = "denied";
13725
13726 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13727 aAuthParams[parmUser].c_str(), aResult.c_str()));
13728 }
13729 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13730 {
13731 enum VRDEAuthDisconnectParams
13732 {
13733 parmUuid = 1,
13734 parmClientId
13735 };
13736
13737 Guid uuid(aAuthParams[parmUuid]);
13738 uint32_t u32ClientId = 0;
13739 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13740 }
13741 else
13742 {
13743 hr = E_INVALIDARG;
13744 }
13745
13746 return hr;
13747}
13748
13749// public methods only for internal purposes
13750/////////////////////////////////////////////////////////////////////////////
13751
13752#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13753/**
13754 * Called from the client watcher thread to check for expected or unexpected
13755 * death of the client process that has a direct session to this machine.
13756 *
13757 * On Win32 and on OS/2, this method is called only when we've got the
13758 * mutex (i.e. the client has either died or terminated normally) so it always
13759 * returns @c true (the client is terminated, the session machine is
13760 * uninitialized).
13761 *
13762 * On other platforms, the method returns @c true if the client process has
13763 * terminated normally or abnormally and the session machine was uninitialized,
13764 * and @c false if the client process is still alive.
13765 *
13766 * @note Locks this object for writing.
13767 */
13768bool SessionMachine::i_checkForDeath()
13769{
13770 Uninit::Reason reason;
13771 bool terminated = false;
13772
13773 /* Enclose autoCaller with a block because calling uninit() from under it
13774 * will deadlock. */
13775 {
13776 AutoCaller autoCaller(this);
13777 if (!autoCaller.isOk())
13778 {
13779 /* return true if not ready, to cause the client watcher to exclude
13780 * the corresponding session from watching */
13781 LogFlowThisFunc(("Already uninitialized!\n"));
13782 return true;
13783 }
13784
13785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13786
13787 /* Determine the reason of death: if the session state is Closing here,
13788 * everything is fine. Otherwise it means that the client did not call
13789 * OnSessionEnd() before it released the IPC semaphore. This may happen
13790 * either because the client process has abnormally terminated, or
13791 * because it simply forgot to call ISession::Close() before exiting. We
13792 * threat the latter also as an abnormal termination (see
13793 * Session::uninit() for details). */
13794 reason = mData->mSession.mState == SessionState_Unlocking ?
13795 Uninit::Normal :
13796 Uninit::Abnormal;
13797
13798 if (mClientToken)
13799 terminated = mClientToken->release();
13800 } /* AutoCaller block */
13801
13802 if (terminated)
13803 uninit(reason);
13804
13805 return terminated;
13806}
13807
13808void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13809{
13810 LogFlowThisFunc(("\n"));
13811
13812 strTokenId.setNull();
13813
13814 AutoCaller autoCaller(this);
13815 AssertComRCReturnVoid(autoCaller.rc());
13816
13817 Assert(mClientToken);
13818 if (mClientToken)
13819 mClientToken->getId(strTokenId);
13820}
13821#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13822IToken *SessionMachine::i_getToken()
13823{
13824 LogFlowThisFunc(("\n"));
13825
13826 AutoCaller autoCaller(this);
13827 AssertComRCReturn(autoCaller.rc(), NULL);
13828
13829 Assert(mClientToken);
13830 if (mClientToken)
13831 return mClientToken->getToken();
13832 else
13833 return NULL;
13834}
13835#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13836
13837Machine::ClientToken *SessionMachine::i_getClientToken()
13838{
13839 LogFlowThisFunc(("\n"));
13840
13841 AutoCaller autoCaller(this);
13842 AssertComRCReturn(autoCaller.rc(), NULL);
13843
13844 return mClientToken;
13845}
13846
13847
13848/**
13849 * @note Locks this object for reading.
13850 */
13851HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13852{
13853 LogFlowThisFunc(("\n"));
13854
13855 AutoCaller autoCaller(this);
13856 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13857
13858 ComPtr<IInternalSessionControl> directControl;
13859 {
13860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13861 if (mData->mSession.mLockType == LockType_VM)
13862 directControl = mData->mSession.mDirectControl;
13863 }
13864
13865 /* ignore notifications sent after #OnSessionEnd() is called */
13866 if (!directControl)
13867 return S_OK;
13868
13869 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13870}
13871
13872/**
13873 * @note Locks this object for reading.
13874 */
13875HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13876 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13877 const Utf8Str &aGuestIp, LONG aGuestPort)
13878{
13879 LogFlowThisFunc(("\n"));
13880
13881 AutoCaller autoCaller(this);
13882 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13883
13884 ComPtr<IInternalSessionControl> directControl;
13885 {
13886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13887 if (mData->mSession.mLockType == LockType_VM)
13888 directControl = mData->mSession.mDirectControl;
13889 }
13890
13891 /* ignore notifications sent after #OnSessionEnd() is called */
13892 if (!directControl)
13893 return S_OK;
13894 /*
13895 * instead acting like callback we ask IVirtualBox deliver corresponding event
13896 */
13897
13898 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13899 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13900 return S_OK;
13901}
13902
13903/**
13904 * @note Locks this object for reading.
13905 */
13906HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13907{
13908 LogFlowThisFunc(("\n"));
13909
13910 AutoCaller autoCaller(this);
13911 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13912
13913 ComPtr<IInternalSessionControl> directControl;
13914 {
13915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13916 if (mData->mSession.mLockType == LockType_VM)
13917 directControl = mData->mSession.mDirectControl;
13918 }
13919
13920 /* ignore notifications sent after #OnSessionEnd() is called */
13921 if (!directControl)
13922 return S_OK;
13923
13924 return directControl->OnAudioAdapterChange(audioAdapter);
13925}
13926
13927/**
13928 * @note Locks this object for reading.
13929 */
13930HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13931{
13932 LogFlowThisFunc(("\n"));
13933
13934 AutoCaller autoCaller(this);
13935 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13936
13937 ComPtr<IInternalSessionControl> directControl;
13938 {
13939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13940 if (mData->mSession.mLockType == LockType_VM)
13941 directControl = mData->mSession.mDirectControl;
13942 }
13943
13944 /* ignore notifications sent after #OnSessionEnd() is called */
13945 if (!directControl)
13946 return S_OK;
13947
13948 return directControl->OnSerialPortChange(serialPort);
13949}
13950
13951/**
13952 * @note Locks this object for reading.
13953 */
13954HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13955{
13956 LogFlowThisFunc(("\n"));
13957
13958 AutoCaller autoCaller(this);
13959 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13960
13961 ComPtr<IInternalSessionControl> directControl;
13962 {
13963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13964 if (mData->mSession.mLockType == LockType_VM)
13965 directControl = mData->mSession.mDirectControl;
13966 }
13967
13968 /* ignore notifications sent after #OnSessionEnd() is called */
13969 if (!directControl)
13970 return S_OK;
13971
13972 return directControl->OnParallelPortChange(parallelPort);
13973}
13974
13975/**
13976 * @note Locks this object for reading.
13977 */
13978HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13979{
13980 LogFlowThisFunc(("\n"));
13981
13982 AutoCaller autoCaller(this);
13983 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13984
13985 ComPtr<IInternalSessionControl> directControl;
13986 {
13987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13988 if (mData->mSession.mLockType == LockType_VM)
13989 directControl = mData->mSession.mDirectControl;
13990 }
13991
13992 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
13993
13994 /* ignore notifications sent after #OnSessionEnd() is called */
13995 if (!directControl)
13996 return S_OK;
13997
13998 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
13999}
14000
14001/**
14002 * @note Locks this object for reading.
14003 */
14004HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14005{
14006 LogFlowThisFunc(("\n"));
14007
14008 AutoCaller autoCaller(this);
14009 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14010
14011 ComPtr<IInternalSessionControl> directControl;
14012 {
14013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14014 if (mData->mSession.mLockType == LockType_VM)
14015 directControl = mData->mSession.mDirectControl;
14016 }
14017
14018 mParent->i_onMediumChanged(aAttachment);
14019
14020 /* ignore notifications sent after #OnSessionEnd() is called */
14021 if (!directControl)
14022 return S_OK;
14023
14024 return directControl->OnMediumChange(aAttachment, aForce);
14025}
14026
14027HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14028{
14029 LogFlowThisFunc(("\n"));
14030
14031 AutoCaller autoCaller(this);
14032 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14033
14034 ComPtr<IInternalSessionControl> directControl;
14035 {
14036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14037 if (mData->mSession.mLockType == LockType_VM)
14038 directControl = mData->mSession.mDirectControl;
14039 }
14040
14041 /* ignore notifications sent after #OnSessionEnd() is called */
14042 if (!directControl)
14043 return S_OK;
14044
14045 return directControl->OnVMProcessPriorityChange(aPriority);
14046}
14047
14048/**
14049 * @note Locks this object for reading.
14050 */
14051HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14052{
14053 LogFlowThisFunc(("\n"));
14054
14055 AutoCaller autoCaller(this);
14056 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14057
14058 ComPtr<IInternalSessionControl> directControl;
14059 {
14060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14061 if (mData->mSession.mLockType == LockType_VM)
14062 directControl = mData->mSession.mDirectControl;
14063 }
14064
14065 /* ignore notifications sent after #OnSessionEnd() is called */
14066 if (!directControl)
14067 return S_OK;
14068
14069 return directControl->OnCPUChange(aCPU, aRemove);
14070}
14071
14072HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14073{
14074 LogFlowThisFunc(("\n"));
14075
14076 AutoCaller autoCaller(this);
14077 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14078
14079 ComPtr<IInternalSessionControl> directControl;
14080 {
14081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14082 if (mData->mSession.mLockType == LockType_VM)
14083 directControl = mData->mSession.mDirectControl;
14084 }
14085
14086 /* ignore notifications sent after #OnSessionEnd() is called */
14087 if (!directControl)
14088 return S_OK;
14089
14090 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14091}
14092
14093/**
14094 * @note Locks this object for reading.
14095 */
14096HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14097{
14098 LogFlowThisFunc(("\n"));
14099
14100 AutoCaller autoCaller(this);
14101 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14102
14103 ComPtr<IInternalSessionControl> directControl;
14104 {
14105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14106 if (mData->mSession.mLockType == LockType_VM)
14107 directControl = mData->mSession.mDirectControl;
14108 }
14109
14110 /* ignore notifications sent after #OnSessionEnd() is called */
14111 if (!directControl)
14112 return S_OK;
14113
14114 return directControl->OnVRDEServerChange(aRestart);
14115}
14116
14117/**
14118 * @note Locks this object for reading.
14119 */
14120HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14121{
14122 LogFlowThisFunc(("\n"));
14123
14124 AutoCaller autoCaller(this);
14125 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14126
14127 ComPtr<IInternalSessionControl> directControl;
14128 {
14129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14130 if (mData->mSession.mLockType == LockType_VM)
14131 directControl = mData->mSession.mDirectControl;
14132 }
14133
14134 /* ignore notifications sent after #OnSessionEnd() is called */
14135 if (!directControl)
14136 return S_OK;
14137
14138 return directControl->OnRecordingChange(aEnable);
14139}
14140
14141/**
14142 * @note Locks this object for reading.
14143 */
14144HRESULT SessionMachine::i_onUSBControllerChange()
14145{
14146 LogFlowThisFunc(("\n"));
14147
14148 AutoCaller autoCaller(this);
14149 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14150
14151 ComPtr<IInternalSessionControl> directControl;
14152 {
14153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14154 if (mData->mSession.mLockType == LockType_VM)
14155 directControl = mData->mSession.mDirectControl;
14156 }
14157
14158 /* ignore notifications sent after #OnSessionEnd() is called */
14159 if (!directControl)
14160 return S_OK;
14161
14162 return directControl->OnUSBControllerChange();
14163}
14164
14165/**
14166 * @note Locks this object for reading.
14167 */
14168HRESULT SessionMachine::i_onSharedFolderChange()
14169{
14170 LogFlowThisFunc(("\n"));
14171
14172 AutoCaller autoCaller(this);
14173 AssertComRCReturnRC(autoCaller.rc());
14174
14175 ComPtr<IInternalSessionControl> directControl;
14176 {
14177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14178 if (mData->mSession.mLockType == LockType_VM)
14179 directControl = mData->mSession.mDirectControl;
14180 }
14181
14182 /* ignore notifications sent after #OnSessionEnd() is called */
14183 if (!directControl)
14184 return S_OK;
14185
14186 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14187}
14188
14189/**
14190 * @note Locks this object for reading.
14191 */
14192HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14193{
14194 LogFlowThisFunc(("\n"));
14195
14196 AutoCaller autoCaller(this);
14197 AssertComRCReturnRC(autoCaller.rc());
14198
14199 ComPtr<IInternalSessionControl> directControl;
14200 {
14201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14202 if (mData->mSession.mLockType == LockType_VM)
14203 directControl = mData->mSession.mDirectControl;
14204 }
14205
14206 /* ignore notifications sent after #OnSessionEnd() is called */
14207 if (!directControl)
14208 return S_OK;
14209
14210 return directControl->OnClipboardModeChange(aClipboardMode);
14211}
14212
14213/**
14214 * @note Locks this object for reading.
14215 */
14216HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14217{
14218 LogFlowThisFunc(("\n"));
14219
14220 AutoCaller autoCaller(this);
14221 AssertComRCReturnRC(autoCaller.rc());
14222
14223 ComPtr<IInternalSessionControl> directControl;
14224 {
14225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14226 if (mData->mSession.mLockType == LockType_VM)
14227 directControl = mData->mSession.mDirectControl;
14228 }
14229
14230 /* ignore notifications sent after #OnSessionEnd() is called */
14231 if (!directControl)
14232 return S_OK;
14233
14234 return directControl->OnClipboardFileTransferModeChange(aEnable);
14235}
14236
14237/**
14238 * @note Locks this object for reading.
14239 */
14240HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14241{
14242 LogFlowThisFunc(("\n"));
14243
14244 AutoCaller autoCaller(this);
14245 AssertComRCReturnRC(autoCaller.rc());
14246
14247 ComPtr<IInternalSessionControl> directControl;
14248 {
14249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14250 if (mData->mSession.mLockType == LockType_VM)
14251 directControl = mData->mSession.mDirectControl;
14252 }
14253
14254 /* ignore notifications sent after #OnSessionEnd() is called */
14255 if (!directControl)
14256 return S_OK;
14257
14258 return directControl->OnDnDModeChange(aDnDMode);
14259}
14260
14261/**
14262 * @note Locks this object for reading.
14263 */
14264HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14265{
14266 LogFlowThisFunc(("\n"));
14267
14268 AutoCaller autoCaller(this);
14269 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14270
14271 ComPtr<IInternalSessionControl> directControl;
14272 {
14273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14274 if (mData->mSession.mLockType == LockType_VM)
14275 directControl = mData->mSession.mDirectControl;
14276 }
14277
14278 /* ignore notifications sent after #OnSessionEnd() is called */
14279 if (!directControl)
14280 return S_OK;
14281
14282 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14283}
14284
14285/**
14286 * @note Locks this object for reading.
14287 */
14288HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14289{
14290 LogFlowThisFunc(("\n"));
14291
14292 AutoCaller autoCaller(this);
14293 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14294
14295 ComPtr<IInternalSessionControl> directControl;
14296 {
14297 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14298 if (mData->mSession.mLockType == LockType_VM)
14299 directControl = mData->mSession.mDirectControl;
14300 }
14301
14302 /* ignore notifications sent after #OnSessionEnd() is called */
14303 if (!directControl)
14304 return S_OK;
14305
14306 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14307}
14308
14309/**
14310 * Returns @c true if this machine's USB controller reports it has a matching
14311 * filter for the given USB device and @c false otherwise.
14312 *
14313 * @note locks this object for reading.
14314 */
14315bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14316{
14317 AutoCaller autoCaller(this);
14318 /* silently return if not ready -- this method may be called after the
14319 * direct machine session has been called */
14320 if (!autoCaller.isOk())
14321 return false;
14322
14323#ifdef VBOX_WITH_USB
14324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14325
14326 switch (mData->mMachineState)
14327 {
14328 case MachineState_Starting:
14329 case MachineState_Restoring:
14330 case MachineState_TeleportingIn:
14331 case MachineState_Paused:
14332 case MachineState_Running:
14333 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14334 * elsewhere... */
14335 alock.release();
14336 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14337 default: break;
14338 }
14339#else
14340 NOREF(aDevice);
14341 NOREF(aMaskedIfs);
14342#endif
14343 return false;
14344}
14345
14346/**
14347 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14348 */
14349HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14350 IVirtualBoxErrorInfo *aError,
14351 ULONG aMaskedIfs,
14352 const com::Utf8Str &aCaptureFilename)
14353{
14354 LogFlowThisFunc(("\n"));
14355
14356 AutoCaller autoCaller(this);
14357
14358 /* This notification may happen after the machine object has been
14359 * uninitialized (the session was closed), so don't assert. */
14360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14361
14362 ComPtr<IInternalSessionControl> directControl;
14363 {
14364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14365 if (mData->mSession.mLockType == LockType_VM)
14366 directControl = mData->mSession.mDirectControl;
14367 }
14368
14369 /* fail on notifications sent after #OnSessionEnd() is called, it is
14370 * expected by the caller */
14371 if (!directControl)
14372 return E_FAIL;
14373
14374 /* No locks should be held at this point. */
14375 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14376 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14377
14378 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14379}
14380
14381/**
14382 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14383 */
14384HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14385 IVirtualBoxErrorInfo *aError)
14386{
14387 LogFlowThisFunc(("\n"));
14388
14389 AutoCaller autoCaller(this);
14390
14391 /* This notification may happen after the machine object has been
14392 * uninitialized (the session was closed), so don't assert. */
14393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14394
14395 ComPtr<IInternalSessionControl> directControl;
14396 {
14397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14398 if (mData->mSession.mLockType == LockType_VM)
14399 directControl = mData->mSession.mDirectControl;
14400 }
14401
14402 /* fail on notifications sent after #OnSessionEnd() is called, it is
14403 * expected by the caller */
14404 if (!directControl)
14405 return E_FAIL;
14406
14407 /* No locks should be held at this point. */
14408 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14409 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14410
14411 return directControl->OnUSBDeviceDetach(aId, aError);
14412}
14413
14414// protected methods
14415/////////////////////////////////////////////////////////////////////////////
14416
14417/**
14418 * Deletes the given file if it is no longer in use by either the current machine state
14419 * (if the machine is "saved") or any of the machine's snapshots.
14420 *
14421 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14422 * but is different for each SnapshotMachine. When calling this, the order of calling this
14423 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14424 * is therefore critical. I know, it's all rather messy.
14425 *
14426 * @param strStateFile
14427 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14428 * the test for whether the saved state file is in use.
14429 */
14430void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14431 Snapshot *pSnapshotToIgnore)
14432{
14433 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14434 if ( (strStateFile.isNotEmpty())
14435 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14436 )
14437 // ... and it must also not be shared with other snapshots
14438 if ( !mData->mFirstSnapshot
14439 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14440 // this checks the SnapshotMachine's state file paths
14441 )
14442 RTFileDelete(strStateFile.c_str());
14443}
14444
14445/**
14446 * Locks the attached media.
14447 *
14448 * All attached hard disks are locked for writing and DVD/floppy are locked for
14449 * reading. Parents of attached hard disks (if any) are locked for reading.
14450 *
14451 * This method also performs accessibility check of all media it locks: if some
14452 * media is inaccessible, the method will return a failure and a bunch of
14453 * extended error info objects per each inaccessible medium.
14454 *
14455 * Note that this method is atomic: if it returns a success, all media are
14456 * locked as described above; on failure no media is locked at all (all
14457 * succeeded individual locks will be undone).
14458 *
14459 * The caller is responsible for doing the necessary state sanity checks.
14460 *
14461 * The locks made by this method must be undone by calling #unlockMedia() when
14462 * no more needed.
14463 */
14464HRESULT SessionMachine::i_lockMedia()
14465{
14466 AutoCaller autoCaller(this);
14467 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14468
14469 AutoMultiWriteLock2 alock(this->lockHandle(),
14470 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14471
14472 /* bail out if trying to lock things with already set up locking */
14473 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14474
14475 MultiResult mrc(S_OK);
14476
14477 /* Collect locking information for all medium objects attached to the VM. */
14478 for (MediumAttachmentList::const_iterator
14479 it = mMediumAttachments->begin();
14480 it != mMediumAttachments->end();
14481 ++it)
14482 {
14483 MediumAttachment *pAtt = *it;
14484 DeviceType_T devType = pAtt->i_getType();
14485 Medium *pMedium = pAtt->i_getMedium();
14486
14487 MediumLockList *pMediumLockList(new MediumLockList());
14488 // There can be attachments without a medium (floppy/dvd), and thus
14489 // it's impossible to create a medium lock list. It still makes sense
14490 // to have the empty medium lock list in the map in case a medium is
14491 // attached later.
14492 if (pMedium != NULL)
14493 {
14494 MediumType_T mediumType = pMedium->i_getType();
14495 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14496 || mediumType == MediumType_Shareable;
14497 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14498
14499 alock.release();
14500 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14501 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14502 false /* fMediumLockWriteAll */,
14503 NULL,
14504 *pMediumLockList);
14505 alock.acquire();
14506 if (FAILED(mrc))
14507 {
14508 delete pMediumLockList;
14509 mData->mSession.mLockedMedia.Clear();
14510 break;
14511 }
14512 }
14513
14514 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14515 if (FAILED(rc))
14516 {
14517 mData->mSession.mLockedMedia.Clear();
14518 mrc = setError(rc,
14519 tr("Collecting locking information for all attached media failed"));
14520 break;
14521 }
14522 }
14523
14524 if (SUCCEEDED(mrc))
14525 {
14526 /* Now lock all media. If this fails, nothing is locked. */
14527 alock.release();
14528 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14529 alock.acquire();
14530 if (FAILED(rc))
14531 {
14532 mrc = setError(rc,
14533 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14534 }
14535 }
14536
14537 return mrc;
14538}
14539
14540/**
14541 * Undoes the locks made by by #lockMedia().
14542 */
14543HRESULT SessionMachine::i_unlockMedia()
14544{
14545 AutoCaller autoCaller(this);
14546 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14547
14548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14549
14550 /* we may be holding important error info on the current thread;
14551 * preserve it */
14552 ErrorInfoKeeper eik;
14553
14554 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14555 AssertComRC(rc);
14556 return rc;
14557}
14558
14559/**
14560 * Helper to change the machine state (reimplementation).
14561 *
14562 * @note Locks this object for writing.
14563 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14564 * it can cause crashes in random places due to unexpectedly committing
14565 * the current settings. The caller is responsible for that. The call
14566 * to saveStateSettings is fine, because this method does not commit.
14567 */
14568HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14569{
14570 LogFlowThisFuncEnter();
14571 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14572
14573 AutoCaller autoCaller(this);
14574 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14575
14576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14577
14578 MachineState_T oldMachineState = mData->mMachineState;
14579
14580 AssertMsgReturn(oldMachineState != aMachineState,
14581 ("oldMachineState=%s, aMachineState=%s\n",
14582 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14583 E_FAIL);
14584
14585 HRESULT rc = S_OK;
14586
14587 int stsFlags = 0;
14588 bool deleteSavedState = false;
14589
14590 /* detect some state transitions */
14591
14592 if ( ( oldMachineState == MachineState_Saved
14593 && aMachineState == MachineState_Restoring)
14594 || ( ( oldMachineState == MachineState_PoweredOff
14595 || oldMachineState == MachineState_Teleported
14596 || oldMachineState == MachineState_Aborted
14597 )
14598 && ( aMachineState == MachineState_TeleportingIn
14599 || aMachineState == MachineState_Starting
14600 )
14601 )
14602 )
14603 {
14604 /* The EMT thread is about to start */
14605
14606 /* Nothing to do here for now... */
14607
14608 /// @todo NEWMEDIA don't let mDVDDrive and other children
14609 /// change anything when in the Starting/Restoring state
14610 }
14611 else if ( ( oldMachineState == MachineState_Running
14612 || oldMachineState == MachineState_Paused
14613 || oldMachineState == MachineState_Teleporting
14614 || oldMachineState == MachineState_OnlineSnapshotting
14615 || oldMachineState == MachineState_LiveSnapshotting
14616 || oldMachineState == MachineState_Stuck
14617 || oldMachineState == MachineState_Starting
14618 || oldMachineState == MachineState_Stopping
14619 || oldMachineState == MachineState_Saving
14620 || oldMachineState == MachineState_Restoring
14621 || oldMachineState == MachineState_TeleportingPausedVM
14622 || oldMachineState == MachineState_TeleportingIn
14623 )
14624 && ( aMachineState == MachineState_PoweredOff
14625 || aMachineState == MachineState_Saved
14626 || aMachineState == MachineState_Teleported
14627 || aMachineState == MachineState_Aborted
14628 )
14629 )
14630 {
14631 /* The EMT thread has just stopped, unlock attached media. Note that as
14632 * opposed to locking that is done from Console, we do unlocking here
14633 * because the VM process may have aborted before having a chance to
14634 * properly unlock all media it locked. */
14635
14636 unlockMedia();
14637 }
14638
14639 if (oldMachineState == MachineState_Restoring)
14640 {
14641 if (aMachineState != MachineState_Saved)
14642 {
14643 /*
14644 * delete the saved state file once the machine has finished
14645 * restoring from it (note that Console sets the state from
14646 * Restoring to Saved if the VM couldn't restore successfully,
14647 * to give the user an ability to fix an error and retry --
14648 * we keep the saved state file in this case)
14649 */
14650 deleteSavedState = true;
14651 }
14652 }
14653 else if ( oldMachineState == MachineState_Saved
14654 && ( aMachineState == MachineState_PoweredOff
14655 || aMachineState == MachineState_Aborted
14656 || aMachineState == MachineState_Teleported
14657 )
14658 )
14659 {
14660 /*
14661 * delete the saved state after SessionMachine::ForgetSavedState() is called
14662 * or if the VM process (owning a direct VM session) crashed while the
14663 * VM was Saved
14664 */
14665
14666 /// @todo (dmik)
14667 // Not sure that deleting the saved state file just because of the
14668 // client death before it attempted to restore the VM is a good
14669 // thing. But when it crashes we need to go to the Aborted state
14670 // which cannot have the saved state file associated... The only
14671 // way to fix this is to make the Aborted condition not a VM state
14672 // but a bool flag: i.e., when a crash occurs, set it to true and
14673 // change the state to PoweredOff or Saved depending on the
14674 // saved state presence.
14675
14676 deleteSavedState = true;
14677 mData->mCurrentStateModified = TRUE;
14678 stsFlags |= SaveSTS_CurStateModified;
14679 }
14680
14681 if ( aMachineState == MachineState_Starting
14682 || aMachineState == MachineState_Restoring
14683 || aMachineState == MachineState_TeleportingIn
14684 )
14685 {
14686 /* set the current state modified flag to indicate that the current
14687 * state is no more identical to the state in the
14688 * current snapshot */
14689 if (!mData->mCurrentSnapshot.isNull())
14690 {
14691 mData->mCurrentStateModified = TRUE;
14692 stsFlags |= SaveSTS_CurStateModified;
14693 }
14694 }
14695
14696 if (deleteSavedState)
14697 {
14698 if (mRemoveSavedState)
14699 {
14700 Assert(!mSSData->strStateFilePath.isEmpty());
14701
14702 // it is safe to delete the saved state file if ...
14703 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14704 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14705 // ... none of the snapshots share the saved state file
14706 )
14707 RTFileDelete(mSSData->strStateFilePath.c_str());
14708 }
14709
14710 mSSData->strStateFilePath.setNull();
14711 stsFlags |= SaveSTS_StateFilePath;
14712 }
14713
14714 /* redirect to the underlying peer machine */
14715 mPeer->i_setMachineState(aMachineState);
14716
14717 if ( oldMachineState != MachineState_RestoringSnapshot
14718 && ( aMachineState == MachineState_PoweredOff
14719 || aMachineState == MachineState_Teleported
14720 || aMachineState == MachineState_Aborted
14721 || aMachineState == MachineState_Saved))
14722 {
14723 /* the machine has stopped execution
14724 * (or the saved state file was adopted) */
14725 stsFlags |= SaveSTS_StateTimeStamp;
14726 }
14727
14728 if ( ( oldMachineState == MachineState_PoweredOff
14729 || oldMachineState == MachineState_Aborted
14730 || oldMachineState == MachineState_Teleported
14731 )
14732 && aMachineState == MachineState_Saved)
14733 {
14734 /* the saved state file was adopted */
14735 Assert(!mSSData->strStateFilePath.isEmpty());
14736 stsFlags |= SaveSTS_StateFilePath;
14737 }
14738
14739#ifdef VBOX_WITH_GUEST_PROPS
14740 if ( aMachineState == MachineState_PoweredOff
14741 || aMachineState == MachineState_Aborted
14742 || aMachineState == MachineState_Teleported)
14743 {
14744 /* Make sure any transient guest properties get removed from the
14745 * property store on shutdown. */
14746 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14747
14748 /* remove it from the settings representation */
14749 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14750 for (settings::GuestPropertiesList::iterator
14751 it = llGuestProperties.begin();
14752 it != llGuestProperties.end();
14753 /*nothing*/)
14754 {
14755 const settings::GuestProperty &prop = *it;
14756 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14757 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14758 {
14759 it = llGuestProperties.erase(it);
14760 fNeedsSaving = true;
14761 }
14762 else
14763 {
14764 ++it;
14765 }
14766 }
14767
14768 /* Additionally remove it from the HWData representation. Required to
14769 * keep everything in sync, as this is what the API keeps using. */
14770 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14771 for (HWData::GuestPropertyMap::iterator
14772 it = llHWGuestProperties.begin();
14773 it != llHWGuestProperties.end();
14774 /*nothing*/)
14775 {
14776 uint32_t fFlags = it->second.mFlags;
14777 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14778 {
14779 /* iterator where we need to continue after the erase call
14780 * (C++03 is a fact still, and it doesn't return the iterator
14781 * which would allow continuing) */
14782 HWData::GuestPropertyMap::iterator it2 = it;
14783 ++it2;
14784 llHWGuestProperties.erase(it);
14785 it = it2;
14786 fNeedsSaving = true;
14787 }
14788 else
14789 {
14790 ++it;
14791 }
14792 }
14793
14794 if (fNeedsSaving)
14795 {
14796 mData->mCurrentStateModified = TRUE;
14797 stsFlags |= SaveSTS_CurStateModified;
14798 }
14799 }
14800#endif /* VBOX_WITH_GUEST_PROPS */
14801
14802 rc = i_saveStateSettings(stsFlags);
14803
14804 if ( ( oldMachineState != MachineState_PoweredOff
14805 && oldMachineState != MachineState_Aborted
14806 && oldMachineState != MachineState_Teleported
14807 )
14808 && ( aMachineState == MachineState_PoweredOff
14809 || aMachineState == MachineState_Aborted
14810 || aMachineState == MachineState_Teleported
14811 )
14812 )
14813 {
14814 /* we've been shut down for any reason */
14815 /* no special action so far */
14816 }
14817
14818 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14819 LogFlowThisFuncLeave();
14820 return rc;
14821}
14822
14823/**
14824 * Sends the current machine state value to the VM process.
14825 *
14826 * @note Locks this object for reading, then calls a client process.
14827 */
14828HRESULT SessionMachine::i_updateMachineStateOnClient()
14829{
14830 AutoCaller autoCaller(this);
14831 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14832
14833 ComPtr<IInternalSessionControl> directControl;
14834 {
14835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14836 AssertReturn(!!mData, E_FAIL);
14837 if (mData->mSession.mLockType == LockType_VM)
14838 directControl = mData->mSession.mDirectControl;
14839
14840 /* directControl may be already set to NULL here in #OnSessionEnd()
14841 * called too early by the direct session process while there is still
14842 * some operation (like deleting the snapshot) in progress. The client
14843 * process in this case is waiting inside Session::close() for the
14844 * "end session" process object to complete, while #uninit() called by
14845 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14846 * operation to complete. For now, we accept this inconsistent behavior
14847 * and simply do nothing here. */
14848
14849 if (mData->mSession.mState == SessionState_Unlocking)
14850 return S_OK;
14851 }
14852
14853 /* ignore notifications sent after #OnSessionEnd() is called */
14854 if (!directControl)
14855 return S_OK;
14856
14857 return directControl->UpdateMachineState(mData->mMachineState);
14858}
14859
14860
14861/*static*/
14862HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14863{
14864 va_list args;
14865 va_start(args, pcszMsg);
14866 HRESULT rc = setErrorInternal(aResultCode,
14867 getStaticClassIID(),
14868 getStaticComponentName(),
14869 Utf8Str(pcszMsg, args),
14870 false /* aWarning */,
14871 true /* aLogIt */);
14872 va_end(args);
14873 return rc;
14874}
14875
14876
14877HRESULT Machine::updateState(MachineState_T aState)
14878{
14879 NOREF(aState);
14880 ReturnComNotImplemented();
14881}
14882
14883HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14884{
14885 NOREF(aProgress);
14886 ReturnComNotImplemented();
14887}
14888
14889HRESULT Machine::endPowerUp(LONG aResult)
14890{
14891 NOREF(aResult);
14892 ReturnComNotImplemented();
14893}
14894
14895HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14896{
14897 NOREF(aProgress);
14898 ReturnComNotImplemented();
14899}
14900
14901HRESULT Machine::endPoweringDown(LONG aResult,
14902 const com::Utf8Str &aErrMsg)
14903{
14904 NOREF(aResult);
14905 NOREF(aErrMsg);
14906 ReturnComNotImplemented();
14907}
14908
14909HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14910 BOOL *aMatched,
14911 ULONG *aMaskedInterfaces)
14912{
14913 NOREF(aDevice);
14914 NOREF(aMatched);
14915 NOREF(aMaskedInterfaces);
14916 ReturnComNotImplemented();
14917
14918}
14919
14920HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14921{
14922 NOREF(aId); NOREF(aCaptureFilename);
14923 ReturnComNotImplemented();
14924}
14925
14926HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14927 BOOL aDone)
14928{
14929 NOREF(aId);
14930 NOREF(aDone);
14931 ReturnComNotImplemented();
14932}
14933
14934HRESULT Machine::autoCaptureUSBDevices()
14935{
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14940{
14941 NOREF(aDone);
14942 ReturnComNotImplemented();
14943}
14944
14945HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14946 ComPtr<IProgress> &aProgress)
14947{
14948 NOREF(aSession);
14949 NOREF(aProgress);
14950 ReturnComNotImplemented();
14951}
14952
14953HRESULT Machine::finishOnlineMergeMedium()
14954{
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14959{
14960 RT_NOREF(aParms, aID);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14965{
14966 RT_NOREF(aID);
14967 ReturnComNotImplemented();
14968}
14969
14970HRESULT Machine::clipboardAreaAttach(ULONG aID)
14971{
14972 RT_NOREF(aID);
14973 ReturnComNotImplemented();
14974}
14975HRESULT Machine::clipboardAreaDetach(ULONG aID)
14976{
14977 RT_NOREF(aID);
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
14982{
14983 RT_NOREF(aID);
14984 ReturnComNotImplemented();
14985}
14986
14987HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
14988{
14989 RT_NOREF(aID, aRefCount);
14990 ReturnComNotImplemented();
14991}
14992
14993HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14994 std::vector<com::Utf8Str> &aValues,
14995 std::vector<LONG64> &aTimestamps,
14996 std::vector<com::Utf8Str> &aFlags)
14997{
14998 NOREF(aNames);
14999 NOREF(aValues);
15000 NOREF(aTimestamps);
15001 NOREF(aFlags);
15002 ReturnComNotImplemented();
15003}
15004
15005HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15006 const com::Utf8Str &aValue,
15007 LONG64 aTimestamp,
15008 const com::Utf8Str &aFlags)
15009{
15010 NOREF(aName);
15011 NOREF(aValue);
15012 NOREF(aTimestamp);
15013 NOREF(aFlags);
15014 ReturnComNotImplemented();
15015}
15016
15017HRESULT Machine::lockMedia()
15018{
15019 ReturnComNotImplemented();
15020}
15021
15022HRESULT Machine::unlockMedia()
15023{
15024 ReturnComNotImplemented();
15025}
15026
15027HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15028 ComPtr<IMediumAttachment> &aNewAttachment)
15029{
15030 NOREF(aAttachment);
15031 NOREF(aNewAttachment);
15032 ReturnComNotImplemented();
15033}
15034
15035HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15036 ULONG aCpuUser,
15037 ULONG aCpuKernel,
15038 ULONG aCpuIdle,
15039 ULONG aMemTotal,
15040 ULONG aMemFree,
15041 ULONG aMemBalloon,
15042 ULONG aMemShared,
15043 ULONG aMemCache,
15044 ULONG aPagedTotal,
15045 ULONG aMemAllocTotal,
15046 ULONG aMemFreeTotal,
15047 ULONG aMemBalloonTotal,
15048 ULONG aMemSharedTotal,
15049 ULONG aVmNetRx,
15050 ULONG aVmNetTx)
15051{
15052 NOREF(aValidStats);
15053 NOREF(aCpuUser);
15054 NOREF(aCpuKernel);
15055 NOREF(aCpuIdle);
15056 NOREF(aMemTotal);
15057 NOREF(aMemFree);
15058 NOREF(aMemBalloon);
15059 NOREF(aMemShared);
15060 NOREF(aMemCache);
15061 NOREF(aPagedTotal);
15062 NOREF(aMemAllocTotal);
15063 NOREF(aMemFreeTotal);
15064 NOREF(aMemBalloonTotal);
15065 NOREF(aMemSharedTotal);
15066 NOREF(aVmNetRx);
15067 NOREF(aVmNetTx);
15068 ReturnComNotImplemented();
15069}
15070
15071HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15072 com::Utf8Str &aResult)
15073{
15074 NOREF(aAuthParams);
15075 NOREF(aResult);
15076 ReturnComNotImplemented();
15077}
15078
15079com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15080{
15081 com::Utf8Str strControllerName = "Unknown";
15082 switch (aBusType)
15083 {
15084 case StorageBus_IDE:
15085 {
15086 strControllerName = "IDE";
15087 break;
15088 }
15089 case StorageBus_SATA:
15090 {
15091 strControllerName = "SATA";
15092 break;
15093 }
15094 case StorageBus_SCSI:
15095 {
15096 strControllerName = "SCSI";
15097 break;
15098 }
15099 case StorageBus_Floppy:
15100 {
15101 strControllerName = "Floppy";
15102 break;
15103 }
15104 case StorageBus_SAS:
15105 {
15106 strControllerName = "SAS";
15107 break;
15108 }
15109 case StorageBus_USB:
15110 {
15111 strControllerName = "USB";
15112 break;
15113 }
15114 default:
15115 break;
15116 }
15117 return strControllerName;
15118}
15119
15120HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15121{
15122 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15123
15124 AutoCaller autoCaller(this);
15125 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15126
15127 HRESULT rc = S_OK;
15128
15129 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15130 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15131 rc = getUSBDeviceFilters(usbDeviceFilters);
15132 if (FAILED(rc)) return rc;
15133
15134 NOREF(aFlags);
15135 com::Utf8Str osTypeId;
15136 ComObjPtr<GuestOSType> osType = NULL;
15137
15138 /* Get the guest os type as a string from the VB. */
15139 rc = getOSTypeId(osTypeId);
15140 if (FAILED(rc)) return rc;
15141
15142 /* Get the os type obj that coresponds, can be used to get
15143 * the defaults for this guest OS. */
15144 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15145 if (FAILED(rc)) return rc;
15146
15147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15148
15149 /* Let the OS type select 64-bit ness. */
15150 mHWData->mLongMode = osType->i_is64Bit()
15151 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15152
15153 /* Let the OS type enable the X2APIC */
15154 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15155
15156 /* This one covers IOAPICEnabled. */
15157 mBIOSSettings->i_applyDefaults(osType);
15158
15159 /* Initialize default record settings. */
15160 mRecordingSettings->i_applyDefaults();
15161
15162 /* Initialize default BIOS settings here */
15163 /* Hardware virtualization must be ON by default */
15164 mHWData->mAPIC = true;
15165 mHWData->mHWVirtExEnabled = true;
15166
15167 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15168 if (FAILED(rc)) return rc;
15169
15170 /* Graphics stuff. */
15171 GraphicsControllerType_T graphicsController;
15172 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15173 if (FAILED(rc)) return rc;
15174
15175 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15176 if (FAILED(rc)) return rc;
15177
15178 ULONG vramSize;
15179 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15180 if (FAILED(rc)) return rc;
15181
15182 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15183 if (FAILED(rc)) return rc;
15184
15185 BOOL fAccelerate2DVideoEnabled;
15186 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15187 if (FAILED(rc)) return rc;
15188
15189 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15190 if (FAILED(rc)) return rc;
15191
15192 BOOL fAccelerate3DEnabled;
15193 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15194 if (FAILED(rc)) return rc;
15195
15196 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15197 if (FAILED(rc)) return rc;
15198
15199 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15200 if (FAILED(rc)) return rc;
15201
15202 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15203 if (FAILED(rc)) return rc;
15204
15205 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15206 if (FAILED(rc)) return rc;
15207
15208 BOOL mRTCUseUTC;
15209 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15210 if (FAILED(rc)) return rc;
15211
15212 setRTCUseUTC(mRTCUseUTC);
15213 if (FAILED(rc)) return rc;
15214
15215 /* the setter does more than just the assignment, so use it */
15216 ChipsetType_T enmChipsetType;
15217 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15218 if (FAILED(rc)) return rc;
15219
15220 rc = COMSETTER(ChipsetType)(enmChipsetType);
15221 if (FAILED(rc)) return rc;
15222
15223 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15224 if (FAILED(rc)) return rc;
15225
15226 /* Apply network adapters defaults */
15227 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15228 mNetworkAdapters[slot]->i_applyDefaults(osType);
15229
15230 /* Apply serial port defaults */
15231 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15232 mSerialPorts[slot]->i_applyDefaults(osType);
15233
15234 /* Apply parallel port defaults - not OS dependent*/
15235 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15236 mParallelPorts[slot]->i_applyDefaults();
15237
15238 /* Audio stuff. */
15239 AudioControllerType_T audioController;
15240 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15241 if (FAILED(rc)) return rc;
15242
15243 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15244 if (FAILED(rc)) return rc;
15245
15246 AudioCodecType_T audioCodec;
15247 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15248 if (FAILED(rc)) return rc;
15249
15250 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15251 if (FAILED(rc)) return rc;
15252
15253 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15254 if (FAILED(rc)) return rc;
15255
15256 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15257 if (FAILED(rc)) return rc;
15258
15259 /* Storage Controllers */
15260 StorageControllerType_T hdStorageControllerType;
15261 StorageBus_T hdStorageBusType;
15262 StorageControllerType_T dvdStorageControllerType;
15263 StorageBus_T dvdStorageBusType;
15264 BOOL recommendedFloppy;
15265 ComPtr<IStorageController> floppyController;
15266 ComPtr<IStorageController> hdController;
15267 ComPtr<IStorageController> dvdController;
15268 Utf8Str strFloppyName, strDVDName, strHDName;
15269
15270 /* GUI auto generates controller names using bus type. Do the same*/
15271 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15272
15273 /* Floppy recommended? add one. */
15274 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15275 if (FAILED(rc)) return rc;
15276 if (recommendedFloppy)
15277 {
15278 rc = addStorageController(strFloppyName,
15279 StorageBus_Floppy,
15280 floppyController);
15281 if (FAILED(rc)) return rc;
15282 }
15283
15284 /* Setup one DVD storage controller. */
15285 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15286 if (FAILED(rc)) return rc;
15287
15288 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15289 if (FAILED(rc)) return rc;
15290
15291 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15292
15293 rc = addStorageController(strDVDName,
15294 dvdStorageBusType,
15295 dvdController);
15296 if (FAILED(rc)) return rc;
15297
15298 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15299 if (FAILED(rc)) return rc;
15300
15301 /* Setup one HDD storage controller. */
15302 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15303 if (FAILED(rc)) return rc;
15304
15305 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15306 if (FAILED(rc)) return rc;
15307
15308 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15309
15310 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15311 {
15312 rc = addStorageController(strHDName,
15313 hdStorageBusType,
15314 hdController);
15315 if (FAILED(rc)) return rc;
15316
15317 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15318 if (FAILED(rc)) return rc;
15319 }
15320 else
15321 {
15322 /* The HD controller is the same as DVD: */
15323 hdController = dvdController;
15324 }
15325
15326 /* Limit the AHCI port count if it's used because windows has trouble with
15327 * too many ports and other guest (OS X in particular) may take extra long
15328 * boot: */
15329
15330 // pParent = static_cast<Medium*>(aP)
15331 IStorageController *temp = hdController;
15332 ComObjPtr<StorageController> storageController;
15333 storageController = static_cast<StorageController *>(temp);
15334
15335 // tempHDController = aHDController;
15336 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15337 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15338 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15339 storageController->COMSETTER(PortCount)(1);
15340
15341 /* USB stuff */
15342
15343 bool ohciEnabled = false;
15344
15345 ComPtr<IUSBController> usbController;
15346 BOOL recommendedUSB3;
15347 BOOL recommendedUSB;
15348 BOOL usbProxyAvailable;
15349
15350 getUSBProxyAvailable(&usbProxyAvailable);
15351 if (FAILED(rc)) return rc;
15352
15353 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15354 if (FAILED(rc)) return rc;
15355 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15356 if (FAILED(rc)) return rc;
15357
15358 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15359 {
15360#ifdef VBOX_WITH_EXTPACK
15361 /* USB 3.0 is only available if the proper ExtPack is installed. */
15362 ExtPackManager *aManager = mParent->i_getExtPackManager();
15363 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15364 {
15365 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15366 if (FAILED(rc)) return rc;
15367
15368 /* xHci includes OHCI */
15369 ohciEnabled = true;
15370 }
15371#endif
15372 }
15373 if ( !ohciEnabled
15374 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15375 {
15376 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15377 if (FAILED(rc)) return rc;
15378 ohciEnabled = true;
15379
15380#ifdef VBOX_WITH_EXTPACK
15381 /* USB 2.0 is only available if the proper ExtPack is installed.
15382 * Note. Configuring EHCI here and providing messages about
15383 * the missing extpack isn't exactly clean, but it is a
15384 * necessary evil to patch over legacy compatability issues
15385 * introduced by the new distribution model. */
15386 ExtPackManager *manager = mParent->i_getExtPackManager();
15387 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15388 {
15389 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15390 if (FAILED(rc)) return rc;
15391 }
15392#endif
15393 }
15394
15395 /* Set recommended human interface device types: */
15396 BOOL recommendedUSBHID;
15397 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15398 if (FAILED(rc)) return rc;
15399
15400 if (recommendedUSBHID)
15401 {
15402 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15403 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15404 if (!ohciEnabled && !usbDeviceFilters.isNull())
15405 {
15406 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15407 if (FAILED(rc)) return rc;
15408 }
15409 }
15410
15411 BOOL recommendedUSBTablet;
15412 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15413 if (FAILED(rc)) return rc;
15414
15415 if (recommendedUSBTablet)
15416 {
15417 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15418 if (!ohciEnabled && !usbDeviceFilters.isNull())
15419 {
15420 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15421 if (FAILED(rc)) return rc;
15422 }
15423 }
15424 return S_OK;
15425}
15426
15427/* This isn't handled entirely by the wrapper generator yet. */
15428#ifdef VBOX_WITH_XPCOM
15429NS_DECL_CLASSINFO(SessionMachine)
15430NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15431
15432NS_DECL_CLASSINFO(SnapshotMachine)
15433NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15434#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