VirtualBox

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

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

Main/comimpl.xsl,++: fireXxxxEvent -> ::FireXxxxEvent. bugref:9790

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 536.8 KB
Line 
1/* $Id: MachineImpl.cpp 85300 2020-07-13 10:04:45Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#ifdef VBOX_WITH_SHARED_CLIPBOARD
91# include <VBox/HostServices/VBoxClipboardSvc.h>
92#endif
93
94#include "VBox/com/MultiResult.h"
95
96#include <algorithm>
97
98#ifdef VBOX_WITH_DTRACE_R3_MAIN
99# include "dtrace/VBoxAPI.h"
100#endif
101
102#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
103# define HOSTSUFF_EXE ".exe"
104#else /* !RT_OS_WINDOWS */
105# define HOSTSUFF_EXE ""
106#endif /* !RT_OS_WINDOWS */
107
108// defines / prototypes
109/////////////////////////////////////////////////////////////////////////////
110
111/////////////////////////////////////////////////////////////////////////////
112// Machine::Data structure
113/////////////////////////////////////////////////////////////////////////////
114
115Machine::Data::Data()
116{
117 mRegistered = FALSE;
118 pMachineConfigFile = NULL;
119 /* Contains hints on what has changed when the user is using the VM (config
120 * changes, running the VM, ...). This is used to decide if a config needs
121 * to be written to disk. */
122 flModifications = 0;
123 /* VM modification usually also trigger setting the current state to
124 * "Modified". Although this is not always the case. An e.g. is the VM
125 * initialization phase or when snapshot related data is changed. The
126 * actually behavior is controlled by the following flag. */
127 m_fAllowStateModification = false;
128 mAccessible = FALSE;
129 /* mUuid is initialized in Machine::init() */
130
131 mMachineState = MachineState_PoweredOff;
132 RTTimeNow(&mLastStateChange);
133
134 mMachineStateDeps = 0;
135 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
136 mMachineStateChangePending = 0;
137
138 mCurrentStateModified = TRUE;
139 mGuestPropertiesModified = FALSE;
140
141 mSession.mPID = NIL_RTPROCESS;
142 mSession.mLockType = LockType_Null;
143 mSession.mState = SessionState_Unlocked;
144}
145
146Machine::Data::~Data()
147{
148 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
149 {
150 RTSemEventMultiDestroy(mMachineStateDepsSem);
151 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
152 }
153 if (pMachineConfigFile)
154 {
155 delete pMachineConfigFile;
156 pMachineConfigFile = NULL;
157 }
158}
159
160/////////////////////////////////////////////////////////////////////////////
161// Machine::HWData structure
162/////////////////////////////////////////////////////////////////////////////
163
164Machine::HWData::HWData()
165{
166 /* default values for a newly created machine */
167 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
168 mMemorySize = 128;
169 mCPUCount = 1;
170 mCPUHotPlugEnabled = false;
171 mMemoryBalloonSize = 0;
172 mPageFusionEnabled = false;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mL1DFlushOnSched = true;
199 mL1DFlushOnVMEntry = false;
200 mMDSClearOnSched = true;
201 mMDSClearOnVMEntry = false;
202 mNestedHWVirt = false;
203 mHPETEnabled = false;
204 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
205 mCpuIdPortabilityLevel = 0;
206 mCpuProfile = "host";
207
208 /* default boot order: floppy - DVD - HDD */
209 mBootOrder[0] = DeviceType_Floppy;
210 mBootOrder[1] = DeviceType_DVD;
211 mBootOrder[2] = DeviceType_HardDisk;
212 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
213 mBootOrder[i] = DeviceType_Null;
214
215 mClipboardMode = ClipboardMode_Disabled;
216 mClipboardFileTransfersEnabled = FALSE;
217
218 mDnDMode = DnDMode_Disabled;
219
220 mFirmwareType = FirmwareType_BIOS;
221 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
222 mPointingHIDType = PointingHIDType_PS2Mouse;
223 mChipsetType = ChipsetType_PIIX3;
224 mParavirtProvider = ParavirtProvider_Default;
225 mEmulatedUSBCardReaderEnabled = FALSE;
226
227 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
228 mCPUAttached[i] = false;
229
230 mIOCacheEnabled = true;
231 mIOCacheSize = 5; /* 5MB */
232}
233
234Machine::HWData::~HWData()
235{
236}
237
238/////////////////////////////////////////////////////////////////////////////
239// Machine class
240/////////////////////////////////////////////////////////////////////////////
241
242// constructor / destructor
243/////////////////////////////////////////////////////////////////////////////
244
245Machine::Machine() :
246#ifdef VBOX_WITH_RESOURCE_USAGE_API
247 mCollectorGuest(NULL),
248#endif
249 mPeer(NULL),
250 mParent(NULL),
251 mSerialPorts(),
252 mParallelPorts(),
253 uRegistryNeedsSaving(0)
254{}
255
256Machine::~Machine()
257{}
258
259HRESULT Machine::FinalConstruct()
260{
261 LogFlowThisFunc(("\n"));
262 return BaseFinalConstruct();
263}
264
265void Machine::FinalRelease()
266{
267 LogFlowThisFunc(("\n"));
268 uninit();
269 BaseFinalRelease();
270}
271
272/**
273 * Initializes a new machine instance; this init() variant creates a new, empty machine.
274 * This gets called from VirtualBox::CreateMachine().
275 *
276 * @param aParent Associated parent object
277 * @param strConfigFile Local file system path to the VM settings file (can
278 * be relative to the VirtualBox config directory).
279 * @param strName name for the machine
280 * @param llGroups list of groups for the machine
281 * @param strOsType OS Type string (stored as is if aOsType is NULL).
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
286 * scheme (includes the UUID).
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 const Utf8Str &strOsType,
295 GuestOSType *aOsType,
296 const Guid &aId,
297 bool fForceOverwrite,
298 bool fDirectoryIncludesUUID)
299{
300 LogFlowThisFuncEnter();
301 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
302
303 /* Enclose the state transition NotReady->InInit->Ready */
304 AutoInitSpan autoInitSpan(this);
305 AssertReturn(autoInitSpan.isOk(), E_FAIL);
306
307 HRESULT rc = initImpl(aParent, strConfigFile);
308 if (FAILED(rc)) return rc;
309
310 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
311 if (FAILED(rc)) return rc;
312
313 if (SUCCEEDED(rc))
314 {
315 // create an empty machine config
316 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
317
318 rc = initDataAndChildObjects();
319 }
320
321 if (SUCCEEDED(rc))
322 {
323 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
324 mData->mAccessible = TRUE;
325
326 unconst(mData->mUuid) = aId;
327
328 mUserData->s.strName = strName;
329
330 if (llGroups.size())
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Let the OS type select 64-bit ness. */
349 mHWData->mLongMode = aOsType->i_is64Bit()
350 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
351
352 /* Let the OS type enable the X2APIC */
353 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
354 }
355 else if (!strOsType.isEmpty())
356 {
357 /* Store OS type */
358 mUserData->s.strOsType = strOsType;
359
360 /* No guest OS type object. Pick some plausible defaults which the
361 * host can handle. There's no way to know or validate anything. */
362 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363 mHWData->mX2APIC = false;
364 }
365
366 /* Apply BIOS defaults. */
367 mBIOSSettings->i_applyDefaults(aOsType);
368
369 /* Apply record defaults. */
370 mRecordingSettings->i_applyDefaults();
371
372 /* Apply network adapters defaults */
373 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
374 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
375
376 /* Apply serial port defaults */
377 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
378 mSerialPorts[slot]->i_applyDefaults(aOsType);
379
380 /* Apply parallel port defaults */
381 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
382 mParallelPorts[slot]->i_applyDefaults();
383
384 /* At this point the changing of the current state modification
385 * flag is allowed. */
386 i_allowStateModification();
387
388 /* commit all changes made during the initialization */
389 i_commit();
390 }
391
392 /* Confirm a successful initialization when it's the case */
393 if (SUCCEEDED(rc))
394 {
395 if (mData->mAccessible)
396 autoInitSpan.setSucceeded();
397 else
398 autoInitSpan.setLimited();
399 }
400
401 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
402 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
403 mData->mRegistered,
404 mData->mAccessible,
405 rc));
406
407 LogFlowThisFuncLeave();
408
409 return rc;
410}
411
412/**
413 * Initializes a new instance with data from machine XML (formerly Init_Registered).
414 * Gets called in two modes:
415 *
416 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
417 * UUID is specified and we mark the machine as "registered";
418 *
419 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
420 * and the machine remains unregistered until RegisterMachine() is called.
421 *
422 * @param aParent Associated parent object
423 * @param strConfigFile Local file system path to the VM settings file (can
424 * be relative to the VirtualBox config directory).
425 * @param aId UUID of the machine or NULL (see above).
426 *
427 * @return Success indicator. if not S_OK, the machine object is invalid
428 */
429HRESULT Machine::initFromSettings(VirtualBox *aParent,
430 const Utf8Str &strConfigFile,
431 const Guid *aId)
432{
433 LogFlowThisFuncEnter();
434 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
435
436 /* Enclose the state transition NotReady->InInit->Ready */
437 AutoInitSpan autoInitSpan(this);
438 AssertReturn(autoInitSpan.isOk(), E_FAIL);
439
440 HRESULT rc = initImpl(aParent, strConfigFile);
441 if (FAILED(rc)) return rc;
442
443 if (aId)
444 {
445 // loading a registered VM:
446 unconst(mData->mUuid) = *aId;
447 mData->mRegistered = TRUE;
448 // now load the settings from XML:
449 rc = i_registeredInit();
450 // this calls initDataAndChildObjects() and loadSettings()
451 }
452 else
453 {
454 // opening an unregistered VM (VirtualBox::OpenMachine()):
455 rc = initDataAndChildObjects();
456
457 if (SUCCEEDED(rc))
458 {
459 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
460 mData->mAccessible = TRUE;
461
462 try
463 {
464 // load and parse machine XML; this will throw on XML or logic errors
465 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
466
467 // reject VM UUID duplicates, they can happen if someone
468 // tries to register an already known VM config again
469 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
470 true /* fPermitInaccessible */,
471 false /* aDoSetError */,
472 NULL) != VBOX_E_OBJECT_NOT_FOUND)
473 {
474 throw setError(E_FAIL,
475 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
476 mData->m_strConfigFile.c_str());
477 }
478
479 // use UUID from machine config
480 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
481
482 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
483 NULL /* puuidRegistry */);
484 if (FAILED(rc)) throw rc;
485
486 /* At this point the changing of the current state modification
487 * flag is allowed. */
488 i_allowStateModification();
489
490 i_commit();
491 }
492 catch (HRESULT err)
493 {
494 /* we assume that error info is set by the thrower */
495 rc = err;
496 }
497 catch (...)
498 {
499 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
500 }
501 }
502 }
503
504 /* Confirm a successful initialization when it's the case */
505 if (SUCCEEDED(rc))
506 {
507 if (mData->mAccessible)
508 autoInitSpan.setSucceeded();
509 else
510 {
511 autoInitSpan.setLimited();
512
513 // uninit media from this machine's media registry, or else
514 // reloading the settings will fail
515 mParent->i_unregisterMachineMedia(i_getId());
516 }
517 }
518
519 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
520 "rc=%08X\n",
521 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
522 mData->mRegistered, mData->mAccessible, rc));
523
524 LogFlowThisFuncLeave();
525
526 return rc;
527}
528
529/**
530 * Initializes a new instance from a machine config that is already in memory
531 * (import OVF case). Since we are importing, the UUID in the machine
532 * config is ignored and we always generate a fresh one.
533 *
534 * @param aParent Associated parent object.
535 * @param strName Name for the new machine; this overrides what is specified in config.
536 * @param strSettingsFilename File name of .vbox file.
537 * @param config Machine configuration loaded and parsed from XML.
538 *
539 * @return Success indicator. if not S_OK, the machine object is invalid
540 */
541HRESULT Machine::init(VirtualBox *aParent,
542 const Utf8Str &strName,
543 const Utf8Str &strSettingsFilename,
544 const settings::MachineConfigFile &config)
545{
546 LogFlowThisFuncEnter();
547
548 /* Enclose the state transition NotReady->InInit->Ready */
549 AutoInitSpan autoInitSpan(this);
550 AssertReturn(autoInitSpan.isOk(), E_FAIL);
551
552 HRESULT rc = initImpl(aParent, strSettingsFilename);
553 if (FAILED(rc)) return rc;
554
555 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
556 if (FAILED(rc)) return rc;
557
558 rc = initDataAndChildObjects();
559
560 if (SUCCEEDED(rc))
561 {
562 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
563 mData->mAccessible = TRUE;
564
565 // create empty machine config for instance data
566 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
567
568 // generate fresh UUID, ignore machine config
569 unconst(mData->mUuid).create();
570
571 rc = i_loadMachineDataFromSettings(config,
572 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
573
574 // override VM name as well, it may be different
575 mUserData->s.strName = strName;
576
577 if (SUCCEEDED(rc))
578 {
579 /* At this point the changing of the current state modification
580 * flag is allowed. */
581 i_allowStateModification();
582
583 /* commit all changes made during the initialization */
584 i_commit();
585 }
586 }
587
588 /* Confirm a successful initialization when it's the case */
589 if (SUCCEEDED(rc))
590 {
591 if (mData->mAccessible)
592 autoInitSpan.setSucceeded();
593 else
594 {
595 /* Ignore all errors from unregistering, they would destroy
596- * the more interesting error information we already have,
597- * pinpointing the issue with the VM config. */
598 ErrorInfoKeeper eik;
599
600 autoInitSpan.setLimited();
601
602 // uninit media from this machine's media registry, or else
603 // reloading the settings will fail
604 mParent->i_unregisterMachineMedia(i_getId());
605 }
606 }
607
608 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
609 "rc=%08X\n",
610 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
611 mData->mRegistered, mData->mAccessible, rc));
612
613 LogFlowThisFuncLeave();
614
615 return rc;
616}
617
618/**
619 * Shared code between the various init() implementations.
620 * @param aParent The VirtualBox object.
621 * @param strConfigFile Settings file.
622 * @return
623 */
624HRESULT Machine::initImpl(VirtualBox *aParent,
625 const Utf8Str &strConfigFile)
626{
627 LogFlowThisFuncEnter();
628
629 AssertReturn(aParent, E_INVALIDARG);
630 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
631
632 HRESULT rc = S_OK;
633
634 /* share the parent weakly */
635 unconst(mParent) = aParent;
636
637 /* allocate the essential machine data structure (the rest will be
638 * allocated later by initDataAndChildObjects() */
639 mData.allocate();
640
641 /* memorize the config file name (as provided) */
642 mData->m_strConfigFile = strConfigFile;
643
644 /* get the full file name */
645 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
646 if (RT_FAILURE(vrc1))
647 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
648 tr("Invalid machine settings file name '%s' (%Rrc)"),
649 strConfigFile.c_str(),
650 vrc1);
651
652 LogFlowThisFuncLeave();
653
654 return rc;
655}
656
657/**
658 * Tries to create a machine settings file in the path stored in the machine
659 * instance data. Used when a new machine is created to fail gracefully if
660 * the settings file could not be written (e.g. because machine dir is read-only).
661 * @return
662 */
663HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
664{
665 HRESULT rc = S_OK;
666
667 // when we create a new machine, we must be able to create the settings file
668 RTFILE f = NIL_RTFILE;
669 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
670 if ( RT_SUCCESS(vrc)
671 || vrc == VERR_SHARING_VIOLATION
672 )
673 {
674 if (RT_SUCCESS(vrc))
675 RTFileClose(f);
676 if (!fForceOverwrite)
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Machine settings file '%s' already exists"),
679 mData->m_strConfigFileFull.c_str());
680 else
681 {
682 /* try to delete the config file, as otherwise the creation
683 * of a new settings file will fail. */
684 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
685 if (RT_FAILURE(vrc2))
686 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
687 tr("Could not delete the existing settings file '%s' (%Rrc)"),
688 mData->m_strConfigFileFull.c_str(), vrc2);
689 }
690 }
691 else if ( vrc != VERR_FILE_NOT_FOUND
692 && vrc != VERR_PATH_NOT_FOUND
693 )
694 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
695 tr("Invalid machine settings file name '%s' (%Rrc)"),
696 mData->m_strConfigFileFull.c_str(),
697 vrc);
698 return rc;
699}
700
701/**
702 * Initializes the registered machine by loading the settings file.
703 * This method is separated from #init() in order to make it possible to
704 * retry the operation after VirtualBox startup instead of refusing to
705 * startup the whole VirtualBox server in case if the settings file of some
706 * registered VM is invalid or inaccessible.
707 *
708 * @note Must be always called from this object's write lock
709 * (unless called from #init() that doesn't need any locking).
710 * @note Locks the mUSBController method for writing.
711 * @note Subclasses must not call this method.
712 */
713HRESULT Machine::i_registeredInit()
714{
715 AssertReturn(!i_isSessionMachine(), E_FAIL);
716 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
717 AssertReturn(mData->mUuid.isValid(), E_FAIL);
718 AssertReturn(!mData->mAccessible, E_FAIL);
719
720 HRESULT rc = initDataAndChildObjects();
721
722 if (SUCCEEDED(rc))
723 {
724 /* Temporarily reset the registered flag in order to let setters
725 * potentially called from loadSettings() succeed (isMutable() used in
726 * all setters will return FALSE for a Machine instance if mRegistered
727 * is TRUE). */
728 mData->mRegistered = FALSE;
729
730 try
731 {
732 // load and parse machine XML; this will throw on XML or logic errors
733 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
734
735 if (mData->mUuid != mData->pMachineConfigFile->uuid)
736 throw setError(E_FAIL,
737 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
738 mData->pMachineConfigFile->uuid.raw(),
739 mData->m_strConfigFileFull.c_str(),
740 mData->mUuid.toString().c_str(),
741 mParent->i_settingsFilePath().c_str());
742
743 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
744 NULL /* const Guid *puuidRegistry */);
745 if (FAILED(rc)) throw rc;
746 }
747 catch (HRESULT err)
748 {
749 /* we assume that error info is set by the thrower */
750 rc = err;
751 }
752 catch (...)
753 {
754 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
755 }
756
757 /* Restore the registered flag (even on failure) */
758 mData->mRegistered = TRUE;
759 }
760
761 if (SUCCEEDED(rc))
762 {
763 /* Set mAccessible to TRUE only if we successfully locked and loaded
764 * the settings file */
765 mData->mAccessible = TRUE;
766
767 /* commit all changes made during loading the settings file */
768 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
769 /// @todo r=klaus for some reason the settings loading logic backs up
770 // the settings, and therefore a commit is needed. Should probably be changed.
771 }
772 else
773 {
774 /* If the machine is registered, then, instead of returning a
775 * failure, we mark it as inaccessible and set the result to
776 * success to give it a try later */
777
778 /* fetch the current error info */
779 mData->mAccessError = com::ErrorInfo();
780 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
781
782 /* rollback all changes */
783 i_rollback(false /* aNotify */);
784
785 // uninit media from this machine's media registry, or else
786 // reloading the settings will fail
787 mParent->i_unregisterMachineMedia(i_getId());
788
789 /* uninitialize the common part to make sure all data is reset to
790 * default (null) values */
791 uninitDataAndChildObjects();
792
793 rc = S_OK;
794 }
795
796 return rc;
797}
798
799/**
800 * Uninitializes the instance.
801 * Called either from FinalRelease() or by the parent when it gets destroyed.
802 *
803 * @note The caller of this method must make sure that this object
804 * a) doesn't have active callers on the current thread and b) is not locked
805 * by the current thread; otherwise uninit() will hang either a) due to
806 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
807 * a dead-lock caused by this thread waiting for all callers on the other
808 * threads are done but preventing them from doing so by holding a lock.
809 */
810void Machine::uninit()
811{
812 LogFlowThisFuncEnter();
813
814 Assert(!isWriteLockOnCurrentThread());
815
816 Assert(!uRegistryNeedsSaving);
817 if (uRegistryNeedsSaving)
818 {
819 AutoCaller autoCaller(this);
820 if (SUCCEEDED(autoCaller.rc()))
821 {
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823 i_saveSettings(NULL, Machine::SaveS_Force);
824 }
825 }
826
827 /* Enclose the state transition Ready->InUninit->NotReady */
828 AutoUninitSpan autoUninitSpan(this);
829 if (autoUninitSpan.uninitDone())
830 return;
831
832 Assert(!i_isSnapshotMachine());
833 Assert(!i_isSessionMachine());
834 Assert(!!mData);
835
836 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
837 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
838
839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
840
841 if (!mData->mSession.mMachine.isNull())
842 {
843 /* Theoretically, this can only happen if the VirtualBox server has been
844 * terminated while there were clients running that owned open direct
845 * sessions. Since in this case we are definitely called by
846 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
847 * won't happen on the client watcher thread (because it has a
848 * VirtualBox caller for the duration of the
849 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
850 * cannot happen until the VirtualBox caller is released). This is
851 * important, because SessionMachine::uninit() cannot correctly operate
852 * after we return from this method (it expects the Machine instance is
853 * still valid). We'll call it ourselves below.
854 */
855 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
856 (SessionMachine*)mData->mSession.mMachine));
857
858 if (Global::IsOnlineOrTransient(mData->mMachineState))
859 {
860 Log1WarningThisFunc(("Setting state to Aborted!\n"));
861 /* set machine state using SessionMachine reimplementation */
862 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
863 }
864
865 /*
866 * Uninitialize SessionMachine using public uninit() to indicate
867 * an unexpected uninitialization.
868 */
869 mData->mSession.mMachine->uninit();
870 /* SessionMachine::uninit() must set mSession.mMachine to null */
871 Assert(mData->mSession.mMachine.isNull());
872 }
873
874 // uninit media from this machine's media registry, if they're still there
875 Guid uuidMachine(i_getId());
876
877 /* the lock is no more necessary (SessionMachine is uninitialized) */
878 alock.release();
879
880 /* XXX This will fail with
881 * "cannot be closed because it is still attached to 1 virtual machines"
882 * because at this point we did not call uninitDataAndChildObjects() yet
883 * and therefore also removeBackReference() for all these mediums was not called! */
884
885 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
886 mParent->i_unregisterMachineMedia(uuidMachine);
887
888 // has machine been modified?
889 if (mData->flModifications)
890 {
891 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
892 i_rollback(false /* aNotify */);
893 }
894
895 if (mData->mAccessible)
896 uninitDataAndChildObjects();
897
898 /* free the essential data structure last */
899 mData.free();
900
901 LogFlowThisFuncLeave();
902}
903
904// Wrapped IMachine properties
905/////////////////////////////////////////////////////////////////////////////
906HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
907{
908 /* mParent is constant during life time, no need to lock */
909 ComObjPtr<VirtualBox> pVirtualBox(mParent);
910 aParent = pVirtualBox;
911
912 return S_OK;
913}
914
915
916HRESULT Machine::getAccessible(BOOL *aAccessible)
917{
918 /* In some cases (medium registry related), it is necessary to be able to
919 * go through the list of all machines. Happens when an inaccessible VM
920 * has a sensible medium registry. */
921 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
923
924 HRESULT rc = S_OK;
925
926 if (!mData->mAccessible)
927 {
928 /* try to initialize the VM once more if not accessible */
929
930 AutoReinitSpan autoReinitSpan(this);
931 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
932
933#ifdef DEBUG
934 LogFlowThisFunc(("Dumping media backreferences\n"));
935 mParent->i_dumpAllBackRefs();
936#endif
937
938 if (mData->pMachineConfigFile)
939 {
940 // reset the XML file to force loadSettings() (called from i_registeredInit())
941 // to parse it again; the file might have changed
942 delete mData->pMachineConfigFile;
943 mData->pMachineConfigFile = NULL;
944 }
945
946 rc = i_registeredInit();
947
948 if (SUCCEEDED(rc) && mData->mAccessible)
949 {
950 autoReinitSpan.setSucceeded();
951
952 /* make sure interesting parties will notice the accessibility
953 * state change */
954 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
955 mParent->i_onMachineDataChange(mData->mUuid);
956 }
957 }
958
959 if (SUCCEEDED(rc))
960 *aAccessible = mData->mAccessible;
961
962 LogFlowThisFuncLeave();
963
964 return rc;
965}
966
967HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
968{
969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
970
971 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
972 {
973 /* return shortly */
974 aAccessError = NULL;
975 return S_OK;
976 }
977
978 HRESULT rc = S_OK;
979
980 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
981 rc = errorInfo.createObject();
982 if (SUCCEEDED(rc))
983 {
984 errorInfo->init(mData->mAccessError.getResultCode(),
985 mData->mAccessError.getInterfaceID().ref(),
986 Utf8Str(mData->mAccessError.getComponent()).c_str(),
987 Utf8Str(mData->mAccessError.getText()));
988 aAccessError = errorInfo;
989 }
990
991 return rc;
992}
993
994HRESULT Machine::getName(com::Utf8Str &aName)
995{
996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
997
998 aName = mUserData->s.strName;
999
1000 return S_OK;
1001}
1002
1003HRESULT Machine::setName(const com::Utf8Str &aName)
1004{
1005 // prohibit setting a UUID only as the machine name, or else it can
1006 // never be found by findMachine()
1007 Guid test(aName);
1008
1009 if (test.isValid())
1010 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1011
1012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1015 if (FAILED(rc)) return rc;
1016
1017 i_setModified(IsModified_MachineData);
1018 mUserData.backup();
1019 mUserData->s.strName = aName;
1020
1021 return S_OK;
1022}
1023
1024HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1025{
1026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1027
1028 aDescription = mUserData->s.strDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1034{
1035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 // this can be done in principle in any state as it doesn't affect the VM
1038 // significantly, but play safe by not messing around while complex
1039 // activities are going on
1040 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1041 if (FAILED(rc)) return rc;
1042
1043 i_setModified(IsModified_MachineData);
1044 mUserData.backup();
1045 mUserData->s.strDescription = aDescription;
1046
1047 return S_OK;
1048}
1049
1050HRESULT Machine::getId(com::Guid &aId)
1051{
1052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1053
1054 aId = mData->mUuid;
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1060{
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062 aGroups.resize(mUserData->s.llGroups.size());
1063 size_t i = 0;
1064 for (StringsList::const_iterator
1065 it = mUserData->s.llGroups.begin();
1066 it != mUserData->s.llGroups.end();
1067 ++it, ++i)
1068 aGroups[i] = (*it);
1069
1070 return S_OK;
1071}
1072
1073HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1074{
1075 StringsList llGroups;
1076 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1077 if (FAILED(rc))
1078 return rc;
1079
1080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 rc = i_checkStateDependency(MutableOrSavedStateDep);
1083 if (FAILED(rc)) return rc;
1084
1085 i_setModified(IsModified_MachineData);
1086 mUserData.backup();
1087 mUserData->s.llGroups = llGroups;
1088
1089 return S_OK;
1090}
1091
1092HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1093{
1094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1095
1096 aOSTypeId = mUserData->s.strOsType;
1097
1098 return S_OK;
1099}
1100
1101HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1102{
1103 /* look up the object by Id to check it is valid */
1104 ComObjPtr<GuestOSType> pGuestOSType;
1105 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1106
1107 /* when setting, always use the "etalon" value for consistency -- lookup
1108 * by ID is case-insensitive and the input value may have different case */
1109 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1110
1111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 HRESULT rc = i_checkStateDependency(MutableStateDep);
1114 if (FAILED(rc)) return rc;
1115
1116 i_setModified(IsModified_MachineData);
1117 mUserData.backup();
1118 mUserData->s.strOsType = osTypeId;
1119
1120 return S_OK;
1121}
1122
1123HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1124{
1125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1126
1127 *aFirmwareType = mHWData->mFirmwareType;
1128
1129 return S_OK;
1130}
1131
1132HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1133{
1134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 HRESULT rc = i_checkStateDependency(MutableStateDep);
1137 if (FAILED(rc)) return rc;
1138
1139 i_setModified(IsModified_MachineData);
1140 mHWData.backup();
1141 mHWData->mFirmwareType = aFirmwareType;
1142 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1143 alock.release();
1144
1145 mBIOSSettings->i_updateNonVolatileStorageFile(strNVRAM);
1146
1147 return S_OK;
1148}
1149
1150HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1151{
1152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1153
1154 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1155
1156 return S_OK;
1157}
1158
1159HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1160{
1161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1162
1163 HRESULT rc = i_checkStateDependency(MutableStateDep);
1164 if (FAILED(rc)) return rc;
1165
1166 i_setModified(IsModified_MachineData);
1167 mHWData.backup();
1168 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1169
1170 return S_OK;
1171}
1172
1173HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1174{
1175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1176
1177 *aPointingHIDType = mHWData->mPointingHIDType;
1178
1179 return S_OK;
1180}
1181
1182HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1183{
1184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1185
1186 HRESULT rc = i_checkStateDependency(MutableStateDep);
1187 if (FAILED(rc)) return rc;
1188
1189 i_setModified(IsModified_MachineData);
1190 mHWData.backup();
1191 mHWData->mPointingHIDType = aPointingHIDType;
1192
1193 return S_OK;
1194}
1195
1196HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1197{
1198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1199
1200 *aChipsetType = mHWData->mChipsetType;
1201
1202 return S_OK;
1203}
1204
1205HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1206{
1207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1208
1209 HRESULT rc = i_checkStateDependency(MutableStateDep);
1210 if (FAILED(rc)) return rc;
1211
1212 if (aChipsetType != mHWData->mChipsetType)
1213 {
1214 i_setModified(IsModified_MachineData);
1215 mHWData.backup();
1216 mHWData->mChipsetType = aChipsetType;
1217
1218 // Resize network adapter array, to be finalized on commit/rollback.
1219 // We must not throw away entries yet, otherwise settings are lost
1220 // without a way to roll back.
1221 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1222 size_t oldCount = mNetworkAdapters.size();
1223 if (newCount > oldCount)
1224 {
1225 mNetworkAdapters.resize(newCount);
1226 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1227 {
1228 unconst(mNetworkAdapters[slot]).createObject();
1229 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1230 }
1231 }
1232 }
1233
1234 return S_OK;
1235}
1236
1237HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1238{
1239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 aParavirtDebug = mHWData->mParavirtDebug;
1242 return S_OK;
1243}
1244
1245HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1246{
1247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 HRESULT rc = i_checkStateDependency(MutableStateDep);
1250 if (FAILED(rc)) return rc;
1251
1252 /** @todo Parse/validate options? */
1253 if (aParavirtDebug != mHWData->mParavirtDebug)
1254 {
1255 i_setModified(IsModified_MachineData);
1256 mHWData.backup();
1257 mHWData->mParavirtDebug = aParavirtDebug;
1258 }
1259
1260 return S_OK;
1261}
1262
1263HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1264{
1265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1266
1267 *aParavirtProvider = mHWData->mParavirtProvider;
1268
1269 return S_OK;
1270}
1271
1272HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1273{
1274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 HRESULT rc = i_checkStateDependency(MutableStateDep);
1277 if (FAILED(rc)) return rc;
1278
1279 if (aParavirtProvider != mHWData->mParavirtProvider)
1280 {
1281 i_setModified(IsModified_MachineData);
1282 mHWData.backup();
1283 mHWData->mParavirtProvider = aParavirtProvider;
1284 }
1285
1286 return S_OK;
1287}
1288
1289HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1290{
1291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1292
1293 *aParavirtProvider = mHWData->mParavirtProvider;
1294 switch (mHWData->mParavirtProvider)
1295 {
1296 case ParavirtProvider_None:
1297 case ParavirtProvider_HyperV:
1298 case ParavirtProvider_KVM:
1299 case ParavirtProvider_Minimal:
1300 break;
1301
1302 /* Resolve dynamic provider types to the effective types. */
1303 default:
1304 {
1305 ComObjPtr<GuestOSType> pGuestOSType;
1306 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1307 pGuestOSType);
1308 if (FAILED(hrc2) || pGuestOSType.isNull())
1309 {
1310 *aParavirtProvider = ParavirtProvider_None;
1311 break;
1312 }
1313
1314 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1315 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1316
1317 switch (mHWData->mParavirtProvider)
1318 {
1319 case ParavirtProvider_Legacy:
1320 {
1321 if (fOsXGuest)
1322 *aParavirtProvider = ParavirtProvider_Minimal;
1323 else
1324 *aParavirtProvider = ParavirtProvider_None;
1325 break;
1326 }
1327
1328 case ParavirtProvider_Default:
1329 {
1330 if (fOsXGuest)
1331 *aParavirtProvider = ParavirtProvider_Minimal;
1332 else if ( mUserData->s.strOsType == "Windows10"
1333 || mUserData->s.strOsType == "Windows10_64"
1334 || mUserData->s.strOsType == "Windows81"
1335 || mUserData->s.strOsType == "Windows81_64"
1336 || mUserData->s.strOsType == "Windows8"
1337 || mUserData->s.strOsType == "Windows8_64"
1338 || mUserData->s.strOsType == "Windows7"
1339 || mUserData->s.strOsType == "Windows7_64"
1340 || mUserData->s.strOsType == "WindowsVista"
1341 || mUserData->s.strOsType == "WindowsVista_64"
1342 || mUserData->s.strOsType == "Windows2012"
1343 || mUserData->s.strOsType == "Windows2012_64"
1344 || mUserData->s.strOsType == "Windows2008"
1345 || mUserData->s.strOsType == "Windows2008_64")
1346 {
1347 *aParavirtProvider = ParavirtProvider_HyperV;
1348 }
1349 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1350 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1351 || mUserData->s.strOsType == "Linux"
1352 || mUserData->s.strOsType == "Linux_64"
1353 || mUserData->s.strOsType == "ArchLinux"
1354 || mUserData->s.strOsType == "ArchLinux_64"
1355 || mUserData->s.strOsType == "Debian"
1356 || mUserData->s.strOsType == "Debian_64"
1357 || mUserData->s.strOsType == "Fedora"
1358 || mUserData->s.strOsType == "Fedora_64"
1359 || mUserData->s.strOsType == "Gentoo"
1360 || mUserData->s.strOsType == "Gentoo_64"
1361 || mUserData->s.strOsType == "Mandriva"
1362 || mUserData->s.strOsType == "Mandriva_64"
1363 || mUserData->s.strOsType == "OpenSUSE"
1364 || mUserData->s.strOsType == "OpenSUSE_64"
1365 || mUserData->s.strOsType == "Oracle"
1366 || mUserData->s.strOsType == "Oracle_64"
1367 || mUserData->s.strOsType == "RedHat"
1368 || mUserData->s.strOsType == "RedHat_64"
1369 || mUserData->s.strOsType == "Turbolinux"
1370 || mUserData->s.strOsType == "Turbolinux_64"
1371 || mUserData->s.strOsType == "Ubuntu"
1372 || mUserData->s.strOsType == "Ubuntu_64"
1373 || mUserData->s.strOsType == "Xandros"
1374 || mUserData->s.strOsType == "Xandros_64")
1375 {
1376 *aParavirtProvider = ParavirtProvider_KVM;
1377 }
1378 else
1379 *aParavirtProvider = ParavirtProvider_None;
1380 break;
1381 }
1382
1383 default: AssertFailedBreak(); /* Shut up MSC. */
1384 }
1385 break;
1386 }
1387 }
1388
1389 Assert( *aParavirtProvider == ParavirtProvider_None
1390 || *aParavirtProvider == ParavirtProvider_Minimal
1391 || *aParavirtProvider == ParavirtProvider_HyperV
1392 || *aParavirtProvider == ParavirtProvider_KVM);
1393 return S_OK;
1394}
1395
1396HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1397{
1398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1399
1400 aHardwareVersion = mHWData->mHWVersion;
1401
1402 return S_OK;
1403}
1404
1405HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1406{
1407 /* check known version */
1408 Utf8Str hwVersion = aHardwareVersion;
1409 if ( hwVersion.compare("1") != 0
1410 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1411 return setError(E_INVALIDARG,
1412 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1413
1414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 HRESULT rc = i_checkStateDependency(MutableStateDep);
1417 if (FAILED(rc)) return rc;
1418
1419 i_setModified(IsModified_MachineData);
1420 mHWData.backup();
1421 mHWData->mHWVersion = aHardwareVersion;
1422
1423 return S_OK;
1424}
1425
1426HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1427{
1428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 if (!mHWData->mHardwareUUID.isZero())
1431 aHardwareUUID = mHWData->mHardwareUUID;
1432 else
1433 aHardwareUUID = mData->mUuid;
1434
1435 return S_OK;
1436}
1437
1438HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1439{
1440 if (!aHardwareUUID.isValid())
1441 return E_INVALIDARG;
1442
1443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1444
1445 HRESULT rc = i_checkStateDependency(MutableStateDep);
1446 if (FAILED(rc)) return rc;
1447
1448 i_setModified(IsModified_MachineData);
1449 mHWData.backup();
1450 if (aHardwareUUID == mData->mUuid)
1451 mHWData->mHardwareUUID.clear();
1452 else
1453 mHWData->mHardwareUUID = aHardwareUUID;
1454
1455 return S_OK;
1456}
1457
1458HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1459{
1460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 *aMemorySize = mHWData->mMemorySize;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::setMemorySize(ULONG aMemorySize)
1468{
1469 /* check RAM limits */
1470 if ( aMemorySize < MM_RAM_MIN_IN_MB
1471 || aMemorySize > MM_RAM_MAX_IN_MB
1472 )
1473 return setError(E_INVALIDARG,
1474 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1475 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1476
1477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1478
1479 HRESULT rc = i_checkStateDependency(MutableStateDep);
1480 if (FAILED(rc)) return rc;
1481
1482 i_setModified(IsModified_MachineData);
1483 mHWData.backup();
1484 mHWData->mMemorySize = aMemorySize;
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1490{
1491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 *aCPUCount = mHWData->mCPUCount;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::setCPUCount(ULONG aCPUCount)
1499{
1500 /* check CPU limits */
1501 if ( aCPUCount < SchemaDefs::MinCPUCount
1502 || aCPUCount > SchemaDefs::MaxCPUCount
1503 )
1504 return setError(E_INVALIDARG,
1505 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1506 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1507
1508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1509
1510 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1511 if (mHWData->mCPUHotPlugEnabled)
1512 {
1513 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1514 {
1515 if (mHWData->mCPUAttached[idx])
1516 return setError(E_INVALIDARG,
1517 tr("There is still a CPU attached to socket %lu."
1518 "Detach the CPU before removing the socket"),
1519 aCPUCount, idx+1);
1520 }
1521 }
1522
1523 HRESULT rc = i_checkStateDependency(MutableStateDep);
1524 if (FAILED(rc)) return rc;
1525
1526 i_setModified(IsModified_MachineData);
1527 mHWData.backup();
1528 mHWData->mCPUCount = aCPUCount;
1529
1530 return S_OK;
1531}
1532
1533HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1534{
1535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1536
1537 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1538
1539 return S_OK;
1540}
1541
1542HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1543{
1544 HRESULT rc = S_OK;
1545
1546 /* check throttle limits */
1547 if ( aCPUExecutionCap < 1
1548 || aCPUExecutionCap > 100
1549 )
1550 return setError(E_INVALIDARG,
1551 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1552 aCPUExecutionCap, 1, 100);
1553
1554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 alock.release();
1557 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1558 alock.acquire();
1559 if (FAILED(rc)) return rc;
1560
1561 i_setModified(IsModified_MachineData);
1562 mHWData.backup();
1563 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1564
1565 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1566 if (Global::IsOnline(mData->mMachineState))
1567 i_saveSettings(NULL);
1568
1569 return S_OK;
1570}
1571
1572HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1573{
1574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1575
1576 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1577
1578 return S_OK;
1579}
1580
1581HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1582{
1583 HRESULT rc = S_OK;
1584
1585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1586
1587 rc = i_checkStateDependency(MutableStateDep);
1588 if (FAILED(rc)) return rc;
1589
1590 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1591 {
1592 if (aCPUHotPlugEnabled)
1593 {
1594 i_setModified(IsModified_MachineData);
1595 mHWData.backup();
1596
1597 /* Add the amount of CPUs currently attached */
1598 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1599 mHWData->mCPUAttached[i] = true;
1600 }
1601 else
1602 {
1603 /*
1604 * We can disable hotplug only if the amount of maximum CPUs is equal
1605 * to the amount of attached CPUs
1606 */
1607 unsigned cCpusAttached = 0;
1608 unsigned iHighestId = 0;
1609
1610 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1611 {
1612 if (mHWData->mCPUAttached[i])
1613 {
1614 cCpusAttached++;
1615 iHighestId = i;
1616 }
1617 }
1618
1619 if ( (cCpusAttached != mHWData->mCPUCount)
1620 || (iHighestId >= mHWData->mCPUCount))
1621 return setError(E_INVALIDARG,
1622 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1623
1624 i_setModified(IsModified_MachineData);
1625 mHWData.backup();
1626 }
1627 }
1628
1629 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1630
1631 return rc;
1632}
1633
1634HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1635{
1636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1637
1638 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1639
1640 return S_OK;
1641}
1642
1643HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1644{
1645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1646
1647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1648 if (SUCCEEDED(hrc))
1649 {
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1653 }
1654 return hrc;
1655}
1656
1657HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1658{
1659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1660 aCPUProfile = mHWData->mCpuProfile;
1661 return S_OK;
1662}
1663
1664HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1665{
1666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1667 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1668 if (SUCCEEDED(hrc))
1669 {
1670 i_setModified(IsModified_MachineData);
1671 mHWData.backup();
1672 /* Empty equals 'host'. */
1673 if (aCPUProfile.isNotEmpty())
1674 mHWData->mCpuProfile = aCPUProfile;
1675 else
1676 mHWData->mCpuProfile = "host";
1677 }
1678 return hrc;
1679}
1680
1681HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1682{
1683#ifdef VBOX_WITH_USB_CARDREADER
1684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1685
1686 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1687
1688 return S_OK;
1689#else
1690 NOREF(aEmulatedUSBCardReaderEnabled);
1691 return E_NOTIMPL;
1692#endif
1693}
1694
1695HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1696{
1697#ifdef VBOX_WITH_USB_CARDREADER
1698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1701 if (FAILED(rc)) return rc;
1702
1703 i_setModified(IsModified_MachineData);
1704 mHWData.backup();
1705 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1706
1707 return S_OK;
1708#else
1709 NOREF(aEmulatedUSBCardReaderEnabled);
1710 return E_NOTIMPL;
1711#endif
1712}
1713
1714HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1715{
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 *aHPETEnabled = mHWData->mHPETEnabled;
1719
1720 return S_OK;
1721}
1722
1723HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1724{
1725 HRESULT rc = S_OK;
1726
1727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1728
1729 rc = i_checkStateDependency(MutableStateDep);
1730 if (FAILED(rc)) return rc;
1731
1732 i_setModified(IsModified_MachineData);
1733 mHWData.backup();
1734
1735 mHWData->mHPETEnabled = aHPETEnabled;
1736
1737 return rc;
1738}
1739
1740/** @todo this method should not be public */
1741HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1742{
1743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1744
1745 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1746
1747 return S_OK;
1748}
1749
1750/**
1751 * Set the memory balloon size.
1752 *
1753 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1754 * we have to make sure that we never call IGuest from here.
1755 */
1756HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1757{
1758 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1759#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1760 /* check limits */
1761 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1762 return setError(E_INVALIDARG,
1763 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1764 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1765
1766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1767
1768 i_setModified(IsModified_MachineData);
1769 mHWData.backup();
1770 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1771
1772 return S_OK;
1773#else
1774 NOREF(aMemoryBalloonSize);
1775 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1776#endif
1777}
1778
1779HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1780{
1781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1782
1783 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1784 return S_OK;
1785}
1786
1787HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1788{
1789#ifdef VBOX_WITH_PAGE_SHARING
1790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1791
1792 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1793 i_setModified(IsModified_MachineData);
1794 mHWData.backup();
1795 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1796 return S_OK;
1797#else
1798 NOREF(aPageFusionEnabled);
1799 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1800#endif
1801}
1802
1803HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1804{
1805 /* mBIOSSettings is constant during life time, no need to lock */
1806 aBIOSSettings = mBIOSSettings;
1807
1808 return S_OK;
1809}
1810
1811HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1812{
1813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 aRecordingSettings = mRecordingSettings;
1816
1817 return S_OK;
1818}
1819
1820HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 aGraphicsAdapter = mGraphicsAdapter;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832
1833 switch (aProperty)
1834 {
1835 case CPUPropertyType_PAE:
1836 *aValue = mHWData->mPAEEnabled;
1837 break;
1838
1839 case CPUPropertyType_LongMode:
1840 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1841 *aValue = TRUE;
1842 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1843 *aValue = FALSE;
1844#if HC_ARCH_BITS == 64
1845 else
1846 *aValue = TRUE;
1847#else
1848 else
1849 {
1850 *aValue = FALSE;
1851
1852 ComObjPtr<GuestOSType> pGuestOSType;
1853 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1854 pGuestOSType);
1855 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1856 {
1857 if (pGuestOSType->i_is64Bit())
1858 {
1859 ComObjPtr<Host> pHost = mParent->i_host();
1860 alock.release();
1861
1862 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1863 if (FAILED(hrc2))
1864 *aValue = FALSE;
1865 }
1866 }
1867 }
1868#endif
1869 break;
1870
1871 case CPUPropertyType_TripleFaultReset:
1872 *aValue = mHWData->mTripleFaultReset;
1873 break;
1874
1875 case CPUPropertyType_APIC:
1876 *aValue = mHWData->mAPIC;
1877 break;
1878
1879 case CPUPropertyType_X2APIC:
1880 *aValue = mHWData->mX2APIC;
1881 break;
1882
1883 case CPUPropertyType_IBPBOnVMExit:
1884 *aValue = mHWData->mIBPBOnVMExit;
1885 break;
1886
1887 case CPUPropertyType_IBPBOnVMEntry:
1888 *aValue = mHWData->mIBPBOnVMEntry;
1889 break;
1890
1891 case CPUPropertyType_SpecCtrl:
1892 *aValue = mHWData->mSpecCtrl;
1893 break;
1894
1895 case CPUPropertyType_SpecCtrlByHost:
1896 *aValue = mHWData->mSpecCtrlByHost;
1897 break;
1898
1899 case CPUPropertyType_HWVirt:
1900 *aValue = mHWData->mNestedHWVirt;
1901 break;
1902
1903 case CPUPropertyType_L1DFlushOnEMTScheduling:
1904 *aValue = mHWData->mL1DFlushOnSched;
1905 break;
1906
1907 case CPUPropertyType_L1DFlushOnVMEntry:
1908 *aValue = mHWData->mL1DFlushOnVMEntry;
1909 break;
1910
1911 case CPUPropertyType_MDSClearOnEMTScheduling:
1912 *aValue = mHWData->mMDSClearOnSched;
1913 break;
1914
1915 case CPUPropertyType_MDSClearOnVMEntry:
1916 *aValue = mHWData->mMDSClearOnVMEntry;
1917 break;
1918
1919 default:
1920 return E_INVALIDARG;
1921 }
1922 return S_OK;
1923}
1924
1925HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1926{
1927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 HRESULT rc = i_checkStateDependency(MutableStateDep);
1930 if (FAILED(rc)) return rc;
1931
1932 switch (aProperty)
1933 {
1934 case CPUPropertyType_PAE:
1935 i_setModified(IsModified_MachineData);
1936 mHWData.backup();
1937 mHWData->mPAEEnabled = !!aValue;
1938 break;
1939
1940 case CPUPropertyType_LongMode:
1941 i_setModified(IsModified_MachineData);
1942 mHWData.backup();
1943 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1944 break;
1945
1946 case CPUPropertyType_TripleFaultReset:
1947 i_setModified(IsModified_MachineData);
1948 mHWData.backup();
1949 mHWData->mTripleFaultReset = !!aValue;
1950 break;
1951
1952 case CPUPropertyType_APIC:
1953 if (mHWData->mX2APIC)
1954 aValue = TRUE;
1955 i_setModified(IsModified_MachineData);
1956 mHWData.backup();
1957 mHWData->mAPIC = !!aValue;
1958 break;
1959
1960 case CPUPropertyType_X2APIC:
1961 i_setModified(IsModified_MachineData);
1962 mHWData.backup();
1963 mHWData->mX2APIC = !!aValue;
1964 if (aValue)
1965 mHWData->mAPIC = !!aValue;
1966 break;
1967
1968 case CPUPropertyType_IBPBOnVMExit:
1969 i_setModified(IsModified_MachineData);
1970 mHWData.backup();
1971 mHWData->mIBPBOnVMExit = !!aValue;
1972 break;
1973
1974 case CPUPropertyType_IBPBOnVMEntry:
1975 i_setModified(IsModified_MachineData);
1976 mHWData.backup();
1977 mHWData->mIBPBOnVMEntry = !!aValue;
1978 break;
1979
1980 case CPUPropertyType_SpecCtrl:
1981 i_setModified(IsModified_MachineData);
1982 mHWData.backup();
1983 mHWData->mSpecCtrl = !!aValue;
1984 break;
1985
1986 case CPUPropertyType_SpecCtrlByHost:
1987 i_setModified(IsModified_MachineData);
1988 mHWData.backup();
1989 mHWData->mSpecCtrlByHost = !!aValue;
1990 break;
1991
1992 case CPUPropertyType_HWVirt:
1993 i_setModified(IsModified_MachineData);
1994 mHWData.backup();
1995 mHWData->mNestedHWVirt = !!aValue;
1996 break;
1997
1998 case CPUPropertyType_L1DFlushOnEMTScheduling:
1999 i_setModified(IsModified_MachineData);
2000 mHWData.backup();
2001 mHWData->mL1DFlushOnSched = !!aValue;
2002 break;
2003
2004 case CPUPropertyType_L1DFlushOnVMEntry:
2005 i_setModified(IsModified_MachineData);
2006 mHWData.backup();
2007 mHWData->mL1DFlushOnVMEntry = !!aValue;
2008 break;
2009
2010 case CPUPropertyType_MDSClearOnEMTScheduling:
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mMDSClearOnSched = !!aValue;
2014 break;
2015
2016 case CPUPropertyType_MDSClearOnVMEntry:
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mMDSClearOnVMEntry = !!aValue;
2020 break;
2021
2022 default:
2023 return E_INVALIDARG;
2024 }
2025 return S_OK;
2026}
2027
2028HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2029 ULONG *aValEcx, ULONG *aValEdx)
2030{
2031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2032 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2033 {
2034 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2035 it != mHWData->mCpuIdLeafList.end();
2036 ++it)
2037 {
2038 if (aOrdinal == 0)
2039 {
2040 const settings::CpuIdLeaf &rLeaf= *it;
2041 *aIdx = rLeaf.idx;
2042 *aSubIdx = rLeaf.idxSub;
2043 *aValEax = rLeaf.uEax;
2044 *aValEbx = rLeaf.uEbx;
2045 *aValEcx = rLeaf.uEcx;
2046 *aValEdx = rLeaf.uEdx;
2047 return S_OK;
2048 }
2049 aOrdinal--;
2050 }
2051 }
2052 return E_INVALIDARG;
2053}
2054
2055HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2056{
2057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 /*
2060 * Search the list.
2061 */
2062 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2063 {
2064 const settings::CpuIdLeaf &rLeaf= *it;
2065 if ( rLeaf.idx == aIdx
2066 && ( aSubIdx == UINT32_MAX
2067 || rLeaf.idxSub == aSubIdx) )
2068 {
2069 *aValEax = rLeaf.uEax;
2070 *aValEbx = rLeaf.uEbx;
2071 *aValEcx = rLeaf.uEcx;
2072 *aValEdx = rLeaf.uEdx;
2073 return S_OK;
2074 }
2075 }
2076
2077 return E_INVALIDARG;
2078}
2079
2080
2081HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2082{
2083 /*
2084 * Validate input before taking locks and checking state.
2085 */
2086 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2087 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2088 if ( aIdx >= UINT32_C(0x20)
2089 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2090 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2091 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2092
2093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2094 HRESULT rc = i_checkStateDependency(MutableStateDep);
2095 if (FAILED(rc)) return rc;
2096
2097 /*
2098 * Impose a maximum number of leaves.
2099 */
2100 if (mHWData->mCpuIdLeafList.size() > 256)
2101 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2102
2103 /*
2104 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2105 */
2106 i_setModified(IsModified_MachineData);
2107 mHWData.backup();
2108
2109 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2110 {
2111 settings::CpuIdLeaf &rLeaf= *it;
2112 if ( rLeaf.idx == aIdx
2113 && ( aSubIdx == UINT32_MAX
2114 || rLeaf.idxSub == aSubIdx) )
2115 it = mHWData->mCpuIdLeafList.erase(it);
2116 else
2117 ++it;
2118 }
2119
2120 settings::CpuIdLeaf NewLeaf;
2121 NewLeaf.idx = aIdx;
2122 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2123 NewLeaf.uEax = aValEax;
2124 NewLeaf.uEbx = aValEbx;
2125 NewLeaf.uEcx = aValEcx;
2126 NewLeaf.uEdx = aValEdx;
2127 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2128 return S_OK;
2129}
2130
2131HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2132{
2133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 HRESULT rc = i_checkStateDependency(MutableStateDep);
2136 if (FAILED(rc)) return rc;
2137
2138 /*
2139 * Do the removal.
2140 */
2141 bool fModified = mHWData.isBackedUp();
2142 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2143 {
2144 settings::CpuIdLeaf &rLeaf= *it;
2145 if ( rLeaf.idx == aIdx
2146 && ( aSubIdx == UINT32_MAX
2147 || rLeaf.idxSub == aSubIdx) )
2148 {
2149 if (!fModified)
2150 {
2151 fModified = true;
2152 i_setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 // Start from the beginning, since mHWData.backup() creates
2155 // a new list, causing iterator mixup. This makes sure that
2156 // the settings are not unnecessarily marked as modified,
2157 // at the price of extra list walking.
2158 it = mHWData->mCpuIdLeafList.begin();
2159 }
2160 else
2161 it = mHWData->mCpuIdLeafList.erase(it);
2162 }
2163 else
2164 ++it;
2165 }
2166
2167 return S_OK;
2168}
2169
2170HRESULT Machine::removeAllCPUIDLeaves()
2171{
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 HRESULT rc = i_checkStateDependency(MutableStateDep);
2175 if (FAILED(rc)) return rc;
2176
2177 if (mHWData->mCpuIdLeafList.size() > 0)
2178 {
2179 i_setModified(IsModified_MachineData);
2180 mHWData.backup();
2181
2182 mHWData->mCpuIdLeafList.clear();
2183 }
2184
2185 return S_OK;
2186}
2187HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2188{
2189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2190
2191 switch(aProperty)
2192 {
2193 case HWVirtExPropertyType_Enabled:
2194 *aValue = mHWData->mHWVirtExEnabled;
2195 break;
2196
2197 case HWVirtExPropertyType_VPID:
2198 *aValue = mHWData->mHWVirtExVPIDEnabled;
2199 break;
2200
2201 case HWVirtExPropertyType_NestedPaging:
2202 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2203 break;
2204
2205 case HWVirtExPropertyType_UnrestrictedExecution:
2206 *aValue = mHWData->mHWVirtExUXEnabled;
2207 break;
2208
2209 case HWVirtExPropertyType_LargePages:
2210 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2211#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2212 *aValue = FALSE;
2213#endif
2214 break;
2215
2216 case HWVirtExPropertyType_Force:
2217 *aValue = mHWData->mHWVirtExForceEnabled;
2218 break;
2219
2220 case HWVirtExPropertyType_UseNativeApi:
2221 *aValue = mHWData->mHWVirtExUseNativeApi;
2222 break;
2223
2224 default:
2225 return E_INVALIDARG;
2226 }
2227 return S_OK;
2228}
2229
2230HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2231{
2232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2233
2234 HRESULT rc = i_checkStateDependency(MutableStateDep);
2235 if (FAILED(rc)) return rc;
2236
2237 switch (aProperty)
2238 {
2239 case HWVirtExPropertyType_Enabled:
2240 i_setModified(IsModified_MachineData);
2241 mHWData.backup();
2242 mHWData->mHWVirtExEnabled = !!aValue;
2243 break;
2244
2245 case HWVirtExPropertyType_VPID:
2246 i_setModified(IsModified_MachineData);
2247 mHWData.backup();
2248 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2249 break;
2250
2251 case HWVirtExPropertyType_NestedPaging:
2252 i_setModified(IsModified_MachineData);
2253 mHWData.backup();
2254 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2255 break;
2256
2257 case HWVirtExPropertyType_UnrestrictedExecution:
2258 i_setModified(IsModified_MachineData);
2259 mHWData.backup();
2260 mHWData->mHWVirtExUXEnabled = !!aValue;
2261 break;
2262
2263 case HWVirtExPropertyType_LargePages:
2264 i_setModified(IsModified_MachineData);
2265 mHWData.backup();
2266 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2267 break;
2268
2269 case HWVirtExPropertyType_Force:
2270 i_setModified(IsModified_MachineData);
2271 mHWData.backup();
2272 mHWData->mHWVirtExForceEnabled = !!aValue;
2273 break;
2274
2275 case HWVirtExPropertyType_UseNativeApi:
2276 i_setModified(IsModified_MachineData);
2277 mHWData.backup();
2278 mHWData->mHWVirtExUseNativeApi = !!aValue;
2279 break;
2280
2281 default:
2282 return E_INVALIDARG;
2283 }
2284
2285 return S_OK;
2286}
2287
2288HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2289{
2290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2291
2292 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2293
2294 return S_OK;
2295}
2296
2297HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2298{
2299 /** @todo (r=dmik):
2300 * 1. Allow to change the name of the snapshot folder containing snapshots
2301 * 2. Rename the folder on disk instead of just changing the property
2302 * value (to be smart and not to leave garbage). Note that it cannot be
2303 * done here because the change may be rolled back. Thus, the right
2304 * place is #saveSettings().
2305 */
2306
2307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 HRESULT rc = i_checkStateDependency(MutableStateDep);
2310 if (FAILED(rc)) return rc;
2311
2312 if (!mData->mCurrentSnapshot.isNull())
2313 return setError(E_FAIL,
2314 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2315
2316 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2317
2318 if (strSnapshotFolder.isEmpty())
2319 strSnapshotFolder = "Snapshots";
2320 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2321 if (RT_FAILURE(vrc))
2322 return setErrorBoth(E_FAIL, vrc,
2323 tr("Invalid snapshot folder '%s' (%Rrc)"),
2324 strSnapshotFolder.c_str(), vrc);
2325
2326 i_setModified(IsModified_MachineData);
2327 mUserData.backup();
2328
2329 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2330
2331 return S_OK;
2332}
2333
2334HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2335{
2336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2337
2338 aMediumAttachments.resize(mMediumAttachments->size());
2339 size_t i = 0;
2340 for (MediumAttachmentList::const_iterator
2341 it = mMediumAttachments->begin();
2342 it != mMediumAttachments->end();
2343 ++it, ++i)
2344 aMediumAttachments[i] = *it;
2345
2346 return S_OK;
2347}
2348
2349HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2350{
2351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2352
2353 Assert(!!mVRDEServer);
2354
2355 aVRDEServer = mVRDEServer;
2356
2357 return S_OK;
2358}
2359
2360HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2361{
2362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 aAudioAdapter = mAudioAdapter;
2365
2366 return S_OK;
2367}
2368
2369HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2370{
2371#ifdef VBOX_WITH_VUSB
2372 clearError();
2373 MultiResult rc(S_OK);
2374
2375# ifdef VBOX_WITH_USB
2376 rc = mParent->i_host()->i_checkUSBProxyService();
2377 if (FAILED(rc)) return rc;
2378# endif
2379
2380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2381
2382 aUSBControllers.resize(mUSBControllers->size());
2383 size_t i = 0;
2384 for (USBControllerList::const_iterator
2385 it = mUSBControllers->begin();
2386 it != mUSBControllers->end();
2387 ++it, ++i)
2388 aUSBControllers[i] = *it;
2389
2390 return S_OK;
2391#else
2392 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2393 * extended error info to indicate that USB is simply not available
2394 * (w/o treating it as a failure), for example, as in OSE */
2395 NOREF(aUSBControllers);
2396 ReturnComNotImplemented();
2397#endif /* VBOX_WITH_VUSB */
2398}
2399
2400HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2401{
2402#ifdef VBOX_WITH_VUSB
2403 clearError();
2404 MultiResult rc(S_OK);
2405
2406# ifdef VBOX_WITH_USB
2407 rc = mParent->i_host()->i_checkUSBProxyService();
2408 if (FAILED(rc)) return rc;
2409# endif
2410
2411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2412
2413 aUSBDeviceFilters = mUSBDeviceFilters;
2414 return rc;
2415#else
2416 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2417 * extended error info to indicate that USB is simply not available
2418 * (w/o treating it as a failure), for example, as in OSE */
2419 NOREF(aUSBDeviceFilters);
2420 ReturnComNotImplemented();
2421#endif /* VBOX_WITH_VUSB */
2422}
2423
2424HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2425{
2426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 aSettingsFilePath = mData->m_strConfigFileFull;
2429
2430 return S_OK;
2431}
2432
2433HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2434{
2435 RT_NOREF(aSettingsFilePath);
2436 ReturnComNotImplemented();
2437}
2438
2439HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2440{
2441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2444 if (FAILED(rc)) return rc;
2445
2446 if (!mData->pMachineConfigFile->fileExists())
2447 // this is a new machine, and no config file exists yet:
2448 *aSettingsModified = TRUE;
2449 else
2450 *aSettingsModified = (mData->flModifications != 0);
2451
2452 return S_OK;
2453}
2454
2455HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2456{
2457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 *aSessionState = mData->mSession.mState;
2460
2461 return S_OK;
2462}
2463
2464HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2465{
2466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 aSessionName = mData->mSession.mName;
2469
2470 return S_OK;
2471}
2472
2473HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2474{
2475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2476
2477 *aSessionPID = mData->mSession.mPID;
2478
2479 return S_OK;
2480}
2481
2482HRESULT Machine::getState(MachineState_T *aState)
2483{
2484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2485
2486 *aState = mData->mMachineState;
2487 Assert(mData->mMachineState != MachineState_Null);
2488
2489 return S_OK;
2490}
2491
2492HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2493{
2494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2497
2498 return S_OK;
2499}
2500
2501HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2502{
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 aStateFilePath = mSSData->strStateFilePath;
2506
2507 return S_OK;
2508}
2509
2510HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2511{
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 i_getLogFolder(aLogFolder);
2515
2516 return S_OK;
2517}
2518
2519HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2520{
2521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2522
2523 aCurrentSnapshot = mData->mCurrentSnapshot;
2524
2525 return S_OK;
2526}
2527
2528HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2529{
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2533 ? 0
2534 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2535
2536 return S_OK;
2537}
2538
2539HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2540{
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 /* Note: for machines with no snapshots, we always return FALSE
2544 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2545 * reasons :) */
2546
2547 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2548 ? FALSE
2549 : mData->mCurrentStateModified;
2550
2551 return S_OK;
2552}
2553
2554HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2555{
2556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2557
2558 aSharedFolders.resize(mHWData->mSharedFolders.size());
2559 size_t i = 0;
2560 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2561 it = mHWData->mSharedFolders.begin();
2562 it != mHWData->mSharedFolders.end();
2563 ++it, ++i)
2564 aSharedFolders[i] = *it;
2565
2566 return S_OK;
2567}
2568
2569HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2570{
2571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2572
2573 *aClipboardMode = mHWData->mClipboardMode;
2574
2575 return S_OK;
2576}
2577
2578HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2579{
2580 HRESULT rc = S_OK;
2581
2582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2583
2584 alock.release();
2585 rc = i_onClipboardModeChange(aClipboardMode);
2586 alock.acquire();
2587 if (FAILED(rc)) return rc;
2588
2589 i_setModified(IsModified_MachineData);
2590 mHWData.backup();
2591 mHWData->mClipboardMode = aClipboardMode;
2592
2593 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2594 if (Global::IsOnline(mData->mMachineState))
2595 i_saveSettings(NULL);
2596
2597 return S_OK;
2598}
2599
2600HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2601{
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2605
2606 return S_OK;
2607}
2608
2609HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2610{
2611 HRESULT rc = S_OK;
2612
2613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 alock.release();
2616 rc = i_onClipboardFileTransferModeChange(aEnabled);
2617 alock.acquire();
2618 if (FAILED(rc)) return rc;
2619
2620 i_setModified(IsModified_MachineData);
2621 mHWData.backup();
2622 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2623
2624 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2625 if (Global::IsOnline(mData->mMachineState))
2626 i_saveSettings(NULL);
2627
2628 return S_OK;
2629}
2630
2631HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2632{
2633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 *aDnDMode = mHWData->mDnDMode;
2636
2637 return S_OK;
2638}
2639
2640HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2641{
2642 HRESULT rc = S_OK;
2643
2644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 alock.release();
2647 rc = i_onDnDModeChange(aDnDMode);
2648
2649 alock.acquire();
2650 if (FAILED(rc)) return rc;
2651
2652 i_setModified(IsModified_MachineData);
2653 mHWData.backup();
2654 mHWData->mDnDMode = aDnDMode;
2655
2656 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2657 if (Global::IsOnline(mData->mMachineState))
2658 i_saveSettings(NULL);
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2664{
2665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2666
2667 aStorageControllers.resize(mStorageControllers->size());
2668 size_t i = 0;
2669 for (StorageControllerList::const_iterator
2670 it = mStorageControllers->begin();
2671 it != mStorageControllers->end();
2672 ++it, ++i)
2673 aStorageControllers[i] = *it;
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2679{
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 *aEnabled = mUserData->s.fTeleporterEnabled;
2683
2684 return S_OK;
2685}
2686
2687HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2688{
2689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2690
2691 /* Only allow it to be set to true when PoweredOff or Aborted.
2692 (Clearing it is always permitted.) */
2693 if ( aTeleporterEnabled
2694 && mData->mRegistered
2695 && ( !i_isSessionMachine()
2696 || ( mData->mMachineState != MachineState_PoweredOff
2697 && mData->mMachineState != MachineState_Teleported
2698 && mData->mMachineState != MachineState_Aborted
2699 )
2700 )
2701 )
2702 return setError(VBOX_E_INVALID_VM_STATE,
2703 tr("The machine is not powered off (state is %s)"),
2704 Global::stringifyMachineState(mData->mMachineState));
2705
2706 i_setModified(IsModified_MachineData);
2707 mUserData.backup();
2708 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2709
2710 return S_OK;
2711}
2712
2713HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2714{
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2718
2719 return S_OK;
2720}
2721
2722HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2723{
2724 if (aTeleporterPort >= _64K)
2725 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2726
2727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2730 if (FAILED(rc)) return rc;
2731
2732 i_setModified(IsModified_MachineData);
2733 mUserData.backup();
2734 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2749{
2750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2753 if (FAILED(rc)) return rc;
2754
2755 i_setModified(IsModified_MachineData);
2756 mUserData.backup();
2757 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2771{
2772 /*
2773 * Hash the password first.
2774 */
2775 com::Utf8Str aT = aTeleporterPassword;
2776
2777 if (!aT.isEmpty())
2778 {
2779 if (VBoxIsPasswordHashed(&aT))
2780 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2781 VBoxHashPassword(&aT);
2782 }
2783
2784 /*
2785 * Do the update.
2786 */
2787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2788 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2789 if (SUCCEEDED(hrc))
2790 {
2791 i_setModified(IsModified_MachineData);
2792 mUserData.backup();
2793 mUserData->s.strTeleporterPassword = aT;
2794 }
2795
2796 return hrc;
2797}
2798
2799HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2800{
2801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2802
2803 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2809{
2810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 /* Only allow it to be set to true when PoweredOff or Aborted.
2813 (Clearing it is always permitted.) */
2814 if ( aRTCUseUTC
2815 && mData->mRegistered
2816 && ( !i_isSessionMachine()
2817 || ( mData->mMachineState != MachineState_PoweredOff
2818 && mData->mMachineState != MachineState_Teleported
2819 && mData->mMachineState != MachineState_Aborted
2820 )
2821 )
2822 )
2823 return setError(VBOX_E_INVALID_VM_STATE,
2824 tr("The machine is not powered off (state is %s)"),
2825 Global::stringifyMachineState(mData->mMachineState));
2826
2827 i_setModified(IsModified_MachineData);
2828 mUserData.backup();
2829 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2839
2840 return S_OK;
2841}
2842
2843HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2844{
2845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 HRESULT rc = i_checkStateDependency(MutableStateDep);
2848 if (FAILED(rc)) return rc;
2849
2850 i_setModified(IsModified_MachineData);
2851 mHWData.backup();
2852 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2853
2854 return S_OK;
2855}
2856
2857HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2858{
2859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 *aIOCacheSize = mHWData->mIOCacheSize;
2862
2863 return S_OK;
2864}
2865
2866HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2867{
2868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 HRESULT rc = i_checkStateDependency(MutableStateDep);
2871 if (FAILED(rc)) return rc;
2872
2873 i_setModified(IsModified_MachineData);
2874 mHWData.backup();
2875 mHWData->mIOCacheSize = aIOCacheSize;
2876
2877 return S_OK;
2878}
2879
2880
2881/**
2882 * @note Locks objects!
2883 */
2884HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2885 LockType_T aLockType)
2886{
2887 /* check the session state */
2888 SessionState_T state;
2889 HRESULT rc = aSession->COMGETTER(State)(&state);
2890 if (FAILED(rc)) return rc;
2891
2892 if (state != SessionState_Unlocked)
2893 return setError(VBOX_E_INVALID_OBJECT_STATE,
2894 tr("The given session is busy"));
2895
2896 // get the client's IInternalSessionControl interface
2897 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2898 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2899 E_INVALIDARG);
2900
2901 // session name (only used in some code paths)
2902 Utf8Str strSessionName;
2903
2904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 if (!mData->mRegistered)
2907 return setError(E_UNEXPECTED,
2908 tr("The machine '%s' is not registered"),
2909 mUserData->s.strName.c_str());
2910
2911 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2912
2913 SessionState_T oldState = mData->mSession.mState;
2914 /* Hack: in case the session is closing and there is a progress object
2915 * which allows waiting for the session to be closed, take the opportunity
2916 * and do a limited wait (max. 1 second). This helps a lot when the system
2917 * is busy and thus session closing can take a little while. */
2918 if ( mData->mSession.mState == SessionState_Unlocking
2919 && mData->mSession.mProgress)
2920 {
2921 alock.release();
2922 mData->mSession.mProgress->WaitForCompletion(1000);
2923 alock.acquire();
2924 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2925 }
2926
2927 // try again now
2928 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2929 // (i.e. session machine exists)
2930 && (aLockType == LockType_Shared) // caller wants a shared link to the
2931 // existing session that holds the write lock:
2932 )
2933 {
2934 // OK, share the session... we are now dealing with three processes:
2935 // 1) VBoxSVC (where this code runs);
2936 // 2) process C: the caller's client process (who wants a shared session);
2937 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2938
2939 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2940 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2941 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2942 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2943 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2944
2945 /*
2946 * Release the lock before calling the client process. It's safe here
2947 * since the only thing to do after we get the lock again is to add
2948 * the remote control to the list (which doesn't directly influence
2949 * anything).
2950 */
2951 alock.release();
2952
2953 // get the console of the session holding the write lock (this is a remote call)
2954 ComPtr<IConsole> pConsoleW;
2955 if (mData->mSession.mLockType == LockType_VM)
2956 {
2957 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2958 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2959 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2960 if (FAILED(rc))
2961 // the failure may occur w/o any error info (from RPC), so provide one
2962 return setError(VBOX_E_VM_ERROR,
2963 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
2964 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2965 }
2966
2967 // share the session machine and W's console with the caller's session
2968 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2969 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2970 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
2971
2972 if (FAILED(rc))
2973 // the failure may occur w/o any error info (from RPC), so provide one
2974 return setError(VBOX_E_VM_ERROR,
2975 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
2976 alock.acquire();
2977
2978 // need to revalidate the state after acquiring the lock again
2979 if (mData->mSession.mState != SessionState_Locked)
2980 {
2981 pSessionControl->Uninitialize();
2982 return setError(VBOX_E_INVALID_SESSION_STATE,
2983 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2984 mUserData->s.strName.c_str());
2985 }
2986
2987 // add the caller's session to the list
2988 mData->mSession.mRemoteControls.push_back(pSessionControl);
2989 }
2990 else if ( mData->mSession.mState == SessionState_Locked
2991 || mData->mSession.mState == SessionState_Unlocking
2992 )
2993 {
2994 // sharing not permitted, or machine still unlocking:
2995 return setError(VBOX_E_INVALID_OBJECT_STATE,
2996 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2997 mUserData->s.strName.c_str());
2998 }
2999 else
3000 {
3001 // machine is not locked: then write-lock the machine (create the session machine)
3002
3003 // must not be busy
3004 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3005
3006 // get the caller's session PID
3007 RTPROCESS pid = NIL_RTPROCESS;
3008 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3009 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3010 Assert(pid != NIL_RTPROCESS);
3011
3012 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3013
3014 if (fLaunchingVMProcess)
3015 {
3016 if (mData->mSession.mPID == NIL_RTPROCESS)
3017 {
3018 // two or more clients racing for a lock, the one which set the
3019 // session state to Spawning will win, the others will get an
3020 // error as we can't decide here if waiting a little would help
3021 // (only for shared locks this would avoid an error)
3022 return setError(VBOX_E_INVALID_OBJECT_STATE,
3023 tr("The machine '%s' already has a lock request pending"),
3024 mUserData->s.strName.c_str());
3025 }
3026
3027 // this machine is awaiting for a spawning session to be opened:
3028 // then the calling process must be the one that got started by
3029 // LaunchVMProcess()
3030
3031 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3032 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3033
3034#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3035 /* Hardened windows builds spawns three processes when a VM is
3036 launched, the 3rd one is the one that will end up here. */
3037 RTPROCESS pidParent;
3038 int vrc = RTProcQueryParent(pid, &pidParent);
3039 if (RT_SUCCESS(vrc))
3040 vrc = RTProcQueryParent(pidParent, &pidParent);
3041 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3042 || vrc == VERR_ACCESS_DENIED)
3043 {
3044 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3045 mData->mSession.mPID = pid;
3046 }
3047#endif
3048
3049 if (mData->mSession.mPID != pid)
3050 return setError(E_ACCESSDENIED,
3051 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3052 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3053 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3054 }
3055
3056 // create the mutable SessionMachine from the current machine
3057 ComObjPtr<SessionMachine> sessionMachine;
3058 sessionMachine.createObject();
3059 rc = sessionMachine->init(this);
3060 AssertComRC(rc);
3061
3062 /* NOTE: doing return from this function after this point but
3063 * before the end is forbidden since it may call SessionMachine::uninit()
3064 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3065 * lock while still holding the Machine lock in alock so that a deadlock
3066 * is possible due to the wrong lock order. */
3067
3068 if (SUCCEEDED(rc))
3069 {
3070 /*
3071 * Set the session state to Spawning to protect against subsequent
3072 * attempts to open a session and to unregister the machine after
3073 * we release the lock.
3074 */
3075 SessionState_T origState = mData->mSession.mState;
3076 mData->mSession.mState = SessionState_Spawning;
3077
3078#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3079 /* Get the client token ID to be passed to the client process */
3080 Utf8Str strTokenId;
3081 sessionMachine->i_getTokenId(strTokenId);
3082 Assert(!strTokenId.isEmpty());
3083#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3084 /* Get the client token to be passed to the client process */
3085 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3086 /* The token is now "owned" by pToken, fix refcount */
3087 if (!pToken.isNull())
3088 pToken->Release();
3089#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3090
3091 /*
3092 * Release the lock before calling the client process -- it will call
3093 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3094 * because the state is Spawning, so that LaunchVMProcess() and
3095 * LockMachine() calls will fail. This method, called before we
3096 * acquire the lock again, will fail because of the wrong PID.
3097 *
3098 * Note that mData->mSession.mRemoteControls accessed outside
3099 * the lock may not be modified when state is Spawning, so it's safe.
3100 */
3101 alock.release();
3102
3103 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3104#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3105 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3106#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3107 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3108 /* Now the token is owned by the client process. */
3109 pToken.setNull();
3110#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3111 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3112
3113 /* The failure may occur w/o any error info (from RPC), so provide one */
3114 if (FAILED(rc))
3115 setError(VBOX_E_VM_ERROR,
3116 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3117
3118 // get session name, either to remember or to compare against
3119 // the already known session name.
3120 {
3121 Bstr bstrSessionName;
3122 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3123 if (SUCCEEDED(rc2))
3124 strSessionName = bstrSessionName;
3125 }
3126
3127 if ( SUCCEEDED(rc)
3128 && fLaunchingVMProcess
3129 )
3130 {
3131 /* complete the remote session initialization */
3132
3133 /* get the console from the direct session */
3134 ComPtr<IConsole> console;
3135 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3136 ComAssertComRC(rc);
3137
3138 if (SUCCEEDED(rc) && !console)
3139 {
3140 ComAssert(!!console);
3141 rc = E_FAIL;
3142 }
3143
3144 /* assign machine & console to the remote session */
3145 if (SUCCEEDED(rc))
3146 {
3147 /*
3148 * after LaunchVMProcess(), the first and the only
3149 * entry in remoteControls is that remote session
3150 */
3151 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3152 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3153 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3154
3155 /* The failure may occur w/o any error info (from RPC), so provide one */
3156 if (FAILED(rc))
3157 setError(VBOX_E_VM_ERROR,
3158 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3159 }
3160
3161 if (FAILED(rc))
3162 pSessionControl->Uninitialize();
3163 }
3164
3165 /* acquire the lock again */
3166 alock.acquire();
3167
3168 /* Restore the session state */
3169 mData->mSession.mState = origState;
3170 }
3171
3172 // finalize spawning anyway (this is why we don't return on errors above)
3173 if (fLaunchingVMProcess)
3174 {
3175 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3176 /* Note that the progress object is finalized later */
3177 /** @todo Consider checking mData->mSession.mProgress for cancellation
3178 * around here. */
3179
3180 /* We don't reset mSession.mPID here because it is necessary for
3181 * SessionMachine::uninit() to reap the child process later. */
3182
3183 if (FAILED(rc))
3184 {
3185 /* Close the remote session, remove the remote control from the list
3186 * and reset session state to Closed (@note keep the code in sync
3187 * with the relevant part in checkForSpawnFailure()). */
3188
3189 Assert(mData->mSession.mRemoteControls.size() == 1);
3190 if (mData->mSession.mRemoteControls.size() == 1)
3191 {
3192 ErrorInfoKeeper eik;
3193 mData->mSession.mRemoteControls.front()->Uninitialize();
3194 }
3195
3196 mData->mSession.mRemoteControls.clear();
3197 mData->mSession.mState = SessionState_Unlocked;
3198 }
3199 }
3200 else
3201 {
3202 /* memorize PID of the directly opened session */
3203 if (SUCCEEDED(rc))
3204 mData->mSession.mPID = pid;
3205 }
3206
3207 if (SUCCEEDED(rc))
3208 {
3209 mData->mSession.mLockType = aLockType;
3210 /* memorize the direct session control and cache IUnknown for it */
3211 mData->mSession.mDirectControl = pSessionControl;
3212 mData->mSession.mState = SessionState_Locked;
3213 if (!fLaunchingVMProcess)
3214 mData->mSession.mName = strSessionName;
3215 /* associate the SessionMachine with this Machine */
3216 mData->mSession.mMachine = sessionMachine;
3217
3218 /* request an IUnknown pointer early from the remote party for later
3219 * identity checks (it will be internally cached within mDirectControl
3220 * at least on XPCOM) */
3221 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3222 NOREF(unk);
3223 }
3224
3225 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3226 * would break the lock order */
3227 alock.release();
3228
3229 /* uninitialize the created session machine on failure */
3230 if (FAILED(rc))
3231 sessionMachine->uninit();
3232 }
3233
3234 if (SUCCEEDED(rc))
3235 {
3236 /*
3237 * tell the client watcher thread to update the set of
3238 * machines that have open sessions
3239 */
3240 mParent->i_updateClientWatcher();
3241
3242 if (oldState != SessionState_Locked)
3243 /* fire an event */
3244 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3245 }
3246
3247 return rc;
3248}
3249
3250/**
3251 * @note Locks objects!
3252 */
3253HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3254 const com::Utf8Str &aName,
3255 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3256 ComPtr<IProgress> &aProgress)
3257{
3258 Utf8Str strFrontend(aName);
3259 /* "emergencystop" doesn't need the session, so skip the checks/interface
3260 * retrieval. This code doesn't quite fit in here, but introducing a
3261 * special API method would be even more effort, and would require explicit
3262 * support by every API client. It's better to hide the feature a bit. */
3263 if (strFrontend != "emergencystop")
3264 CheckComArgNotNull(aSession);
3265
3266 HRESULT rc = S_OK;
3267 if (strFrontend.isEmpty())
3268 {
3269 Bstr bstrFrontend;
3270 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3271 if (FAILED(rc))
3272 return rc;
3273 strFrontend = bstrFrontend;
3274 if (strFrontend.isEmpty())
3275 {
3276 ComPtr<ISystemProperties> systemProperties;
3277 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3278 if (FAILED(rc))
3279 return rc;
3280 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3281 if (FAILED(rc))
3282 return rc;
3283 strFrontend = bstrFrontend;
3284 }
3285 /* paranoia - emergencystop is not a valid default */
3286 if (strFrontend == "emergencystop")
3287 strFrontend = Utf8Str::Empty;
3288 }
3289 /* default frontend: Qt GUI */
3290 if (strFrontend.isEmpty())
3291 strFrontend = "GUI/Qt";
3292
3293 if (strFrontend != "emergencystop")
3294 {
3295 /* check the session state */
3296 SessionState_T state;
3297 rc = aSession->COMGETTER(State)(&state);
3298 if (FAILED(rc))
3299 return rc;
3300
3301 if (state != SessionState_Unlocked)
3302 return setError(VBOX_E_INVALID_OBJECT_STATE,
3303 tr("The given session is busy"));
3304
3305 /* get the IInternalSessionControl interface */
3306 ComPtr<IInternalSessionControl> control(aSession);
3307 ComAssertMsgRet(!control.isNull(),
3308 ("No IInternalSessionControl interface"),
3309 E_INVALIDARG);
3310
3311 /* get the teleporter enable state for the progress object init. */
3312 BOOL fTeleporterEnabled;
3313 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3314 if (FAILED(rc))
3315 return rc;
3316
3317 /* create a progress object */
3318 ComObjPtr<ProgressProxy> progress;
3319 progress.createObject();
3320 rc = progress->init(mParent,
3321 static_cast<IMachine*>(this),
3322 Bstr(tr("Starting VM")).raw(),
3323 TRUE /* aCancelable */,
3324 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3325 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3326 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3327 2 /* uFirstOperationWeight */,
3328 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3329
3330 if (SUCCEEDED(rc))
3331 {
3332 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3333 if (SUCCEEDED(rc))
3334 {
3335 aProgress = progress;
3336
3337 /* signal the client watcher thread */
3338 mParent->i_updateClientWatcher();
3339
3340 /* fire an event */
3341 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3342 }
3343 }
3344 }
3345 else
3346 {
3347 /* no progress object - either instant success or failure */
3348 aProgress = NULL;
3349
3350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3351
3352 if (mData->mSession.mState != SessionState_Locked)
3353 return setError(VBOX_E_INVALID_OBJECT_STATE,
3354 tr("The machine '%s' is not locked by a session"),
3355 mUserData->s.strName.c_str());
3356
3357 /* must have a VM process associated - do not kill normal API clients
3358 * with an open session */
3359 if (!Global::IsOnline(mData->mMachineState))
3360 return setError(VBOX_E_INVALID_OBJECT_STATE,
3361 tr("The machine '%s' does not have a VM process"),
3362 mUserData->s.strName.c_str());
3363
3364 /* forcibly terminate the VM process */
3365 if (mData->mSession.mPID != NIL_RTPROCESS)
3366 RTProcTerminate(mData->mSession.mPID);
3367
3368 /* signal the client watcher thread, as most likely the client has
3369 * been terminated */
3370 mParent->i_updateClientWatcher();
3371 }
3372
3373 return rc;
3374}
3375
3376HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3377{
3378 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3379 return setError(E_INVALIDARG,
3380 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3381 aPosition, SchemaDefs::MaxBootPosition);
3382
3383 if (aDevice == DeviceType_USB)
3384 return setError(E_NOTIMPL,
3385 tr("Booting from USB device is currently not supported"));
3386
3387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3388
3389 HRESULT rc = i_checkStateDependency(MutableStateDep);
3390 if (FAILED(rc)) return rc;
3391
3392 i_setModified(IsModified_MachineData);
3393 mHWData.backup();
3394 mHWData->mBootOrder[aPosition - 1] = aDevice;
3395
3396 return S_OK;
3397}
3398
3399HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3400{
3401 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3402 return setError(E_INVALIDARG,
3403 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3404 aPosition, SchemaDefs::MaxBootPosition);
3405
3406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3407
3408 *aDevice = mHWData->mBootOrder[aPosition - 1];
3409
3410 return S_OK;
3411}
3412
3413HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3414 LONG aControllerPort,
3415 LONG aDevice,
3416 DeviceType_T aType,
3417 const ComPtr<IMedium> &aMedium)
3418{
3419 IMedium *aM = aMedium;
3420 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3421 aName.c_str(), aControllerPort, aDevice, aType, aM));
3422
3423 // request the host lock first, since might be calling Host methods for getting host drives;
3424 // next, protect the media tree all the while we're in here, as well as our member variables
3425 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3426 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3427
3428 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3429 if (FAILED(rc)) return rc;
3430
3431 /// @todo NEWMEDIA implicit machine registration
3432 if (!mData->mRegistered)
3433 return setError(VBOX_E_INVALID_OBJECT_STATE,
3434 tr("Cannot attach storage devices to an unregistered machine"));
3435
3436 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3437
3438 /* Check for an existing controller. */
3439 ComObjPtr<StorageController> ctl;
3440 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3441 if (FAILED(rc)) return rc;
3442
3443 StorageControllerType_T ctrlType;
3444 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3445 if (FAILED(rc))
3446 return setError(E_FAIL,
3447 tr("Could not get type of controller '%s'"),
3448 aName.c_str());
3449
3450 bool fSilent = false;
3451 Utf8Str strReconfig;
3452
3453 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3454 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3455 if ( mData->mMachineState == MachineState_Paused
3456 && strReconfig == "1")
3457 fSilent = true;
3458
3459 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3460 bool fHotplug = false;
3461 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3462 fHotplug = true;
3463
3464 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3465 return setError(VBOX_E_INVALID_VM_STATE,
3466 tr("Controller '%s' does not support hotplugging"),
3467 aName.c_str());
3468
3469 // check that the port and device are not out of range
3470 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3471 if (FAILED(rc)) return rc;
3472
3473 /* check if the device slot is already busy */
3474 MediumAttachment *pAttachTemp;
3475 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3476 aName,
3477 aControllerPort,
3478 aDevice)))
3479 {
3480 Medium *pMedium = pAttachTemp->i_getMedium();
3481 if (pMedium)
3482 {
3483 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3484 return setError(VBOX_E_OBJECT_IN_USE,
3485 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3486 pMedium->i_getLocationFull().c_str(),
3487 aControllerPort,
3488 aDevice,
3489 aName.c_str());
3490 }
3491 else
3492 return setError(VBOX_E_OBJECT_IN_USE,
3493 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3494 aControllerPort, aDevice, aName.c_str());
3495 }
3496
3497 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3498 if (aMedium && medium.isNull())
3499 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3500
3501 AutoCaller mediumCaller(medium);
3502 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3503
3504 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3505
3506 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3507 && !medium.isNull()
3508 )
3509 return setError(VBOX_E_OBJECT_IN_USE,
3510 tr("Medium '%s' is already attached to this virtual machine"),
3511 medium->i_getLocationFull().c_str());
3512
3513 if (!medium.isNull())
3514 {
3515 MediumType_T mtype = medium->i_getType();
3516 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3517 // For DVDs it's not written to the config file, so needs no global config
3518 // version bump. For floppies it's a new attribute "type", which is ignored
3519 // by older VirtualBox version, so needs no global config version bump either.
3520 // For hard disks this type is not accepted.
3521 if (mtype == MediumType_MultiAttach)
3522 {
3523 // This type is new with VirtualBox 4.0 and therefore requires settings
3524 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3525 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3526 // two reasons: The medium type is a property of the media registry tree, which
3527 // can reside in the global config file (for pre-4.0 media); we would therefore
3528 // possibly need to bump the global config version. We don't want to do that though
3529 // because that might make downgrading to pre-4.0 impossible.
3530 // As a result, we can only use these two new types if the medium is NOT in the
3531 // global registry:
3532 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3533 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3534 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3535 )
3536 return setError(VBOX_E_INVALID_OBJECT_STATE,
3537 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3538 "to machines that were created with VirtualBox 4.0 or later"),
3539 medium->i_getLocationFull().c_str());
3540 }
3541 }
3542
3543 bool fIndirect = false;
3544 if (!medium.isNull())
3545 fIndirect = medium->i_isReadOnly();
3546 bool associate = true;
3547
3548 do
3549 {
3550 if ( aType == DeviceType_HardDisk
3551 && mMediumAttachments.isBackedUp())
3552 {
3553 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3554
3555 /* check if the medium was attached to the VM before we started
3556 * changing attachments in which case the attachment just needs to
3557 * be restored */
3558 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3559 {
3560 AssertReturn(!fIndirect, E_FAIL);
3561
3562 /* see if it's the same bus/channel/device */
3563 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3564 {
3565 /* the simplest case: restore the whole attachment
3566 * and return, nothing else to do */
3567 mMediumAttachments->push_back(pAttachTemp);
3568
3569 /* Reattach the medium to the VM. */
3570 if (fHotplug || fSilent)
3571 {
3572 mediumLock.release();
3573 treeLock.release();
3574 alock.release();
3575
3576 MediumLockList *pMediumLockList(new MediumLockList());
3577
3578 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3579 medium /* pToLockWrite */,
3580 false /* fMediumLockWriteAll */,
3581 NULL,
3582 *pMediumLockList);
3583 alock.acquire();
3584 if (FAILED(rc))
3585 delete pMediumLockList;
3586 else
3587 {
3588 mData->mSession.mLockedMedia.Unlock();
3589 alock.release();
3590 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3591 mData->mSession.mLockedMedia.Lock();
3592 alock.acquire();
3593 }
3594 alock.release();
3595
3596 if (SUCCEEDED(rc))
3597 {
3598 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3599 /* Remove lock list in case of error. */
3600 if (FAILED(rc))
3601 {
3602 mData->mSession.mLockedMedia.Unlock();
3603 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3604 mData->mSession.mLockedMedia.Lock();
3605 }
3606 }
3607 }
3608
3609 return S_OK;
3610 }
3611
3612 /* bus/channel/device differ; we need a new attachment object,
3613 * but don't try to associate it again */
3614 associate = false;
3615 break;
3616 }
3617 }
3618
3619 /* go further only if the attachment is to be indirect */
3620 if (!fIndirect)
3621 break;
3622
3623 /* perform the so called smart attachment logic for indirect
3624 * attachments. Note that smart attachment is only applicable to base
3625 * hard disks. */
3626
3627 if (medium->i_getParent().isNull())
3628 {
3629 /* first, investigate the backup copy of the current hard disk
3630 * attachments to make it possible to re-attach existing diffs to
3631 * another device slot w/o losing their contents */
3632 if (mMediumAttachments.isBackedUp())
3633 {
3634 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3635
3636 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3637 uint32_t foundLevel = 0;
3638
3639 for (MediumAttachmentList::const_iterator
3640 it = oldAtts.begin();
3641 it != oldAtts.end();
3642 ++it)
3643 {
3644 uint32_t level = 0;
3645 MediumAttachment *pAttach = *it;
3646 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3647 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3648 if (pMedium.isNull())
3649 continue;
3650
3651 if (pMedium->i_getBase(&level) == medium)
3652 {
3653 /* skip the hard disk if its currently attached (we
3654 * cannot attach the same hard disk twice) */
3655 if (i_findAttachment(*mMediumAttachments.data(),
3656 pMedium))
3657 continue;
3658
3659 /* matched device, channel and bus (i.e. attached to the
3660 * same place) will win and immediately stop the search;
3661 * otherwise the attachment that has the youngest
3662 * descendant of medium will be used
3663 */
3664 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3665 {
3666 /* the simplest case: restore the whole attachment
3667 * and return, nothing else to do */
3668 mMediumAttachments->push_back(*it);
3669
3670 /* Reattach the medium to the VM. */
3671 if (fHotplug || fSilent)
3672 {
3673 mediumLock.release();
3674 treeLock.release();
3675 alock.release();
3676
3677 MediumLockList *pMediumLockList(new MediumLockList());
3678
3679 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3680 medium /* pToLockWrite */,
3681 false /* fMediumLockWriteAll */,
3682 NULL,
3683 *pMediumLockList);
3684 alock.acquire();
3685 if (FAILED(rc))
3686 delete pMediumLockList;
3687 else
3688 {
3689 mData->mSession.mLockedMedia.Unlock();
3690 alock.release();
3691 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3692 mData->mSession.mLockedMedia.Lock();
3693 alock.acquire();
3694 }
3695 alock.release();
3696
3697 if (SUCCEEDED(rc))
3698 {
3699 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3700 /* Remove lock list in case of error. */
3701 if (FAILED(rc))
3702 {
3703 mData->mSession.mLockedMedia.Unlock();
3704 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3705 mData->mSession.mLockedMedia.Lock();
3706 }
3707 }
3708 }
3709
3710 return S_OK;
3711 }
3712 else if ( foundIt == oldAtts.end()
3713 || level > foundLevel /* prefer younger */
3714 )
3715 {
3716 foundIt = it;
3717 foundLevel = level;
3718 }
3719 }
3720 }
3721
3722 if (foundIt != oldAtts.end())
3723 {
3724 /* use the previously attached hard disk */
3725 medium = (*foundIt)->i_getMedium();
3726 mediumCaller.attach(medium);
3727 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3728 mediumLock.attach(medium);
3729 /* not implicit, doesn't require association with this VM */
3730 fIndirect = false;
3731 associate = false;
3732 /* go right to the MediumAttachment creation */
3733 break;
3734 }
3735 }
3736
3737 /* must give up the medium lock and medium tree lock as below we
3738 * go over snapshots, which needs a lock with higher lock order. */
3739 mediumLock.release();
3740 treeLock.release();
3741
3742 /* then, search through snapshots for the best diff in the given
3743 * hard disk's chain to base the new diff on */
3744
3745 ComObjPtr<Medium> base;
3746 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3747 while (snap)
3748 {
3749 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3750
3751 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3752
3753 MediumAttachment *pAttachFound = NULL;
3754 uint32_t foundLevel = 0;
3755
3756 for (MediumAttachmentList::const_iterator
3757 it = snapAtts.begin();
3758 it != snapAtts.end();
3759 ++it)
3760 {
3761 MediumAttachment *pAttach = *it;
3762 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3763 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3764 if (pMedium.isNull())
3765 continue;
3766
3767 uint32_t level = 0;
3768 if (pMedium->i_getBase(&level) == medium)
3769 {
3770 /* matched device, channel and bus (i.e. attached to the
3771 * same place) will win and immediately stop the search;
3772 * otherwise the attachment that has the youngest
3773 * descendant of medium will be used
3774 */
3775 if ( pAttach->i_getDevice() == aDevice
3776 && pAttach->i_getPort() == aControllerPort
3777 && pAttach->i_getControllerName() == aName
3778 )
3779 {
3780 pAttachFound = pAttach;
3781 break;
3782 }
3783 else if ( !pAttachFound
3784 || level > foundLevel /* prefer younger */
3785 )
3786 {
3787 pAttachFound = pAttach;
3788 foundLevel = level;
3789 }
3790 }
3791 }
3792
3793 if (pAttachFound)
3794 {
3795 base = pAttachFound->i_getMedium();
3796 break;
3797 }
3798
3799 snap = snap->i_getParent();
3800 }
3801
3802 /* re-lock medium tree and the medium, as we need it below */
3803 treeLock.acquire();
3804 mediumLock.acquire();
3805
3806 /* found a suitable diff, use it as a base */
3807 if (!base.isNull())
3808 {
3809 medium = base;
3810 mediumCaller.attach(medium);
3811 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3812 mediumLock.attach(medium);
3813 }
3814 }
3815
3816 Utf8Str strFullSnapshotFolder;
3817 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3818
3819 ComObjPtr<Medium> diff;
3820 diff.createObject();
3821 // store this diff in the same registry as the parent
3822 Guid uuidRegistryParent;
3823 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3824 {
3825 // parent image has no registry: this can happen if we're attaching a new immutable
3826 // image that has not yet been attached (medium then points to the base and we're
3827 // creating the diff image for the immutable, and the parent is not yet registered);
3828 // put the parent in the machine registry then
3829 mediumLock.release();
3830 treeLock.release();
3831 alock.release();
3832 i_addMediumToRegistry(medium);
3833 alock.acquire();
3834 treeLock.acquire();
3835 mediumLock.acquire();
3836 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3837 }
3838 rc = diff->init(mParent,
3839 medium->i_getPreferredDiffFormat(),
3840 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3841 uuidRegistryParent,
3842 DeviceType_HardDisk);
3843 if (FAILED(rc)) return rc;
3844
3845 /* Apply the normal locking logic to the entire chain. */
3846 MediumLockList *pMediumLockList(new MediumLockList());
3847 mediumLock.release();
3848 treeLock.release();
3849 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3850 diff /* pToLockWrite */,
3851 false /* fMediumLockWriteAll */,
3852 medium,
3853 *pMediumLockList);
3854 treeLock.acquire();
3855 mediumLock.acquire();
3856 if (SUCCEEDED(rc))
3857 {
3858 mediumLock.release();
3859 treeLock.release();
3860 rc = pMediumLockList->Lock();
3861 treeLock.acquire();
3862 mediumLock.acquire();
3863 if (FAILED(rc))
3864 setError(rc,
3865 tr("Could not lock medium when creating diff '%s'"),
3866 diff->i_getLocationFull().c_str());
3867 else
3868 {
3869 /* will release the lock before the potentially lengthy
3870 * operation, so protect with the special state */
3871 MachineState_T oldState = mData->mMachineState;
3872 i_setMachineState(MachineState_SettingUp);
3873
3874 mediumLock.release();
3875 treeLock.release();
3876 alock.release();
3877
3878 rc = medium->i_createDiffStorage(diff,
3879 medium->i_getPreferredDiffVariant(),
3880 pMediumLockList,
3881 NULL /* aProgress */,
3882 true /* aWait */,
3883 false /* aNotify */);
3884
3885 alock.acquire();
3886 treeLock.acquire();
3887 mediumLock.acquire();
3888
3889 i_setMachineState(oldState);
3890 }
3891 }
3892
3893 /* Unlock the media and free the associated memory. */
3894 delete pMediumLockList;
3895
3896 if (FAILED(rc)) return rc;
3897
3898 /* use the created diff for the actual attachment */
3899 medium = diff;
3900 mediumCaller.attach(medium);
3901 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3902 mediumLock.attach(medium);
3903 }
3904 while (0);
3905
3906 ComObjPtr<MediumAttachment> attachment;
3907 attachment.createObject();
3908 rc = attachment->init(this,
3909 medium,
3910 aName,
3911 aControllerPort,
3912 aDevice,
3913 aType,
3914 fIndirect,
3915 false /* fPassthrough */,
3916 false /* fTempEject */,
3917 false /* fNonRotational */,
3918 false /* fDiscard */,
3919 fHotplug /* fHotPluggable */,
3920 Utf8Str::Empty);
3921 if (FAILED(rc)) return rc;
3922
3923 if (associate && !medium.isNull())
3924 {
3925 // as the last step, associate the medium to the VM
3926 rc = medium->i_addBackReference(mData->mUuid);
3927 // here we can fail because of Deleting, or being in process of creating a Diff
3928 if (FAILED(rc)) return rc;
3929
3930 mediumLock.release();
3931 treeLock.release();
3932 alock.release();
3933 i_addMediumToRegistry(medium);
3934 alock.acquire();
3935 treeLock.acquire();
3936 mediumLock.acquire();
3937 }
3938
3939 /* success: finally remember the attachment */
3940 i_setModified(IsModified_Storage);
3941 mMediumAttachments.backup();
3942 mMediumAttachments->push_back(attachment);
3943
3944 mediumLock.release();
3945 treeLock.release();
3946 alock.release();
3947
3948 if (fHotplug || fSilent)
3949 {
3950 if (!medium.isNull())
3951 {
3952 MediumLockList *pMediumLockList(new MediumLockList());
3953
3954 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3955 medium /* pToLockWrite */,
3956 false /* fMediumLockWriteAll */,
3957 NULL,
3958 *pMediumLockList);
3959 alock.acquire();
3960 if (FAILED(rc))
3961 delete pMediumLockList;
3962 else
3963 {
3964 mData->mSession.mLockedMedia.Unlock();
3965 alock.release();
3966 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3967 mData->mSession.mLockedMedia.Lock();
3968 alock.acquire();
3969 }
3970 alock.release();
3971 }
3972
3973 if (SUCCEEDED(rc))
3974 {
3975 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3976 /* Remove lock list in case of error. */
3977 if (FAILED(rc))
3978 {
3979 mData->mSession.mLockedMedia.Unlock();
3980 mData->mSession.mLockedMedia.Remove(attachment);
3981 mData->mSession.mLockedMedia.Lock();
3982 }
3983 }
3984 }
3985
3986 /* Save modified registries, but skip this machine as it's the caller's
3987 * job to save its settings like all other settings changes. */
3988 mParent->i_unmarkRegistryModified(i_getId());
3989 mParent->i_saveModifiedRegistries();
3990
3991 if (SUCCEEDED(rc))
3992 {
3993 if (fIndirect && medium != aM)
3994 mParent->i_onMediumConfigChanged(medium);
3995 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3996 }
3997
3998 return rc;
3999}
4000
4001HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4002 LONG aDevice)
4003{
4004 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4005 aName.c_str(), aControllerPort, aDevice));
4006
4007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4008
4009 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4010 if (FAILED(rc)) return rc;
4011
4012 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4013
4014 /* Check for an existing controller. */
4015 ComObjPtr<StorageController> ctl;
4016 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4017 if (FAILED(rc)) return rc;
4018
4019 StorageControllerType_T ctrlType;
4020 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4021 if (FAILED(rc))
4022 return setError(E_FAIL,
4023 tr("Could not get type of controller '%s'"),
4024 aName.c_str());
4025
4026 bool fSilent = false;
4027 Utf8Str strReconfig;
4028
4029 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4030 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4031 if ( mData->mMachineState == MachineState_Paused
4032 && strReconfig == "1")
4033 fSilent = true;
4034
4035 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4036 bool fHotplug = false;
4037 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4038 fHotplug = true;
4039
4040 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4041 return setError(VBOX_E_INVALID_VM_STATE,
4042 tr("Controller '%s' does not support hotplugging"),
4043 aName.c_str());
4044
4045 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4046 aName,
4047 aControllerPort,
4048 aDevice);
4049 if (!pAttach)
4050 return setError(VBOX_E_OBJECT_NOT_FOUND,
4051 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4052 aDevice, aControllerPort, aName.c_str());
4053
4054 if (fHotplug && !pAttach->i_getHotPluggable())
4055 return setError(VBOX_E_NOT_SUPPORTED,
4056 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4057 aDevice, aControllerPort, aName.c_str());
4058
4059 /*
4060 * The VM has to detach the device before we delete any implicit diffs.
4061 * If this fails we can roll back without loosing data.
4062 */
4063 if (fHotplug || fSilent)
4064 {
4065 alock.release();
4066 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4067 alock.acquire();
4068 }
4069 if (FAILED(rc)) return rc;
4070
4071 /* If we are here everything went well and we can delete the implicit now. */
4072 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4073
4074 alock.release();
4075
4076 /* Save modified registries, but skip this machine as it's the caller's
4077 * job to save its settings like all other settings changes. */
4078 mParent->i_unmarkRegistryModified(i_getId());
4079 mParent->i_saveModifiedRegistries();
4080
4081 if (SUCCEEDED(rc))
4082 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4083
4084 return rc;
4085}
4086
4087HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4088 LONG aDevice, BOOL aPassthrough)
4089{
4090 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4091 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4092
4093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4094
4095 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4096 if (FAILED(rc)) return rc;
4097
4098 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4099
4100 /* Check for an existing controller. */
4101 ComObjPtr<StorageController> ctl;
4102 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4103 if (FAILED(rc)) return rc;
4104
4105 StorageControllerType_T ctrlType;
4106 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4107 if (FAILED(rc))
4108 return setError(E_FAIL,
4109 tr("Could not get type of controller '%s'"),
4110 aName.c_str());
4111
4112 bool fSilent = false;
4113 Utf8Str strReconfig;
4114
4115 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4116 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4117 if ( mData->mMachineState == MachineState_Paused
4118 && strReconfig == "1")
4119 fSilent = true;
4120
4121 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4122 bool fHotplug = false;
4123 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4124 fHotplug = true;
4125
4126 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4127 return setError(VBOX_E_INVALID_VM_STATE,
4128 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4129 aName.c_str());
4130
4131 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4132 aName,
4133 aControllerPort,
4134 aDevice);
4135 if (!pAttach)
4136 return setError(VBOX_E_OBJECT_NOT_FOUND,
4137 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4138 aDevice, aControllerPort, aName.c_str());
4139
4140
4141 i_setModified(IsModified_Storage);
4142 mMediumAttachments.backup();
4143
4144 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4145
4146 if (pAttach->i_getType() != DeviceType_DVD)
4147 return setError(E_INVALIDARG,
4148 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4149 aDevice, aControllerPort, aName.c_str());
4150
4151 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4152
4153 pAttach->i_updatePassthrough(!!aPassthrough);
4154
4155 attLock.release();
4156 alock.release();
4157 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4158 if (SUCCEEDED(rc) && fValueChanged)
4159 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4160
4161 return rc;
4162}
4163
4164HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4165 LONG aDevice, BOOL aTemporaryEject)
4166{
4167
4168 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4169 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4170
4171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4172
4173 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4174 if (FAILED(rc)) return rc;
4175
4176 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4177 aName,
4178 aControllerPort,
4179 aDevice);
4180 if (!pAttach)
4181 return setError(VBOX_E_OBJECT_NOT_FOUND,
4182 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4183 aDevice, aControllerPort, aName.c_str());
4184
4185
4186 i_setModified(IsModified_Storage);
4187 mMediumAttachments.backup();
4188
4189 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4190
4191 if (pAttach->i_getType() != DeviceType_DVD)
4192 return setError(E_INVALIDARG,
4193 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4194 aDevice, aControllerPort, aName.c_str());
4195 pAttach->i_updateTempEject(!!aTemporaryEject);
4196
4197 return S_OK;
4198}
4199
4200HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4201 LONG aDevice, BOOL aNonRotational)
4202{
4203
4204 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4205 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4206
4207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4208
4209 HRESULT rc = i_checkStateDependency(MutableStateDep);
4210 if (FAILED(rc)) return rc;
4211
4212 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4213
4214 if (Global::IsOnlineOrTransient(mData->mMachineState))
4215 return setError(VBOX_E_INVALID_VM_STATE,
4216 tr("Invalid machine state: %s"),
4217 Global::stringifyMachineState(mData->mMachineState));
4218
4219 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4220 aName,
4221 aControllerPort,
4222 aDevice);
4223 if (!pAttach)
4224 return setError(VBOX_E_OBJECT_NOT_FOUND,
4225 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4226 aDevice, aControllerPort, aName.c_str());
4227
4228
4229 i_setModified(IsModified_Storage);
4230 mMediumAttachments.backup();
4231
4232 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4233
4234 if (pAttach->i_getType() != DeviceType_HardDisk)
4235 return setError(E_INVALIDARG,
4236 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"),
4237 aDevice, aControllerPort, aName.c_str());
4238 pAttach->i_updateNonRotational(!!aNonRotational);
4239
4240 return S_OK;
4241}
4242
4243HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4244 LONG aDevice, BOOL aDiscard)
4245{
4246
4247 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4248 aName.c_str(), aControllerPort, aDevice, aDiscard));
4249
4250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4251
4252 HRESULT rc = i_checkStateDependency(MutableStateDep);
4253 if (FAILED(rc)) return rc;
4254
4255 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4256
4257 if (Global::IsOnlineOrTransient(mData->mMachineState))
4258 return setError(VBOX_E_INVALID_VM_STATE,
4259 tr("Invalid machine state: %s"),
4260 Global::stringifyMachineState(mData->mMachineState));
4261
4262 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4263 aName,
4264 aControllerPort,
4265 aDevice);
4266 if (!pAttach)
4267 return setError(VBOX_E_OBJECT_NOT_FOUND,
4268 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4269 aDevice, aControllerPort, aName.c_str());
4270
4271
4272 i_setModified(IsModified_Storage);
4273 mMediumAttachments.backup();
4274
4275 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4276
4277 if (pAttach->i_getType() != DeviceType_HardDisk)
4278 return setError(E_INVALIDARG,
4279 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"),
4280 aDevice, aControllerPort, aName.c_str());
4281 pAttach->i_updateDiscard(!!aDiscard);
4282
4283 return S_OK;
4284}
4285
4286HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4287 LONG aDevice, BOOL aHotPluggable)
4288{
4289 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4290 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4291
4292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4293
4294 HRESULT rc = i_checkStateDependency(MutableStateDep);
4295 if (FAILED(rc)) return rc;
4296
4297 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4298
4299 if (Global::IsOnlineOrTransient(mData->mMachineState))
4300 return setError(VBOX_E_INVALID_VM_STATE,
4301 tr("Invalid machine state: %s"),
4302 Global::stringifyMachineState(mData->mMachineState));
4303
4304 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4305 aName,
4306 aControllerPort,
4307 aDevice);
4308 if (!pAttach)
4309 return setError(VBOX_E_OBJECT_NOT_FOUND,
4310 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4311 aDevice, aControllerPort, aName.c_str());
4312
4313 /* Check for an existing controller. */
4314 ComObjPtr<StorageController> ctl;
4315 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4316 if (FAILED(rc)) return rc;
4317
4318 StorageControllerType_T ctrlType;
4319 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4320 if (FAILED(rc))
4321 return setError(E_FAIL,
4322 tr("Could not get type of controller '%s'"),
4323 aName.c_str());
4324
4325 if (!i_isControllerHotplugCapable(ctrlType))
4326 return setError(VBOX_E_NOT_SUPPORTED,
4327 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4328 aName.c_str());
4329
4330 i_setModified(IsModified_Storage);
4331 mMediumAttachments.backup();
4332
4333 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4334
4335 if (pAttach->i_getType() == DeviceType_Floppy)
4336 return setError(E_INVALIDARG,
4337 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"),
4338 aDevice, aControllerPort, aName.c_str());
4339 pAttach->i_updateHotPluggable(!!aHotPluggable);
4340
4341 return S_OK;
4342}
4343
4344HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4345 LONG aDevice)
4346{
4347 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4348 aName.c_str(), aControllerPort, aDevice));
4349
4350 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4351}
4352
4353HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4354 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4355{
4356 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4357 aName.c_str(), aControllerPort, aDevice));
4358
4359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4360
4361 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4362 if (FAILED(rc)) return rc;
4363
4364 if (Global::IsOnlineOrTransient(mData->mMachineState))
4365 return setError(VBOX_E_INVALID_VM_STATE,
4366 tr("Invalid machine state: %s"),
4367 Global::stringifyMachineState(mData->mMachineState));
4368
4369 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4370 aName,
4371 aControllerPort,
4372 aDevice);
4373 if (!pAttach)
4374 return setError(VBOX_E_OBJECT_NOT_FOUND,
4375 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4376 aDevice, aControllerPort, aName.c_str());
4377
4378
4379 i_setModified(IsModified_Storage);
4380 mMediumAttachments.backup();
4381
4382 IBandwidthGroup *iB = aBandwidthGroup;
4383 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4384 if (aBandwidthGroup && group.isNull())
4385 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4386
4387 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4388
4389 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4390 if (strBandwidthGroupOld.isNotEmpty())
4391 {
4392 /* Get the bandwidth group object and release it - this must not fail. */
4393 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4394 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4395 Assert(SUCCEEDED(rc));
4396
4397 pBandwidthGroupOld->i_release();
4398 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4399 }
4400
4401 if (!group.isNull())
4402 {
4403 group->i_reference();
4404 pAttach->i_updateBandwidthGroup(group->i_getName());
4405 }
4406
4407 return S_OK;
4408}
4409
4410HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4411 LONG aControllerPort,
4412 LONG aDevice,
4413 DeviceType_T aType)
4414{
4415 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4416 aName.c_str(), aControllerPort, aDevice, aType));
4417
4418 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4419}
4420
4421
4422HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4423 LONG aControllerPort,
4424 LONG aDevice,
4425 BOOL aForce)
4426{
4427 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4428 aName.c_str(), aControllerPort, aForce));
4429
4430 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4431}
4432
4433HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4434 LONG aControllerPort,
4435 LONG aDevice,
4436 const ComPtr<IMedium> &aMedium,
4437 BOOL aForce)
4438{
4439 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4440 aName.c_str(), aControllerPort, aDevice, aForce));
4441
4442 // request the host lock first, since might be calling Host methods for getting host drives;
4443 // next, protect the media tree all the while we're in here, as well as our member variables
4444 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4445 this->lockHandle(),
4446 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4447
4448 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4449 aName,
4450 aControllerPort,
4451 aDevice);
4452 if (pAttach.isNull())
4453 return setError(VBOX_E_OBJECT_NOT_FOUND,
4454 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4455 aDevice, aControllerPort, aName.c_str());
4456
4457 /* Remember previously mounted medium. The medium before taking the
4458 * backup is not necessarily the same thing. */
4459 ComObjPtr<Medium> oldmedium;
4460 oldmedium = pAttach->i_getMedium();
4461
4462 IMedium *iM = aMedium;
4463 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4464 if (aMedium && pMedium.isNull())
4465 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4466
4467 AutoCaller mediumCaller(pMedium);
4468 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4469
4470 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4471 if (pMedium)
4472 {
4473 DeviceType_T mediumType = pAttach->i_getType();
4474 switch (mediumType)
4475 {
4476 case DeviceType_DVD:
4477 case DeviceType_Floppy:
4478 break;
4479
4480 default:
4481 return setError(VBOX_E_INVALID_OBJECT_STATE,
4482 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4483 aControllerPort,
4484 aDevice,
4485 aName.c_str());
4486 }
4487 }
4488
4489 i_setModified(IsModified_Storage);
4490 mMediumAttachments.backup();
4491
4492 {
4493 // The backup operation makes the pAttach reference point to the
4494 // old settings. Re-get the correct reference.
4495 pAttach = i_findAttachment(*mMediumAttachments.data(),
4496 aName,
4497 aControllerPort,
4498 aDevice);
4499 if (!oldmedium.isNull())
4500 oldmedium->i_removeBackReference(mData->mUuid);
4501 if (!pMedium.isNull())
4502 {
4503 pMedium->i_addBackReference(mData->mUuid);
4504
4505 mediumLock.release();
4506 multiLock.release();
4507 i_addMediumToRegistry(pMedium);
4508 multiLock.acquire();
4509 mediumLock.acquire();
4510 }
4511
4512 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4513 pAttach->i_updateMedium(pMedium);
4514 }
4515
4516 i_setModified(IsModified_Storage);
4517
4518 mediumLock.release();
4519 multiLock.release();
4520 HRESULT rc = i_onMediumChange(pAttach, aForce);
4521 multiLock.acquire();
4522 mediumLock.acquire();
4523
4524 /* On error roll back this change only. */
4525 if (FAILED(rc))
4526 {
4527 if (!pMedium.isNull())
4528 pMedium->i_removeBackReference(mData->mUuid);
4529 pAttach = i_findAttachment(*mMediumAttachments.data(),
4530 aName,
4531 aControllerPort,
4532 aDevice);
4533 /* If the attachment is gone in the meantime, bail out. */
4534 if (pAttach.isNull())
4535 return rc;
4536 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4537 if (!oldmedium.isNull())
4538 oldmedium->i_addBackReference(mData->mUuid);
4539 pAttach->i_updateMedium(oldmedium);
4540 }
4541
4542 mediumLock.release();
4543 multiLock.release();
4544
4545 /* Save modified registries, but skip this machine as it's the caller's
4546 * job to save its settings like all other settings changes. */
4547 mParent->i_unmarkRegistryModified(i_getId());
4548 mParent->i_saveModifiedRegistries();
4549
4550 return rc;
4551}
4552HRESULT Machine::getMedium(const com::Utf8Str &aName,
4553 LONG aControllerPort,
4554 LONG aDevice,
4555 ComPtr<IMedium> &aMedium)
4556{
4557 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4558 aName.c_str(), aControllerPort, aDevice));
4559
4560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4561
4562 aMedium = NULL;
4563
4564 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4565 aName,
4566 aControllerPort,
4567 aDevice);
4568 if (pAttach.isNull())
4569 return setError(VBOX_E_OBJECT_NOT_FOUND,
4570 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4571 aDevice, aControllerPort, aName.c_str());
4572
4573 aMedium = pAttach->i_getMedium();
4574
4575 return S_OK;
4576}
4577
4578HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4579{
4580
4581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4582
4583 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4584
4585 return S_OK;
4586}
4587
4588HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4589{
4590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4591
4592 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4593
4594 return S_OK;
4595}
4596
4597HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4598{
4599 /* Do not assert if slot is out of range, just return the advertised
4600 status. testdriver/vbox.py triggers this in logVmInfo. */
4601 if (aSlot >= mNetworkAdapters.size())
4602 return setError(E_INVALIDARG,
4603 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4604 aSlot, mNetworkAdapters.size());
4605
4606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4607
4608 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4609
4610 return S_OK;
4611}
4612
4613HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4614{
4615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4616
4617 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4618 size_t i = 0;
4619 for (settings::StringsMap::const_iterator
4620 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4621 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4622 ++it, ++i)
4623 aKeys[i] = it->first;
4624
4625 return S_OK;
4626}
4627
4628 /**
4629 * @note Locks this object for reading.
4630 */
4631HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4632 com::Utf8Str &aValue)
4633{
4634 /* start with nothing found */
4635 aValue = "";
4636
4637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4638
4639 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4640 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4641 // found:
4642 aValue = it->second; // source is a Utf8Str
4643
4644 /* return the result to caller (may be empty) */
4645 return S_OK;
4646}
4647
4648 /**
4649 * @note Locks mParent for writing + this object for writing.
4650 */
4651HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4652{
4653 /* Because control characters in aKey have caused problems in the settings
4654 * they are rejected unless the key should be deleted. */
4655 if (!aValue.isEmpty())
4656 {
4657 for (size_t i = 0; i < aKey.length(); ++i)
4658 {
4659 char ch = aKey[i];
4660 if (RTLocCIsCntrl(ch))
4661 return E_INVALIDARG;
4662 }
4663 }
4664
4665 Utf8Str strOldValue; // empty
4666
4667 // locking note: we only hold the read lock briefly to look up the old value,
4668 // then release it and call the onExtraCanChange callbacks. There is a small
4669 // chance of a race insofar as the callback might be called twice if two callers
4670 // change the same key at the same time, but that's a much better solution
4671 // than the deadlock we had here before. The actual changing of the extradata
4672 // is then performed under the write lock and race-free.
4673
4674 // look up the old value first; if nothing has changed then we need not do anything
4675 {
4676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4677
4678 // For snapshots don't even think about allowing changes, extradata
4679 // is global for a machine, so there is nothing snapshot specific.
4680 if (i_isSnapshotMachine())
4681 return setError(VBOX_E_INVALID_VM_STATE,
4682 tr("Cannot set extradata for a snapshot"));
4683
4684 // check if the right IMachine instance is used
4685 if (mData->mRegistered && !i_isSessionMachine())
4686 return setError(VBOX_E_INVALID_VM_STATE,
4687 tr("Cannot set extradata for an immutable machine"));
4688
4689 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4690 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4691 strOldValue = it->second;
4692 }
4693
4694 bool fChanged;
4695 if ((fChanged = (strOldValue != aValue)))
4696 {
4697 // ask for permission from all listeners outside the locks;
4698 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4699 // lock to copy the list of callbacks to invoke
4700 Bstr error;
4701 Bstr bstrValue(aValue);
4702
4703 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4704 {
4705 const char *sep = error.isEmpty() ? "" : ": ";
4706 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4707 return setError(E_ACCESSDENIED,
4708 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4709 aKey.c_str(),
4710 aValue.c_str(),
4711 sep,
4712 error.raw());
4713 }
4714
4715 // data is changing and change not vetoed: then write it out under the lock
4716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4717
4718 if (aValue.isEmpty())
4719 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4720 else
4721 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4722 // creates a new key if needed
4723
4724 bool fNeedsGlobalSaveSettings = false;
4725 // This saving of settings is tricky: there is no "old state" for the
4726 // extradata items at all (unlike all other settings), so the old/new
4727 // settings comparison would give a wrong result!
4728 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4729
4730 if (fNeedsGlobalSaveSettings)
4731 {
4732 // save the global settings; for that we should hold only the VirtualBox lock
4733 alock.release();
4734 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4735 mParent->i_saveSettings();
4736 }
4737 }
4738
4739 // fire notification outside the lock
4740 if (fChanged)
4741 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4742
4743 return S_OK;
4744}
4745
4746HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4747{
4748 aProgress = NULL;
4749 NOREF(aSettingsFilePath);
4750 ReturnComNotImplemented();
4751}
4752
4753HRESULT Machine::saveSettings()
4754{
4755 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4756
4757 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4758 if (FAILED(rc)) return rc;
4759
4760 /* the settings file path may never be null */
4761 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4762
4763 /* save all VM data excluding snapshots */
4764 bool fNeedsGlobalSaveSettings = false;
4765 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4766 mlock.release();
4767
4768 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4769 {
4770 // save the global settings; for that we should hold only the VirtualBox lock
4771 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4772 rc = mParent->i_saveSettings();
4773 }
4774
4775 return rc;
4776}
4777
4778
4779HRESULT Machine::discardSettings()
4780{
4781 /*
4782 * We need to take the machine list lock here as well as the machine one
4783 * or we'll get into trouble should any media stuff require rolling back.
4784 *
4785 * Details:
4786 *
4787 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4788 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4789 * 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]
4790 * 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
4791 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4792 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4793 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4794 * 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
4795 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4796 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4797 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4798 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4799 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4800 * 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]
4801 * 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] (*)
4802 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4803 * 0:005> k
4804 * # Child-SP RetAddr Call Site
4805 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4806 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4807 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4808 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4809 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4810 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4811 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4812 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4813 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4814 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4815 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4816 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4817 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4818 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4819 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4820 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4821 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4822 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4823 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4824 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4825 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4826 *
4827 */
4828 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4830
4831 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4832 if (FAILED(rc)) return rc;
4833
4834 /*
4835 * during this rollback, the session will be notified if data has
4836 * been actually changed
4837 */
4838 i_rollback(true /* aNotify */);
4839
4840 return S_OK;
4841}
4842
4843/** @note Locks objects! */
4844HRESULT Machine::unregister(AutoCaller &autoCaller,
4845 CleanupMode_T aCleanupMode,
4846 std::vector<ComPtr<IMedium> > &aMedia)
4847{
4848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4849
4850 Guid id(i_getId());
4851
4852 if (mData->mSession.mState != SessionState_Unlocked)
4853 return setError(VBOX_E_INVALID_OBJECT_STATE,
4854 tr("Cannot unregister the machine '%s' while it is locked"),
4855 mUserData->s.strName.c_str());
4856
4857 // wait for state dependents to drop to zero
4858 i_ensureNoStateDependencies();
4859
4860 if (!mData->mAccessible)
4861 {
4862 // inaccessible machines can only be unregistered; uninitialize ourselves
4863 // here because currently there may be no unregistered that are inaccessible
4864 // (this state combination is not supported). Note releasing the caller and
4865 // leaving the lock before calling uninit()
4866 alock.release();
4867 autoCaller.release();
4868
4869 uninit();
4870
4871 mParent->i_unregisterMachine(this, id);
4872 // calls VirtualBox::i_saveSettings()
4873
4874 return S_OK;
4875 }
4876
4877 HRESULT rc = S_OK;
4878 mData->llFilesToDelete.clear();
4879
4880 if (!mSSData->strStateFilePath.isEmpty())
4881 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4882
4883 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4884 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4885 mData->llFilesToDelete.push_back(strNVRAMFile);
4886
4887 // This list collects the medium objects from all medium attachments
4888 // which we will detach from the machine and its snapshots, in a specific
4889 // order which allows for closing all media without getting "media in use"
4890 // errors, simply by going through the list from the front to the back:
4891 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4892 // and must be closed before the parent media from the snapshots, or closing the parents
4893 // will fail because they still have children);
4894 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4895 // the root ("first") snapshot of the machine.
4896 MediaList llMedia;
4897
4898 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4899 && mMediumAttachments->size()
4900 )
4901 {
4902 // we have media attachments: detach them all and add the Medium objects to our list
4903 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4904 }
4905
4906 if (mData->mFirstSnapshot)
4907 {
4908 // add the media from the medium attachments of the snapshots to llMedia
4909 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4910 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4911 // into the children first
4912
4913 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4914 MachineState_T oldState = mData->mMachineState;
4915 mData->mMachineState = MachineState_DeletingSnapshot;
4916
4917 // make a copy of the first snapshot reference so the refcount does not
4918 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4919 // (would hang due to the AutoCaller voodoo)
4920 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4921
4922 // GO!
4923 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4924
4925 mData->mMachineState = oldState;
4926 }
4927
4928 if (FAILED(rc))
4929 {
4930 i_rollbackMedia();
4931 return rc;
4932 }
4933
4934 // commit all the media changes made above
4935 i_commitMedia();
4936
4937 mData->mRegistered = false;
4938
4939 // machine lock no longer needed
4940 alock.release();
4941
4942 /* Make sure that the settings of the current VM are not saved, because
4943 * they are rather crippled at this point to meet the cleanup expectations
4944 * and there's no point destroying the VM config on disk just because. */
4945 mParent->i_unmarkRegistryModified(id);
4946
4947 // return media to caller
4948 aMedia.resize(llMedia.size());
4949 size_t i = 0;
4950 for (MediaList::const_iterator
4951 it = llMedia.begin();
4952 it != llMedia.end();
4953 ++it, ++i)
4954 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4955
4956 mParent->i_unregisterMachine(this, id);
4957 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4958
4959 return S_OK;
4960}
4961
4962/**
4963 * Task record for deleting a machine config.
4964 */
4965class Machine::DeleteConfigTask
4966 : public Machine::Task
4967{
4968public:
4969 DeleteConfigTask(Machine *m,
4970 Progress *p,
4971 const Utf8Str &t,
4972 const RTCList<ComPtr<IMedium> > &llMediums,
4973 const StringsList &llFilesToDelete)
4974 : Task(m, p, t),
4975 m_llMediums(llMediums),
4976 m_llFilesToDelete(llFilesToDelete)
4977 {}
4978
4979private:
4980 void handler()
4981 {
4982 try
4983 {
4984 m_pMachine->i_deleteConfigHandler(*this);
4985 }
4986 catch (...)
4987 {
4988 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4989 }
4990 }
4991
4992 RTCList<ComPtr<IMedium> > m_llMediums;
4993 StringsList m_llFilesToDelete;
4994
4995 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4996};
4997
4998/**
4999 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5000 * SessionMachine::taskHandler().
5001 *
5002 * @note Locks this object for writing.
5003 *
5004 * @param task
5005 * @return
5006 */
5007void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5008{
5009 LogFlowThisFuncEnter();
5010
5011 AutoCaller autoCaller(this);
5012 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5013 if (FAILED(autoCaller.rc()))
5014 {
5015 /* we might have been uninitialized because the session was accidentally
5016 * closed by the client, so don't assert */
5017 HRESULT rc = setError(E_FAIL,
5018 tr("The session has been accidentally closed"));
5019 task.m_pProgress->i_notifyComplete(rc);
5020 LogFlowThisFuncLeave();
5021 return;
5022 }
5023
5024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5025
5026 HRESULT rc = S_OK;
5027
5028 try
5029 {
5030 ULONG uLogHistoryCount = 3;
5031 ComPtr<ISystemProperties> systemProperties;
5032 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5033 if (FAILED(rc)) throw rc;
5034
5035 if (!systemProperties.isNull())
5036 {
5037 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5038 if (FAILED(rc)) throw rc;
5039 }
5040
5041 MachineState_T oldState = mData->mMachineState;
5042 i_setMachineState(MachineState_SettingUp);
5043 alock.release();
5044 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5045 {
5046 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5047 {
5048 AutoCaller mac(pMedium);
5049 if (FAILED(mac.rc())) throw mac.rc();
5050 Utf8Str strLocation = pMedium->i_getLocationFull();
5051 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5052 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5053 if (FAILED(rc)) throw rc;
5054 }
5055 if (pMedium->i_isMediumFormatFile())
5056 {
5057 ComPtr<IProgress> pProgress2;
5058 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5059 if (FAILED(rc)) throw rc;
5060 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5061 if (FAILED(rc)) throw rc;
5062 }
5063
5064 /* Close the medium, deliberately without checking the return
5065 * code, and without leaving any trace in the error info, as
5066 * a failure here is a very minor issue, which shouldn't happen
5067 * as above we even managed to delete the medium. */
5068 {
5069 ErrorInfoKeeper eik;
5070 pMedium->Close();
5071 }
5072 }
5073 i_setMachineState(oldState);
5074 alock.acquire();
5075
5076 // delete the files pushed on the task list by Machine::Delete()
5077 // (this includes saved states of the machine and snapshots and
5078 // medium storage files from the IMedium list passed in, and the
5079 // machine XML file)
5080 for (StringsList::const_iterator
5081 it = task.m_llFilesToDelete.begin();
5082 it != task.m_llFilesToDelete.end();
5083 ++it)
5084 {
5085 const Utf8Str &strFile = *it;
5086 LogFunc(("Deleting file %s\n", strFile.c_str()));
5087 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5088 if (FAILED(rc)) throw rc;
5089
5090 int vrc = RTFileDelete(strFile.c_str());
5091 if (RT_FAILURE(vrc))
5092 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5093 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5094 }
5095
5096 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5097 if (FAILED(rc)) throw rc;
5098
5099 /* delete the settings only when the file actually exists */
5100 if (mData->pMachineConfigFile->fileExists())
5101 {
5102 /* Delete any backup or uncommitted XML files. Ignore failures.
5103 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5104 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5105 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5106 RTFileDelete(otherXml.c_str());
5107 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5108 RTFileDelete(otherXml.c_str());
5109
5110 /* delete the Logs folder, nothing important should be left
5111 * there (we don't check for errors because the user might have
5112 * some private files there that we don't want to delete) */
5113 Utf8Str logFolder;
5114 getLogFolder(logFolder);
5115 Assert(logFolder.length());
5116 if (RTDirExists(logFolder.c_str()))
5117 {
5118 /* Delete all VBox.log[.N] files from the Logs folder
5119 * (this must be in sync with the rotation logic in
5120 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5121 * files that may have been created by the GUI. */
5122 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5123 RTFileDelete(log.c_str());
5124 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5125 RTFileDelete(log.c_str());
5126 for (ULONG i = uLogHistoryCount; i > 0; i--)
5127 {
5128 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5129 RTFileDelete(log.c_str());
5130 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5131 RTFileDelete(log.c_str());
5132 }
5133#if defined(RT_OS_WINDOWS)
5134 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5135 RTFileDelete(log.c_str());
5136 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5137 RTFileDelete(log.c_str());
5138#endif
5139
5140 RTDirRemove(logFolder.c_str());
5141 }
5142
5143 /* delete the Snapshots folder, nothing important should be left
5144 * there (we don't check for errors because the user might have
5145 * some private files there that we don't want to delete) */
5146 Utf8Str strFullSnapshotFolder;
5147 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5148 Assert(!strFullSnapshotFolder.isEmpty());
5149 if (RTDirExists(strFullSnapshotFolder.c_str()))
5150 RTDirRemove(strFullSnapshotFolder.c_str());
5151
5152 // delete the directory that contains the settings file, but only
5153 // if it matches the VM name
5154 Utf8Str settingsDir;
5155 if (i_isInOwnDir(&settingsDir))
5156 RTDirRemove(settingsDir.c_str());
5157 }
5158
5159 alock.release();
5160
5161 mParent->i_saveModifiedRegistries();
5162 }
5163 catch (HRESULT aRC) { rc = aRC; }
5164
5165 task.m_pProgress->i_notifyComplete(rc);
5166
5167 LogFlowThisFuncLeave();
5168}
5169
5170HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5171{
5172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5173
5174 HRESULT rc = i_checkStateDependency(MutableStateDep);
5175 if (FAILED(rc)) return rc;
5176
5177 if (mData->mRegistered)
5178 return setError(VBOX_E_INVALID_VM_STATE,
5179 tr("Cannot delete settings of a registered machine"));
5180
5181 // collect files to delete
5182 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5183 // machine config file
5184 if (mData->pMachineConfigFile->fileExists())
5185 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5186 // backup of machine config file
5187 Utf8Str strTmp(mData->m_strConfigFileFull);
5188 strTmp.append("-prev");
5189 if (RTFileExists(strTmp.c_str()))
5190 llFilesToDelete.push_back(strTmp);
5191
5192 RTCList<ComPtr<IMedium> > llMediums;
5193 for (size_t i = 0; i < aMedia.size(); ++i)
5194 {
5195 IMedium *pIMedium(aMedia[i]);
5196 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5197 if (pMedium.isNull())
5198 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5199 SafeArray<BSTR> ids;
5200 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5201 if (FAILED(rc)) return rc;
5202 /* At this point the medium should not have any back references
5203 * anymore. If it has it is attached to another VM and *must* not
5204 * deleted. */
5205 if (ids.size() < 1)
5206 llMediums.append(pMedium);
5207 }
5208
5209 ComObjPtr<Progress> pProgress;
5210 pProgress.createObject();
5211 rc = pProgress->init(i_getVirtualBox(),
5212 static_cast<IMachine*>(this) /* aInitiator */,
5213 tr("Deleting files"),
5214 true /* fCancellable */,
5215 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5216 tr("Collecting file inventory"));
5217 if (FAILED(rc))
5218 return rc;
5219
5220 /* create and start the task on a separate thread (note that it will not
5221 * start working until we release alock) */
5222 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5223 rc = pTask->createThread();
5224 pTask = NULL;
5225 if (FAILED(rc))
5226 return rc;
5227
5228 pProgress.queryInterfaceTo(aProgress.asOutParam());
5229
5230 LogFlowFuncLeave();
5231
5232 return S_OK;
5233}
5234
5235HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5236{
5237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5238
5239 ComObjPtr<Snapshot> pSnapshot;
5240 HRESULT rc;
5241
5242 if (aNameOrId.isEmpty())
5243 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5244 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5245 else
5246 {
5247 Guid uuid(aNameOrId);
5248 if (uuid.isValid())
5249 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5250 else
5251 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5252 }
5253 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5254
5255 return rc;
5256}
5257
5258HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5259 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5260{
5261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5262
5263 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5264 if (FAILED(rc)) return rc;
5265
5266 ComObjPtr<SharedFolder> sharedFolder;
5267 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5268 if (SUCCEEDED(rc))
5269 return setError(VBOX_E_OBJECT_IN_USE,
5270 tr("Shared folder named '%s' already exists"),
5271 aName.c_str());
5272
5273 sharedFolder.createObject();
5274 rc = sharedFolder->init(i_getMachine(),
5275 aName,
5276 aHostPath,
5277 !!aWritable,
5278 !!aAutomount,
5279 aAutoMountPoint,
5280 true /* fFailOnError */);
5281 if (FAILED(rc)) return rc;
5282
5283 i_setModified(IsModified_SharedFolders);
5284 mHWData.backup();
5285 mHWData->mSharedFolders.push_back(sharedFolder);
5286
5287 /* inform the direct session if any */
5288 alock.release();
5289 i_onSharedFolderChange();
5290
5291 return S_OK;
5292}
5293
5294HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5295{
5296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5297
5298 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5299 if (FAILED(rc)) return rc;
5300
5301 ComObjPtr<SharedFolder> sharedFolder;
5302 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5303 if (FAILED(rc)) return rc;
5304
5305 i_setModified(IsModified_SharedFolders);
5306 mHWData.backup();
5307 mHWData->mSharedFolders.remove(sharedFolder);
5308
5309 /* inform the direct session if any */
5310 alock.release();
5311 i_onSharedFolderChange();
5312
5313 return S_OK;
5314}
5315
5316HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5317{
5318 /* start with No */
5319 *aCanShow = FALSE;
5320
5321 ComPtr<IInternalSessionControl> directControl;
5322 {
5323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5324
5325 if (mData->mSession.mState != SessionState_Locked)
5326 return setError(VBOX_E_INVALID_VM_STATE,
5327 tr("Machine is not locked for session (session state: %s)"),
5328 Global::stringifySessionState(mData->mSession.mState));
5329
5330 if (mData->mSession.mLockType == LockType_VM)
5331 directControl = mData->mSession.mDirectControl;
5332 }
5333
5334 /* ignore calls made after #OnSessionEnd() is called */
5335 if (!directControl)
5336 return S_OK;
5337
5338 LONG64 dummy;
5339 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5340}
5341
5342HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5343{
5344 ComPtr<IInternalSessionControl> directControl;
5345 {
5346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5347
5348 if (mData->mSession.mState != SessionState_Locked)
5349 return setError(E_FAIL,
5350 tr("Machine is not locked for session (session state: %s)"),
5351 Global::stringifySessionState(mData->mSession.mState));
5352
5353 if (mData->mSession.mLockType == LockType_VM)
5354 directControl = mData->mSession.mDirectControl;
5355 }
5356
5357 /* ignore calls made after #OnSessionEnd() is called */
5358 if (!directControl)
5359 return S_OK;
5360
5361 BOOL dummy;
5362 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5363}
5364
5365#ifdef VBOX_WITH_GUEST_PROPS
5366/**
5367 * Look up a guest property in VBoxSVC's internal structures.
5368 */
5369HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5370 com::Utf8Str &aValue,
5371 LONG64 *aTimestamp,
5372 com::Utf8Str &aFlags) const
5373{
5374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5375
5376 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5377 if (it != mHWData->mGuestProperties.end())
5378 {
5379 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5380 aValue = it->second.strValue;
5381 *aTimestamp = it->second.mTimestamp;
5382 GuestPropWriteFlags(it->second.mFlags, szFlags);
5383 aFlags = Utf8Str(szFlags);
5384 }
5385
5386 return S_OK;
5387}
5388
5389/**
5390 * Query the VM that a guest property belongs to for the property.
5391 * @returns E_ACCESSDENIED if the VM process is not available or not
5392 * currently handling queries and the lookup should then be done in
5393 * VBoxSVC.
5394 */
5395HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5396 com::Utf8Str &aValue,
5397 LONG64 *aTimestamp,
5398 com::Utf8Str &aFlags) const
5399{
5400 HRESULT rc = S_OK;
5401 Bstr bstrValue;
5402 Bstr bstrFlags;
5403
5404 ComPtr<IInternalSessionControl> directControl;
5405 {
5406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5407 if (mData->mSession.mLockType == LockType_VM)
5408 directControl = mData->mSession.mDirectControl;
5409 }
5410
5411 /* ignore calls made after #OnSessionEnd() is called */
5412 if (!directControl)
5413 rc = E_ACCESSDENIED;
5414 else
5415 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5416 0 /* accessMode */,
5417 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5418
5419 aValue = bstrValue;
5420 aFlags = bstrFlags;
5421
5422 return rc;
5423}
5424#endif // VBOX_WITH_GUEST_PROPS
5425
5426HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5427 com::Utf8Str &aValue,
5428 LONG64 *aTimestamp,
5429 com::Utf8Str &aFlags)
5430{
5431#ifndef VBOX_WITH_GUEST_PROPS
5432 ReturnComNotImplemented();
5433#else // VBOX_WITH_GUEST_PROPS
5434
5435 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5436
5437 if (rc == E_ACCESSDENIED)
5438 /* The VM is not running or the service is not (yet) accessible */
5439 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5440 return rc;
5441#endif // VBOX_WITH_GUEST_PROPS
5442}
5443
5444HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5445{
5446 LONG64 dummyTimestamp;
5447 com::Utf8Str dummyFlags;
5448 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5449 return rc;
5450
5451}
5452HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5453{
5454 com::Utf8Str dummyFlags;
5455 com::Utf8Str dummyValue;
5456 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5457 return rc;
5458}
5459
5460#ifdef VBOX_WITH_GUEST_PROPS
5461/**
5462 * Set a guest property in VBoxSVC's internal structures.
5463 */
5464HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5465 const com::Utf8Str &aFlags, bool fDelete)
5466{
5467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5468 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5469 if (FAILED(rc)) return rc;
5470
5471 try
5472 {
5473 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5474 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5475 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5476
5477 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5478 if (it == mHWData->mGuestProperties.end())
5479 {
5480 if (!fDelete)
5481 {
5482 i_setModified(IsModified_MachineData);
5483 mHWData.backupEx();
5484
5485 RTTIMESPEC time;
5486 HWData::GuestProperty prop;
5487 prop.strValue = Bstr(aValue).raw();
5488 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5489 prop.mFlags = fFlags;
5490 mHWData->mGuestProperties[aName] = prop;
5491 }
5492 }
5493 else
5494 {
5495 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5496 {
5497 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5498 }
5499 else
5500 {
5501 i_setModified(IsModified_MachineData);
5502 mHWData.backupEx();
5503
5504 /* The backupEx() operation invalidates our iterator,
5505 * so get a new one. */
5506 it = mHWData->mGuestProperties.find(aName);
5507 Assert(it != mHWData->mGuestProperties.end());
5508
5509 if (!fDelete)
5510 {
5511 RTTIMESPEC time;
5512 it->second.strValue = aValue;
5513 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5514 it->second.mFlags = fFlags;
5515 }
5516 else
5517 mHWData->mGuestProperties.erase(it);
5518 }
5519 }
5520
5521 if (SUCCEEDED(rc))
5522 {
5523 alock.release();
5524
5525 mParent->i_onGuestPropertyChange(mData->mUuid,
5526 Bstr(aName).raw(),
5527 Bstr(aValue).raw(),
5528 Bstr(aFlags).raw());
5529 }
5530 }
5531 catch (std::bad_alloc &)
5532 {
5533 rc = E_OUTOFMEMORY;
5534 }
5535
5536 return rc;
5537}
5538
5539/**
5540 * Set a property on the VM that that property belongs to.
5541 * @returns E_ACCESSDENIED if the VM process is not available or not
5542 * currently handling queries and the setting should then be done in
5543 * VBoxSVC.
5544 */
5545HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5546 const com::Utf8Str &aFlags, bool fDelete)
5547{
5548 HRESULT rc;
5549
5550 try
5551 {
5552 ComPtr<IInternalSessionControl> directControl;
5553 {
5554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5555 if (mData->mSession.mLockType == LockType_VM)
5556 directControl = mData->mSession.mDirectControl;
5557 }
5558
5559 Bstr dummy1; /* will not be changed (setter) */
5560 Bstr dummy2; /* will not be changed (setter) */
5561 LONG64 dummy64;
5562 if (!directControl)
5563 rc = E_ACCESSDENIED;
5564 else
5565 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5566 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5567 fDelete ? 2 : 1 /* accessMode */,
5568 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5569 }
5570 catch (std::bad_alloc &)
5571 {
5572 rc = E_OUTOFMEMORY;
5573 }
5574
5575 return rc;
5576}
5577#endif // VBOX_WITH_GUEST_PROPS
5578
5579HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5580 const com::Utf8Str &aFlags)
5581{
5582#ifndef VBOX_WITH_GUEST_PROPS
5583 ReturnComNotImplemented();
5584#else // VBOX_WITH_GUEST_PROPS
5585 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5586 if (rc == E_ACCESSDENIED)
5587 /* The VM is not running or the service is not (yet) accessible */
5588 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5589 return rc;
5590#endif // VBOX_WITH_GUEST_PROPS
5591}
5592
5593HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5594{
5595 return setGuestProperty(aProperty, aValue, "");
5596}
5597
5598HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5599{
5600#ifndef VBOX_WITH_GUEST_PROPS
5601 ReturnComNotImplemented();
5602#else // VBOX_WITH_GUEST_PROPS
5603 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5604 if (rc == E_ACCESSDENIED)
5605 /* The VM is not running or the service is not (yet) accessible */
5606 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5607 return rc;
5608#endif // VBOX_WITH_GUEST_PROPS
5609}
5610
5611#ifdef VBOX_WITH_GUEST_PROPS
5612/**
5613 * Enumerate the guest properties in VBoxSVC's internal structures.
5614 */
5615HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5616 std::vector<com::Utf8Str> &aNames,
5617 std::vector<com::Utf8Str> &aValues,
5618 std::vector<LONG64> &aTimestamps,
5619 std::vector<com::Utf8Str> &aFlags)
5620{
5621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5622 Utf8Str strPatterns(aPatterns);
5623
5624 /*
5625 * Look for matching patterns and build up a list.
5626 */
5627 HWData::GuestPropertyMap propMap;
5628 for (HWData::GuestPropertyMap::const_iterator
5629 it = mHWData->mGuestProperties.begin();
5630 it != mHWData->mGuestProperties.end();
5631 ++it)
5632 {
5633 if ( strPatterns.isEmpty()
5634 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5635 RTSTR_MAX,
5636 it->first.c_str(),
5637 RTSTR_MAX,
5638 NULL)
5639 )
5640 propMap.insert(*it);
5641 }
5642
5643 alock.release();
5644
5645 /*
5646 * And build up the arrays for returning the property information.
5647 */
5648 size_t cEntries = propMap.size();
5649
5650 aNames.resize(cEntries);
5651 aValues.resize(cEntries);
5652 aTimestamps.resize(cEntries);
5653 aFlags.resize(cEntries);
5654
5655 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5656 size_t i = 0;
5657 for (HWData::GuestPropertyMap::const_iterator
5658 it = propMap.begin();
5659 it != propMap.end();
5660 ++it, ++i)
5661 {
5662 aNames[i] = it->first;
5663 aValues[i] = it->second.strValue;
5664 aTimestamps[i] = it->second.mTimestamp;
5665 GuestPropWriteFlags(it->second.mFlags, szFlags);
5666 aFlags[i] = Utf8Str(szFlags);
5667 }
5668
5669 return S_OK;
5670}
5671
5672/**
5673 * Enumerate the properties managed by a VM.
5674 * @returns E_ACCESSDENIED if the VM process is not available or not
5675 * currently handling queries and the setting should then be done in
5676 * VBoxSVC.
5677 */
5678HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5679 std::vector<com::Utf8Str> &aNames,
5680 std::vector<com::Utf8Str> &aValues,
5681 std::vector<LONG64> &aTimestamps,
5682 std::vector<com::Utf8Str> &aFlags)
5683{
5684 HRESULT rc;
5685 ComPtr<IInternalSessionControl> directControl;
5686 {
5687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5688 if (mData->mSession.mLockType == LockType_VM)
5689 directControl = mData->mSession.mDirectControl;
5690 }
5691
5692 com::SafeArray<BSTR> bNames;
5693 com::SafeArray<BSTR> bValues;
5694 com::SafeArray<LONG64> bTimestamps;
5695 com::SafeArray<BSTR> bFlags;
5696
5697 if (!directControl)
5698 rc = E_ACCESSDENIED;
5699 else
5700 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5701 ComSafeArrayAsOutParam(bNames),
5702 ComSafeArrayAsOutParam(bValues),
5703 ComSafeArrayAsOutParam(bTimestamps),
5704 ComSafeArrayAsOutParam(bFlags));
5705 size_t i;
5706 aNames.resize(bNames.size());
5707 for (i = 0; i < bNames.size(); ++i)
5708 aNames[i] = Utf8Str(bNames[i]);
5709 aValues.resize(bValues.size());
5710 for (i = 0; i < bValues.size(); ++i)
5711 aValues[i] = Utf8Str(bValues[i]);
5712 aTimestamps.resize(bTimestamps.size());
5713 for (i = 0; i < bTimestamps.size(); ++i)
5714 aTimestamps[i] = bTimestamps[i];
5715 aFlags.resize(bFlags.size());
5716 for (i = 0; i < bFlags.size(); ++i)
5717 aFlags[i] = Utf8Str(bFlags[i]);
5718
5719 return rc;
5720}
5721#endif // VBOX_WITH_GUEST_PROPS
5722HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5723 std::vector<com::Utf8Str> &aNames,
5724 std::vector<com::Utf8Str> &aValues,
5725 std::vector<LONG64> &aTimestamps,
5726 std::vector<com::Utf8Str> &aFlags)
5727{
5728#ifndef VBOX_WITH_GUEST_PROPS
5729 ReturnComNotImplemented();
5730#else // VBOX_WITH_GUEST_PROPS
5731
5732 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5733
5734 if (rc == E_ACCESSDENIED)
5735 /* The VM is not running or the service is not (yet) accessible */
5736 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5737 return rc;
5738#endif // VBOX_WITH_GUEST_PROPS
5739}
5740
5741HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5742 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5743{
5744 MediumAttachmentList atts;
5745
5746 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5747 if (FAILED(rc)) return rc;
5748
5749 aMediumAttachments.resize(atts.size());
5750 size_t i = 0;
5751 for (MediumAttachmentList::const_iterator
5752 it = atts.begin();
5753 it != atts.end();
5754 ++it, ++i)
5755 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5756
5757 return S_OK;
5758}
5759
5760HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5761 LONG aControllerPort,
5762 LONG aDevice,
5763 ComPtr<IMediumAttachment> &aAttachment)
5764{
5765 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5766 aName.c_str(), aControllerPort, aDevice));
5767
5768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5769
5770 aAttachment = NULL;
5771
5772 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5773 aName,
5774 aControllerPort,
5775 aDevice);
5776 if (pAttach.isNull())
5777 return setError(VBOX_E_OBJECT_NOT_FOUND,
5778 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5779 aDevice, aControllerPort, aName.c_str());
5780
5781 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5782
5783 return S_OK;
5784}
5785
5786
5787HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5788 StorageBus_T aConnectionType,
5789 ComPtr<IStorageController> &aController)
5790{
5791 if ( (aConnectionType <= StorageBus_Null)
5792 || (aConnectionType > StorageBus_VirtioSCSI))
5793 return setError(E_INVALIDARG,
5794 tr("Invalid connection type: %d"),
5795 aConnectionType);
5796
5797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5798
5799 HRESULT rc = i_checkStateDependency(MutableStateDep);
5800 if (FAILED(rc)) return rc;
5801
5802 /* try to find one with the name first. */
5803 ComObjPtr<StorageController> ctrl;
5804
5805 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5806 if (SUCCEEDED(rc))
5807 return setError(VBOX_E_OBJECT_IN_USE,
5808 tr("Storage controller named '%s' already exists"),
5809 aName.c_str());
5810
5811 ctrl.createObject();
5812
5813 /* get a new instance number for the storage controller */
5814 ULONG ulInstance = 0;
5815 bool fBootable = true;
5816 for (StorageControllerList::const_iterator
5817 it = mStorageControllers->begin();
5818 it != mStorageControllers->end();
5819 ++it)
5820 {
5821 if ((*it)->i_getStorageBus() == aConnectionType)
5822 {
5823 ULONG ulCurInst = (*it)->i_getInstance();
5824
5825 if (ulCurInst >= ulInstance)
5826 ulInstance = ulCurInst + 1;
5827
5828 /* Only one controller of each type can be marked as bootable. */
5829 if ((*it)->i_getBootable())
5830 fBootable = false;
5831 }
5832 }
5833
5834 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5835 if (FAILED(rc)) return rc;
5836
5837 i_setModified(IsModified_Storage);
5838 mStorageControllers.backup();
5839 mStorageControllers->push_back(ctrl);
5840
5841 ctrl.queryInterfaceTo(aController.asOutParam());
5842
5843 /* inform the direct session if any */
5844 alock.release();
5845 i_onStorageControllerChange(i_getId(), aName);
5846
5847 return S_OK;
5848}
5849
5850HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5851 ComPtr<IStorageController> &aStorageController)
5852{
5853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5854
5855 ComObjPtr<StorageController> ctrl;
5856
5857 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5858 if (SUCCEEDED(rc))
5859 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5860
5861 return rc;
5862}
5863
5864HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5865 ULONG aInstance,
5866 ComPtr<IStorageController> &aStorageController)
5867{
5868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5869
5870 for (StorageControllerList::const_iterator
5871 it = mStorageControllers->begin();
5872 it != mStorageControllers->end();
5873 ++it)
5874 {
5875 if ( (*it)->i_getStorageBus() == aConnectionType
5876 && (*it)->i_getInstance() == aInstance)
5877 {
5878 (*it).queryInterfaceTo(aStorageController.asOutParam());
5879 return S_OK;
5880 }
5881 }
5882
5883 return setError(VBOX_E_OBJECT_NOT_FOUND,
5884 tr("Could not find a storage controller with instance number '%lu'"),
5885 aInstance);
5886}
5887
5888HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5889{
5890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5891
5892 HRESULT rc = i_checkStateDependency(MutableStateDep);
5893 if (FAILED(rc)) return rc;
5894
5895 ComObjPtr<StorageController> ctrl;
5896
5897 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5898 if (SUCCEEDED(rc))
5899 {
5900 /* Ensure that only one controller of each type is marked as bootable. */
5901 if (aBootable == TRUE)
5902 {
5903 for (StorageControllerList::const_iterator
5904 it = mStorageControllers->begin();
5905 it != mStorageControllers->end();
5906 ++it)
5907 {
5908 ComObjPtr<StorageController> aCtrl = (*it);
5909
5910 if ( (aCtrl->i_getName() != aName)
5911 && aCtrl->i_getBootable() == TRUE
5912 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5913 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5914 {
5915 aCtrl->i_setBootable(FALSE);
5916 break;
5917 }
5918 }
5919 }
5920
5921 if (SUCCEEDED(rc))
5922 {
5923 ctrl->i_setBootable(aBootable);
5924 i_setModified(IsModified_Storage);
5925 }
5926 }
5927
5928 if (SUCCEEDED(rc))
5929 {
5930 /* inform the direct session if any */
5931 alock.release();
5932 i_onStorageControllerChange(i_getId(), aName);
5933 }
5934
5935 return rc;
5936}
5937
5938HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5939{
5940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5941
5942 HRESULT rc = i_checkStateDependency(MutableStateDep);
5943 if (FAILED(rc)) return rc;
5944
5945 ComObjPtr<StorageController> ctrl;
5946 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5947 if (FAILED(rc)) return rc;
5948
5949 MediumAttachmentList llDetachedAttachments;
5950 {
5951 /* find all attached devices to the appropriate storage controller and detach them all */
5952 // make a temporary list because detachDevice invalidates iterators into
5953 // mMediumAttachments
5954 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5955
5956 for (MediumAttachmentList::const_iterator
5957 it = llAttachments2.begin();
5958 it != llAttachments2.end();
5959 ++it)
5960 {
5961 MediumAttachment *pAttachTemp = *it;
5962
5963 AutoCaller localAutoCaller(pAttachTemp);
5964 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5965
5966 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5967
5968 if (pAttachTemp->i_getControllerName() == aName)
5969 {
5970 llDetachedAttachments.push_back(pAttachTemp);
5971 rc = i_detachDevice(pAttachTemp, alock, NULL);
5972 if (FAILED(rc)) return rc;
5973 }
5974 }
5975 }
5976
5977 /* send event about detached devices before removing parent controller */
5978 for (MediumAttachmentList::const_iterator
5979 it = llDetachedAttachments.begin();
5980 it != llDetachedAttachments.end();
5981 ++it)
5982 {
5983 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5984 }
5985
5986 /* We can remove it now. */
5987 i_setModified(IsModified_Storage);
5988 mStorageControllers.backup();
5989
5990 ctrl->i_unshare();
5991
5992 mStorageControllers->remove(ctrl);
5993
5994 /* inform the direct session if any */
5995 alock.release();
5996 i_onStorageControllerChange(i_getId(), aName);
5997
5998 return S_OK;
5999}
6000
6001HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6002 ComPtr<IUSBController> &aController)
6003{
6004 if ( (aType <= USBControllerType_Null)
6005 || (aType >= USBControllerType_Last))
6006 return setError(E_INVALIDARG,
6007 tr("Invalid USB controller type: %d"),
6008 aType);
6009
6010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6011
6012 HRESULT rc = i_checkStateDependency(MutableStateDep);
6013 if (FAILED(rc)) return rc;
6014
6015 /* try to find one with the same type first. */
6016 ComObjPtr<USBController> ctrl;
6017
6018 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6019 if (SUCCEEDED(rc))
6020 return setError(VBOX_E_OBJECT_IN_USE,
6021 tr("USB controller named '%s' already exists"),
6022 aName.c_str());
6023
6024 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6025 ULONG maxInstances;
6026 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6027 if (FAILED(rc))
6028 return rc;
6029
6030 ULONG cInstances = i_getUSBControllerCountByType(aType);
6031 if (cInstances >= maxInstances)
6032 return setError(E_INVALIDARG,
6033 tr("Too many USB controllers of this type"));
6034
6035 ctrl.createObject();
6036
6037 rc = ctrl->init(this, aName, aType);
6038 if (FAILED(rc)) return rc;
6039
6040 i_setModified(IsModified_USB);
6041 mUSBControllers.backup();
6042 mUSBControllers->push_back(ctrl);
6043
6044 ctrl.queryInterfaceTo(aController.asOutParam());
6045
6046 /* inform the direct session if any */
6047 alock.release();
6048 i_onUSBControllerChange();
6049
6050 return S_OK;
6051}
6052
6053HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6054{
6055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6056
6057 ComObjPtr<USBController> ctrl;
6058
6059 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6060 if (SUCCEEDED(rc))
6061 ctrl.queryInterfaceTo(aController.asOutParam());
6062
6063 return rc;
6064}
6065
6066HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6067 ULONG *aControllers)
6068{
6069 if ( (aType <= USBControllerType_Null)
6070 || (aType >= USBControllerType_Last))
6071 return setError(E_INVALIDARG,
6072 tr("Invalid USB controller type: %d"),
6073 aType);
6074
6075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6076
6077 ComObjPtr<USBController> ctrl;
6078
6079 *aControllers = i_getUSBControllerCountByType(aType);
6080
6081 return S_OK;
6082}
6083
6084HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6085{
6086
6087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6088
6089 HRESULT rc = i_checkStateDependency(MutableStateDep);
6090 if (FAILED(rc)) return rc;
6091
6092 ComObjPtr<USBController> ctrl;
6093 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6094 if (FAILED(rc)) return rc;
6095
6096 i_setModified(IsModified_USB);
6097 mUSBControllers.backup();
6098
6099 ctrl->i_unshare();
6100
6101 mUSBControllers->remove(ctrl);
6102
6103 /* inform the direct session if any */
6104 alock.release();
6105 i_onUSBControllerChange();
6106
6107 return S_OK;
6108}
6109
6110HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6111 ULONG *aOriginX,
6112 ULONG *aOriginY,
6113 ULONG *aWidth,
6114 ULONG *aHeight,
6115 BOOL *aEnabled)
6116{
6117 uint32_t u32OriginX= 0;
6118 uint32_t u32OriginY= 0;
6119 uint32_t u32Width = 0;
6120 uint32_t u32Height = 0;
6121 uint16_t u16Flags = 0;
6122
6123 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6124 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6125 if (RT_FAILURE(vrc))
6126 {
6127#ifdef RT_OS_WINDOWS
6128 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6129 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6130 * So just assign fEnable to TRUE again.
6131 * The right fix would be to change GUI API wrappers to make sure that parameters
6132 * are changed only if API succeeds.
6133 */
6134 *aEnabled = TRUE;
6135#endif
6136 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6137 tr("Saved guest size is not available (%Rrc)"),
6138 vrc);
6139 }
6140
6141 *aOriginX = u32OriginX;
6142 *aOriginY = u32OriginY;
6143 *aWidth = u32Width;
6144 *aHeight = u32Height;
6145 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6146
6147 return S_OK;
6148}
6149
6150HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6151 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6152{
6153 if (aScreenId != 0)
6154 return E_NOTIMPL;
6155
6156 if ( aBitmapFormat != BitmapFormat_BGR0
6157 && aBitmapFormat != BitmapFormat_BGRA
6158 && aBitmapFormat != BitmapFormat_RGBA
6159 && aBitmapFormat != BitmapFormat_PNG)
6160 return setError(E_NOTIMPL,
6161 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6162
6163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6164
6165 uint8_t *pu8Data = NULL;
6166 uint32_t cbData = 0;
6167 uint32_t u32Width = 0;
6168 uint32_t u32Height = 0;
6169
6170 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6171
6172 if (RT_FAILURE(vrc))
6173 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6174 tr("Saved thumbnail data is not available (%Rrc)"),
6175 vrc);
6176
6177 HRESULT hr = S_OK;
6178
6179 *aWidth = u32Width;
6180 *aHeight = u32Height;
6181
6182 if (cbData > 0)
6183 {
6184 /* Convert pixels to the format expected by the API caller. */
6185 if (aBitmapFormat == BitmapFormat_BGR0)
6186 {
6187 /* [0] B, [1] G, [2] R, [3] 0. */
6188 aData.resize(cbData);
6189 memcpy(&aData.front(), pu8Data, cbData);
6190 }
6191 else if (aBitmapFormat == BitmapFormat_BGRA)
6192 {
6193 /* [0] B, [1] G, [2] R, [3] A. */
6194 aData.resize(cbData);
6195 for (uint32_t i = 0; i < cbData; i += 4)
6196 {
6197 aData[i] = pu8Data[i];
6198 aData[i + 1] = pu8Data[i + 1];
6199 aData[i + 2] = pu8Data[i + 2];
6200 aData[i + 3] = 0xff;
6201 }
6202 }
6203 else if (aBitmapFormat == BitmapFormat_RGBA)
6204 {
6205 /* [0] R, [1] G, [2] B, [3] A. */
6206 aData.resize(cbData);
6207 for (uint32_t i = 0; i < cbData; i += 4)
6208 {
6209 aData[i] = pu8Data[i + 2];
6210 aData[i + 1] = pu8Data[i + 1];
6211 aData[i + 2] = pu8Data[i];
6212 aData[i + 3] = 0xff;
6213 }
6214 }
6215 else if (aBitmapFormat == BitmapFormat_PNG)
6216 {
6217 uint8_t *pu8PNG = NULL;
6218 uint32_t cbPNG = 0;
6219 uint32_t cxPNG = 0;
6220 uint32_t cyPNG = 0;
6221
6222 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6223
6224 if (RT_SUCCESS(vrc))
6225 {
6226 aData.resize(cbPNG);
6227 if (cbPNG)
6228 memcpy(&aData.front(), pu8PNG, cbPNG);
6229 }
6230 else
6231 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6232 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6233 vrc);
6234
6235 RTMemFree(pu8PNG);
6236 }
6237 }
6238
6239 freeSavedDisplayScreenshot(pu8Data);
6240
6241 return hr;
6242}
6243
6244HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6245 ULONG *aWidth,
6246 ULONG *aHeight,
6247 std::vector<BitmapFormat_T> &aBitmapFormats)
6248{
6249 if (aScreenId != 0)
6250 return E_NOTIMPL;
6251
6252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6253
6254 uint8_t *pu8Data = NULL;
6255 uint32_t cbData = 0;
6256 uint32_t u32Width = 0;
6257 uint32_t u32Height = 0;
6258
6259 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6260
6261 if (RT_FAILURE(vrc))
6262 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6263 tr("Saved screenshot data is not available (%Rrc)"),
6264 vrc);
6265
6266 *aWidth = u32Width;
6267 *aHeight = u32Height;
6268 aBitmapFormats.resize(1);
6269 aBitmapFormats[0] = BitmapFormat_PNG;
6270
6271 freeSavedDisplayScreenshot(pu8Data);
6272
6273 return S_OK;
6274}
6275
6276HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6277 BitmapFormat_T aBitmapFormat,
6278 ULONG *aWidth,
6279 ULONG *aHeight,
6280 std::vector<BYTE> &aData)
6281{
6282 if (aScreenId != 0)
6283 return E_NOTIMPL;
6284
6285 if (aBitmapFormat != BitmapFormat_PNG)
6286 return E_NOTIMPL;
6287
6288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6289
6290 uint8_t *pu8Data = NULL;
6291 uint32_t cbData = 0;
6292 uint32_t u32Width = 0;
6293 uint32_t u32Height = 0;
6294
6295 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6296
6297 if (RT_FAILURE(vrc))
6298 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6299 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6300 vrc);
6301
6302 *aWidth = u32Width;
6303 *aHeight = u32Height;
6304
6305 aData.resize(cbData);
6306 if (cbData)
6307 memcpy(&aData.front(), pu8Data, cbData);
6308
6309 freeSavedDisplayScreenshot(pu8Data);
6310
6311 return S_OK;
6312}
6313
6314HRESULT Machine::hotPlugCPU(ULONG aCpu)
6315{
6316 HRESULT rc = S_OK;
6317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6318
6319 if (!mHWData->mCPUHotPlugEnabled)
6320 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6321
6322 if (aCpu >= mHWData->mCPUCount)
6323 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6324
6325 if (mHWData->mCPUAttached[aCpu])
6326 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6327
6328 alock.release();
6329 rc = i_onCPUChange(aCpu, false);
6330 alock.acquire();
6331 if (FAILED(rc)) return rc;
6332
6333 i_setModified(IsModified_MachineData);
6334 mHWData.backup();
6335 mHWData->mCPUAttached[aCpu] = true;
6336
6337 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6338 if (Global::IsOnline(mData->mMachineState))
6339 i_saveSettings(NULL);
6340
6341 return S_OK;
6342}
6343
6344HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6345{
6346 HRESULT rc = S_OK;
6347
6348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6349
6350 if (!mHWData->mCPUHotPlugEnabled)
6351 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6352
6353 if (aCpu >= SchemaDefs::MaxCPUCount)
6354 return setError(E_INVALIDARG,
6355 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6356 SchemaDefs::MaxCPUCount);
6357
6358 if (!mHWData->mCPUAttached[aCpu])
6359 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6360
6361 /* CPU 0 can't be detached */
6362 if (aCpu == 0)
6363 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6364
6365 alock.release();
6366 rc = i_onCPUChange(aCpu, true);
6367 alock.acquire();
6368 if (FAILED(rc)) return rc;
6369
6370 i_setModified(IsModified_MachineData);
6371 mHWData.backup();
6372 mHWData->mCPUAttached[aCpu] = false;
6373
6374 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6375 if (Global::IsOnline(mData->mMachineState))
6376 i_saveSettings(NULL);
6377
6378 return S_OK;
6379}
6380
6381HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6382{
6383 *aAttached = false;
6384
6385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6386
6387 /* If hotplug is enabled the CPU is always enabled. */
6388 if (!mHWData->mCPUHotPlugEnabled)
6389 {
6390 if (aCpu < mHWData->mCPUCount)
6391 *aAttached = true;
6392 }
6393 else
6394 {
6395 if (aCpu < SchemaDefs::MaxCPUCount)
6396 *aAttached = mHWData->mCPUAttached[aCpu];
6397 }
6398
6399 return S_OK;
6400}
6401
6402HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6403{
6404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6405
6406 Utf8Str log = i_getLogFilename(aIdx);
6407 if (!RTFileExists(log.c_str()))
6408 log.setNull();
6409 aFilename = log;
6410
6411 return S_OK;
6412}
6413
6414HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6415{
6416 if (aSize < 0)
6417 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6418
6419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6420
6421 HRESULT rc = S_OK;
6422 Utf8Str log = i_getLogFilename(aIdx);
6423
6424 /* do not unnecessarily hold the lock while doing something which does
6425 * not need the lock and potentially takes a long time. */
6426 alock.release();
6427
6428 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6429 * keeps the SOAP reply size under 1M for the webservice (we're using
6430 * base64 encoded strings for binary data for years now, avoiding the
6431 * expansion of each byte array element to approx. 25 bytes of XML. */
6432 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6433 aData.resize(cbData);
6434
6435 RTFILE LogFile;
6436 int vrc = RTFileOpen(&LogFile, log.c_str(),
6437 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6438 if (RT_SUCCESS(vrc))
6439 {
6440 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6441 if (RT_SUCCESS(vrc))
6442 aData.resize(cbData);
6443 else
6444 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6445 tr("Could not read log file '%s' (%Rrc)"),
6446 log.c_str(), vrc);
6447 RTFileClose(LogFile);
6448 }
6449 else
6450 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6451 tr("Could not open log file '%s' (%Rrc)"),
6452 log.c_str(), vrc);
6453
6454 if (FAILED(rc))
6455 aData.resize(0);
6456
6457 return rc;
6458}
6459
6460
6461/**
6462 * Currently this method doesn't attach device to the running VM,
6463 * just makes sure it's plugged on next VM start.
6464 */
6465HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6466{
6467 // lock scope
6468 {
6469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6470
6471 HRESULT rc = i_checkStateDependency(MutableStateDep);
6472 if (FAILED(rc)) return rc;
6473
6474 ChipsetType_T aChipset = ChipsetType_PIIX3;
6475 COMGETTER(ChipsetType)(&aChipset);
6476
6477 if (aChipset != ChipsetType_ICH9)
6478 {
6479 return setError(E_INVALIDARG,
6480 tr("Host PCI attachment only supported with ICH9 chipset"));
6481 }
6482
6483 // check if device with this host PCI address already attached
6484 for (HWData::PCIDeviceAssignmentList::const_iterator
6485 it = mHWData->mPCIDeviceAssignments.begin();
6486 it != mHWData->mPCIDeviceAssignments.end();
6487 ++it)
6488 {
6489 LONG iHostAddress = -1;
6490 ComPtr<PCIDeviceAttachment> pAttach;
6491 pAttach = *it;
6492 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6493 if (iHostAddress == aHostAddress)
6494 return setError(E_INVALIDARG,
6495 tr("Device with host PCI address already attached to this VM"));
6496 }
6497
6498 ComObjPtr<PCIDeviceAttachment> pda;
6499 char name[32];
6500
6501 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6502 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6503 pda.createObject();
6504 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6505 i_setModified(IsModified_MachineData);
6506 mHWData.backup();
6507 mHWData->mPCIDeviceAssignments.push_back(pda);
6508 }
6509
6510 return S_OK;
6511}
6512
6513/**
6514 * Currently this method doesn't detach device from the running VM,
6515 * just makes sure it's not plugged on next VM start.
6516 */
6517HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6518{
6519 ComObjPtr<PCIDeviceAttachment> pAttach;
6520 bool fRemoved = false;
6521 HRESULT rc;
6522
6523 // lock scope
6524 {
6525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6526
6527 rc = i_checkStateDependency(MutableStateDep);
6528 if (FAILED(rc)) return rc;
6529
6530 for (HWData::PCIDeviceAssignmentList::const_iterator
6531 it = mHWData->mPCIDeviceAssignments.begin();
6532 it != mHWData->mPCIDeviceAssignments.end();
6533 ++it)
6534 {
6535 LONG iHostAddress = -1;
6536 pAttach = *it;
6537 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6538 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6539 {
6540 i_setModified(IsModified_MachineData);
6541 mHWData.backup();
6542 mHWData->mPCIDeviceAssignments.remove(pAttach);
6543 fRemoved = true;
6544 break;
6545 }
6546 }
6547 }
6548
6549
6550 /* Fire event outside of the lock */
6551 if (fRemoved)
6552 {
6553 Assert(!pAttach.isNull());
6554 ComPtr<IEventSource> es;
6555 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6556 Assert(SUCCEEDED(rc));
6557 Bstr mid;
6558 rc = this->COMGETTER(Id)(mid.asOutParam());
6559 Assert(SUCCEEDED(rc));
6560 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6561 }
6562
6563 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6564 tr("No host PCI device %08x attached"),
6565 aHostAddress
6566 );
6567}
6568
6569HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6570{
6571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6572
6573 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6574 size_t i = 0;
6575 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6576 it = mHWData->mPCIDeviceAssignments.begin();
6577 it != mHWData->mPCIDeviceAssignments.end();
6578 ++it, ++i)
6579 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6580
6581 return S_OK;
6582}
6583
6584HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6585{
6586 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6587
6588 return S_OK;
6589}
6590
6591HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6592{
6593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6594
6595 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6596
6597 return S_OK;
6598}
6599
6600HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6601{
6602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6603 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6604 if (SUCCEEDED(hrc))
6605 {
6606 hrc = mHWData.backupEx();
6607 if (SUCCEEDED(hrc))
6608 {
6609 i_setModified(IsModified_MachineData);
6610 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6611 }
6612 }
6613 return hrc;
6614}
6615
6616HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6617{
6618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6619 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6620 return S_OK;
6621}
6622
6623HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6624{
6625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6626 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6627 if (SUCCEEDED(hrc))
6628 {
6629 hrc = mHWData.backupEx();
6630 if (SUCCEEDED(hrc))
6631 {
6632 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6633 if (SUCCEEDED(hrc))
6634 i_setModified(IsModified_MachineData);
6635 }
6636 }
6637 return hrc;
6638}
6639
6640HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6641{
6642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6643
6644 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6645
6646 return S_OK;
6647}
6648
6649HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6650{
6651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6652 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6653 if (SUCCEEDED(hrc))
6654 {
6655 hrc = mHWData.backupEx();
6656 if (SUCCEEDED(hrc))
6657 {
6658 i_setModified(IsModified_MachineData);
6659 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6660 }
6661 }
6662 return hrc;
6663}
6664
6665HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6666{
6667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6668
6669 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6670
6671 return S_OK;
6672}
6673
6674HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6675{
6676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6677
6678 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6679 if ( SUCCEEDED(hrc)
6680 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6681 {
6682 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6683 int vrc;
6684
6685 if (aAutostartEnabled)
6686 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6687 else
6688 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6689
6690 if (RT_SUCCESS(vrc))
6691 {
6692 hrc = mHWData.backupEx();
6693 if (SUCCEEDED(hrc))
6694 {
6695 i_setModified(IsModified_MachineData);
6696 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6697 }
6698 }
6699 else if (vrc == VERR_NOT_SUPPORTED)
6700 hrc = setError(VBOX_E_NOT_SUPPORTED,
6701 tr("The VM autostart feature is not supported on this platform"));
6702 else if (vrc == VERR_PATH_NOT_FOUND)
6703 hrc = setError(E_FAIL,
6704 tr("The path to the autostart database is not set"));
6705 else
6706 hrc = setError(E_UNEXPECTED,
6707 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6708 aAutostartEnabled ? "Adding" : "Removing",
6709 mUserData->s.strName.c_str(), vrc);
6710 }
6711 return hrc;
6712}
6713
6714HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6715{
6716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6717
6718 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6719
6720 return S_OK;
6721}
6722
6723HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6724{
6725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6726 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6727 if (SUCCEEDED(hrc))
6728 {
6729 hrc = mHWData.backupEx();
6730 if (SUCCEEDED(hrc))
6731 {
6732 i_setModified(IsModified_MachineData);
6733 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6734 }
6735 }
6736 return hrc;
6737}
6738
6739HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6740{
6741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6742
6743 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6744
6745 return S_OK;
6746}
6747
6748HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6749{
6750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6751 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6752 if ( SUCCEEDED(hrc)
6753 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6754 {
6755 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6756 int vrc;
6757
6758 if (aAutostopType != AutostopType_Disabled)
6759 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6760 else
6761 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6762
6763 if (RT_SUCCESS(vrc))
6764 {
6765 hrc = mHWData.backupEx();
6766 if (SUCCEEDED(hrc))
6767 {
6768 i_setModified(IsModified_MachineData);
6769 mHWData->mAutostart.enmAutostopType = aAutostopType;
6770 }
6771 }
6772 else if (vrc == VERR_NOT_SUPPORTED)
6773 hrc = setError(VBOX_E_NOT_SUPPORTED,
6774 tr("The VM autostop feature is not supported on this platform"));
6775 else if (vrc == VERR_PATH_NOT_FOUND)
6776 hrc = setError(E_FAIL,
6777 tr("The path to the autostart database is not set"));
6778 else
6779 hrc = setError(E_UNEXPECTED,
6780 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6781 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6782 mUserData->s.strName.c_str(), vrc);
6783 }
6784 return hrc;
6785}
6786
6787HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6788{
6789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6790
6791 aDefaultFrontend = mHWData->mDefaultFrontend;
6792
6793 return S_OK;
6794}
6795
6796HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6797{
6798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6799 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6800 if (SUCCEEDED(hrc))
6801 {
6802 hrc = mHWData.backupEx();
6803 if (SUCCEEDED(hrc))
6804 {
6805 i_setModified(IsModified_MachineData);
6806 mHWData->mDefaultFrontend = aDefaultFrontend;
6807 }
6808 }
6809 return hrc;
6810}
6811
6812HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6813{
6814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6815 size_t cbIcon = mUserData->s.ovIcon.size();
6816 aIcon.resize(cbIcon);
6817 if (cbIcon)
6818 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6819 return S_OK;
6820}
6821
6822HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6823{
6824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6825 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6826 if (SUCCEEDED(hrc))
6827 {
6828 i_setModified(IsModified_MachineData);
6829 mUserData.backup();
6830 size_t cbIcon = aIcon.size();
6831 mUserData->s.ovIcon.resize(cbIcon);
6832 if (cbIcon)
6833 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6834 }
6835 return hrc;
6836}
6837
6838HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6839{
6840#ifdef VBOX_WITH_USB
6841 *aUSBProxyAvailable = true;
6842#else
6843 *aUSBProxyAvailable = false;
6844#endif
6845 return S_OK;
6846}
6847
6848HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6849{
6850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6851
6852 *aVMProcessPriority = mUserData->s.enmVMPriority;
6853
6854 return S_OK;
6855}
6856
6857HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6858{
6859 RT_NOREF(aVMProcessPriority);
6860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6861 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6862 if (SUCCEEDED(hrc))
6863 {
6864 hrc = mUserData.backupEx();
6865 if (SUCCEEDED(hrc))
6866 {
6867 i_setModified(IsModified_MachineData);
6868 mUserData->s.enmVMPriority = aVMProcessPriority;
6869 }
6870 }
6871 alock.release();
6872 if (SUCCEEDED(hrc))
6873 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6874 return hrc;
6875}
6876
6877HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6878 ComPtr<IProgress> &aProgress)
6879{
6880 ComObjPtr<Progress> pP;
6881 Progress *ppP = pP;
6882 IProgress *iP = static_cast<IProgress *>(ppP);
6883 IProgress **pProgress = &iP;
6884
6885 IMachine *pTarget = aTarget;
6886
6887 /* Convert the options. */
6888 RTCList<CloneOptions_T> optList;
6889 if (aOptions.size())
6890 for (size_t i = 0; i < aOptions.size(); ++i)
6891 optList.append(aOptions[i]);
6892
6893 if (optList.contains(CloneOptions_Link))
6894 {
6895 if (!i_isSnapshotMachine())
6896 return setError(E_INVALIDARG,
6897 tr("Linked clone can only be created from a snapshot"));
6898 if (aMode != CloneMode_MachineState)
6899 return setError(E_INVALIDARG,
6900 tr("Linked clone can only be created for a single machine state"));
6901 }
6902 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6903
6904 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6905
6906 HRESULT rc = pWorker->start(pProgress);
6907
6908 pP = static_cast<Progress *>(*pProgress);
6909 pP.queryInterfaceTo(aProgress.asOutParam());
6910
6911 return rc;
6912
6913}
6914
6915HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6916 const com::Utf8Str &aType,
6917 ComPtr<IProgress> &aProgress)
6918{
6919 LogFlowThisFuncEnter();
6920
6921 ComObjPtr<Progress> ptrProgress;
6922 HRESULT hrc = ptrProgress.createObject();
6923 if (SUCCEEDED(hrc))
6924 {
6925 /* Initialize our worker task */
6926 MachineMoveVM *pTask = NULL;
6927 try
6928 {
6929 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6930 }
6931 catch (std::bad_alloc &)
6932 {
6933 return E_OUTOFMEMORY;
6934 }
6935
6936 hrc = pTask->init();//no exceptions are thrown
6937
6938 if (SUCCEEDED(hrc))
6939 {
6940 hrc = pTask->createThread();
6941 pTask = NULL; /* Consumed by createThread(). */
6942 if (SUCCEEDED(hrc))
6943 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6944 else
6945 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6946 }
6947 else
6948 delete pTask;
6949 }
6950
6951 LogFlowThisFuncLeave();
6952 return hrc;
6953
6954}
6955
6956HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6957{
6958 NOREF(aProgress);
6959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6960
6961 // This check should always fail.
6962 HRESULT rc = i_checkStateDependency(MutableStateDep);
6963 if (FAILED(rc)) return rc;
6964
6965 AssertFailedReturn(E_NOTIMPL);
6966}
6967
6968HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6969{
6970 NOREF(aSavedStateFile);
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::discardSavedState(BOOL aFRemoveFile)
6981{
6982 NOREF(aFRemoveFile);
6983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6984
6985 // This check should always fail.
6986 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
6987 if (FAILED(rc)) return rc;
6988
6989 AssertFailedReturn(E_NOTIMPL);
6990}
6991
6992// public methods for internal purposes
6993/////////////////////////////////////////////////////////////////////////////
6994
6995/**
6996 * Adds the given IsModified_* flag to the dirty flags of the machine.
6997 * This must be called either during i_loadSettings or under the machine write lock.
6998 * @param fl Flag
6999 * @param fAllowStateModification If state modifications are allowed.
7000 */
7001void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7002{
7003 mData->flModifications |= fl;
7004 if (fAllowStateModification && i_isStateModificationAllowed())
7005 mData->mCurrentStateModified = true;
7006}
7007
7008/**
7009 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7010 * care of the write locking.
7011 *
7012 * @param fModification The flag to add.
7013 * @param fAllowStateModification If state modifications are allowed.
7014 */
7015void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7016{
7017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7018 i_setModified(fModification, fAllowStateModification);
7019}
7020
7021/**
7022 * Saves the registry entry of this machine to the given configuration node.
7023 *
7024 * @param data Machine registry data.
7025 *
7026 * @note locks this object for reading.
7027 */
7028HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7029{
7030 AutoLimitedCaller autoCaller(this);
7031 AssertComRCReturnRC(autoCaller.rc());
7032
7033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7034
7035 data.uuid = mData->mUuid;
7036 data.strSettingsFile = mData->m_strConfigFile;
7037
7038 return S_OK;
7039}
7040
7041/**
7042 * Calculates the absolute path of the given path taking the directory of the
7043 * machine settings file as the current directory.
7044 *
7045 * @param strPath Path to calculate the absolute path for.
7046 * @param aResult Where to put the result (used only on success, can be the
7047 * same Utf8Str instance as passed in @a aPath).
7048 * @return IPRT result.
7049 *
7050 * @note Locks this object for reading.
7051 */
7052int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7053{
7054 AutoCaller autoCaller(this);
7055 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7056
7057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7058
7059 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7060
7061 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7062
7063 strSettingsDir.stripFilename();
7064 char szFolder[RTPATH_MAX];
7065 size_t cbFolder = sizeof(szFolder);
7066 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7067 if (RT_SUCCESS(vrc))
7068 aResult = szFolder;
7069
7070 return vrc;
7071}
7072
7073/**
7074 * Copies strSource to strTarget, making it relative to the machine folder
7075 * if it is a subdirectory thereof, or simply copying it otherwise.
7076 *
7077 * @param strSource Path to evaluate and copy.
7078 * @param strTarget Buffer to receive target path.
7079 *
7080 * @note Locks this object for reading.
7081 */
7082void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7083 Utf8Str &strTarget)
7084{
7085 AutoCaller autoCaller(this);
7086 AssertComRCReturn(autoCaller.rc(), (void)0);
7087
7088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7089
7090 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7091 // use strTarget as a temporary buffer to hold the machine settings dir
7092 strTarget = mData->m_strConfigFileFull;
7093 strTarget.stripFilename();
7094 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7095 {
7096 // is relative: then append what's left
7097 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7098 // for empty paths (only possible for subdirs) use "." to avoid
7099 // triggering default settings for not present config attributes.
7100 if (strTarget.isEmpty())
7101 strTarget = ".";
7102 }
7103 else
7104 // is not relative: then overwrite
7105 strTarget = strSource;
7106}
7107
7108/**
7109 * Returns the full path to the machine's log folder in the
7110 * \a aLogFolder argument.
7111 */
7112void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7113{
7114 AutoCaller autoCaller(this);
7115 AssertComRCReturnVoid(autoCaller.rc());
7116
7117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7118
7119 char szTmp[RTPATH_MAX];
7120 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7121 if (RT_SUCCESS(vrc))
7122 {
7123 if (szTmp[0] && !mUserData.isNull())
7124 {
7125 char szTmp2[RTPATH_MAX];
7126 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7127 if (RT_SUCCESS(vrc))
7128 aLogFolder.printf("%s%c%s",
7129 szTmp2,
7130 RTPATH_DELIMITER,
7131 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7132 }
7133 else
7134 vrc = VERR_PATH_IS_RELATIVE;
7135 }
7136
7137 if (RT_FAILURE(vrc))
7138 {
7139 // fallback if VBOX_USER_LOGHOME is not set or invalid
7140 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7141 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7142 aLogFolder.append(RTPATH_DELIMITER);
7143 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7144 }
7145}
7146
7147/**
7148 * Returns the full path to the machine's log file for an given index.
7149 */
7150Utf8Str Machine::i_getLogFilename(ULONG idx)
7151{
7152 Utf8Str logFolder;
7153 getLogFolder(logFolder);
7154 Assert(logFolder.length());
7155
7156 Utf8Str log;
7157 if (idx == 0)
7158 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7159#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7160 else if (idx == 1)
7161 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7162 else
7163 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7164#else
7165 else
7166 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7167#endif
7168 return log;
7169}
7170
7171/**
7172 * Returns the full path to the machine's hardened log file.
7173 */
7174Utf8Str Machine::i_getHardeningLogFilename(void)
7175{
7176 Utf8Str strFilename;
7177 getLogFolder(strFilename);
7178 Assert(strFilename.length());
7179 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7180 return strFilename;
7181}
7182
7183/**
7184 * Returns the default NVRAM filename based on the location of the VM config.
7185 * Note that this is a relative path.
7186 */
7187Utf8Str Machine::i_getDefaultNVRAMFilename()
7188{
7189 AutoCaller autoCaller(this);
7190 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7191
7192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7193
7194 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7195 || i_isSnapshotMachine())
7196 return Utf8Str::Empty;
7197
7198 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7199 strNVRAMFilePath.stripPath();
7200 strNVRAMFilePath.stripSuffix();
7201 strNVRAMFilePath += ".nvram";
7202
7203 return strNVRAMFilePath;
7204}
7205
7206/**
7207 * Returns the NVRAM filename for a new snapshot. This intentionally works
7208 * similarly to the saved state file naming. Note that this is usually
7209 * a relative path, unless the snapshot folder is absolute.
7210 */
7211Utf8Str Machine::i_getSnapshotNVRAMFilename()
7212{
7213 AutoCaller autoCaller(this);
7214 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7215
7216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7217
7218 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7219 return Utf8Str::Empty;
7220
7221 RTTIMESPEC ts;
7222 RTTimeNow(&ts);
7223 RTTIME time;
7224 RTTimeExplode(&time, &ts);
7225
7226 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7227 strNVRAMFilePath += RTPATH_DELIMITER;
7228 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7229 time.i32Year, time.u8Month, time.u8MonthDay,
7230 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7231
7232 return strNVRAMFilePath;
7233}
7234
7235/**
7236 * Composes a unique saved state filename based on the current system time. The filename is
7237 * granular to the second so this will work so long as no more than one snapshot is taken on
7238 * a machine per second.
7239 *
7240 * Before version 4.1, we used this formula for saved state files:
7241 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7242 * which no longer works because saved state files can now be shared between the saved state of the
7243 * "saved" machine and an online snapshot, and the following would cause problems:
7244 * 1) save machine
7245 * 2) create online snapshot from that machine state --> reusing saved state file
7246 * 3) save machine again --> filename would be reused, breaking the online snapshot
7247 *
7248 * So instead we now use a timestamp.
7249 *
7250 * @param strStateFilePath
7251 */
7252
7253void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7254{
7255 AutoCaller autoCaller(this);
7256 AssertComRCReturnVoid(autoCaller.rc());
7257
7258 {
7259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7260 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7261 }
7262
7263 RTTIMESPEC ts;
7264 RTTimeNow(&ts);
7265 RTTIME time;
7266 RTTimeExplode(&time, &ts);
7267
7268 strStateFilePath += RTPATH_DELIMITER;
7269 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7270 time.i32Year, time.u8Month, time.u8MonthDay,
7271 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7272}
7273
7274/**
7275 * Returns whether at least one USB controller is present for the VM.
7276 */
7277bool Machine::i_isUSBControllerPresent()
7278{
7279 AutoCaller autoCaller(this);
7280 AssertComRCReturn(autoCaller.rc(), false);
7281
7282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7283
7284 return (mUSBControllers->size() > 0);
7285}
7286
7287
7288/**
7289 * @note Locks this object for writing, calls the client process
7290 * (inside the lock).
7291 */
7292HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7293 const Utf8Str &strFrontend,
7294 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7295 ProgressProxy *aProgress)
7296{
7297 LogFlowThisFuncEnter();
7298
7299 AssertReturn(aControl, E_FAIL);
7300 AssertReturn(aProgress, E_FAIL);
7301 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7302
7303 AutoCaller autoCaller(this);
7304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7305
7306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7307
7308 if (!mData->mRegistered)
7309 return setError(E_UNEXPECTED,
7310 tr("The machine '%s' is not registered"),
7311 mUserData->s.strName.c_str());
7312
7313 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7314
7315 /* The process started when launching a VM with separate UI/VM processes is always
7316 * the UI process, i.e. needs special handling as it won't claim the session. */
7317 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7318
7319 if (fSeparate)
7320 {
7321 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7322 return setError(VBOX_E_INVALID_OBJECT_STATE,
7323 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7324 mUserData->s.strName.c_str());
7325 }
7326 else
7327 {
7328 if ( mData->mSession.mState == SessionState_Locked
7329 || mData->mSession.mState == SessionState_Spawning
7330 || mData->mSession.mState == SessionState_Unlocking)
7331 return setError(VBOX_E_INVALID_OBJECT_STATE,
7332 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7333 mUserData->s.strName.c_str());
7334
7335 /* may not be busy */
7336 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7337 }
7338
7339 /* Hardening logging */
7340#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7341 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7342 {
7343 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7344 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7345 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7346 {
7347 Utf8Str strStartupLogDir = strHardeningLogFile;
7348 strStartupLogDir.stripFilename();
7349 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7350 file without stripping the file. */
7351 }
7352 strSupHardeningLogArg.append(strHardeningLogFile);
7353
7354 /* Remove legacy log filename to avoid confusion. */
7355 Utf8Str strOldStartupLogFile;
7356 getLogFolder(strOldStartupLogFile);
7357 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7358 RTFileDelete(strOldStartupLogFile.c_str());
7359 }
7360#else
7361 Utf8Str strSupHardeningLogArg;
7362#endif
7363
7364 Utf8Str strAppOverride;
7365#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7366 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7367#endif
7368
7369 bool fUseVBoxSDS = false;
7370 Utf8Str strCanonicalName;
7371 if (false)
7372 { }
7373#ifdef VBOX_WITH_QTGUI
7374 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7375 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7376 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7377 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7378 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7379 {
7380 strCanonicalName = "GUI/Qt";
7381 fUseVBoxSDS = true;
7382 }
7383#endif
7384#ifdef VBOX_WITH_VBOXSDL
7385 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7386 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7387 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7388 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7389 {
7390 strCanonicalName = "GUI/SDL";
7391 fUseVBoxSDS = true;
7392 }
7393#endif
7394#ifdef VBOX_WITH_HEADLESS
7395 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7396 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7397 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7398 {
7399 strCanonicalName = "headless";
7400 }
7401#endif
7402 else
7403 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7404
7405 Utf8Str idStr = mData->mUuid.toString();
7406 Utf8Str const &strMachineName = mUserData->s.strName;
7407 RTPROCESS pid = NIL_RTPROCESS;
7408
7409#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7410 RT_NOREF(fUseVBoxSDS);
7411#else
7412 DWORD idCallerSession = ~(DWORD)0;
7413 if (fUseVBoxSDS)
7414 {
7415 /*
7416 * The VBoxSDS should be used for process launching the VM with
7417 * GUI only if the caller and the VBoxSDS are in different Windows
7418 * sessions and the caller in the interactive one.
7419 */
7420 fUseVBoxSDS = false;
7421
7422 /* Get windows session of the current process. The process token used
7423 due to several reasons:
7424 1. The token is absent for the current thread except someone set it
7425 for us.
7426 2. Needs to get the id of the session where the process is started.
7427 We only need to do this once, though. */
7428 static DWORD s_idCurrentSession = ~(DWORD)0;
7429 DWORD idCurrentSession = s_idCurrentSession;
7430 if (idCurrentSession == ~(DWORD)0)
7431 {
7432 HANDLE hCurrentProcessToken = NULL;
7433 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7434 {
7435 DWORD cbIgn = 0;
7436 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7437 s_idCurrentSession = idCurrentSession;
7438 else
7439 {
7440 idCurrentSession = ~(DWORD)0;
7441 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7442 }
7443 CloseHandle(hCurrentProcessToken);
7444 }
7445 else
7446 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7447 }
7448
7449 /* get the caller's session */
7450 HRESULT hrc = CoImpersonateClient();
7451 if (SUCCEEDED(hrc))
7452 {
7453 HANDLE hCallerThreadToken;
7454 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7455 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7456 &hCallerThreadToken))
7457 {
7458 SetLastError(NO_ERROR);
7459 DWORD cbIgn = 0;
7460 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7461 {
7462 /* Only need to use SDS if the session ID differs: */
7463 if (idCurrentSession != idCallerSession)
7464 {
7465 fUseVBoxSDS = false;
7466
7467 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7468 DWORD cbTokenGroups = 0;
7469 PTOKEN_GROUPS pTokenGroups = NULL;
7470 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7471 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7472 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7473 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7474 {
7475 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7476 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7477 PSID pInteractiveSid = NULL;
7478 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7479 {
7480 /* Iterate over the groups looking for the interactive SID: */
7481 fUseVBoxSDS = false;
7482 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7483 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7484 {
7485 fUseVBoxSDS = true;
7486 break;
7487 }
7488 FreeSid(pInteractiveSid);
7489 }
7490 }
7491 else
7492 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7493 RTMemTmpFree(pTokenGroups);
7494 }
7495 }
7496 else
7497 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7498 CloseHandle(hCallerThreadToken);
7499 }
7500 else
7501 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7502 CoRevertToSelf();
7503 }
7504 else
7505 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7506 }
7507 if (fUseVBoxSDS)
7508 {
7509 /* connect to VBoxSDS */
7510 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7511 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7512 if (FAILED(rc))
7513 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7514 strMachineName.c_str());
7515
7516 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7517 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7518 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7519 service to access the files. */
7520 rc = CoSetProxyBlanket(pVBoxSDS,
7521 RPC_C_AUTHN_DEFAULT,
7522 RPC_C_AUTHZ_DEFAULT,
7523 COLE_DEFAULT_PRINCIPAL,
7524 RPC_C_AUTHN_LEVEL_DEFAULT,
7525 RPC_C_IMP_LEVEL_IMPERSONATE,
7526 NULL,
7527 EOAC_DEFAULT);
7528 if (FAILED(rc))
7529 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7530
7531 size_t const cEnvVars = aEnvironmentChanges.size();
7532 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7533 for (size_t i = 0; i < cEnvVars; i++)
7534 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7535
7536 ULONG uPid = 0;
7537 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7538 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7539 idCallerSession, &uPid);
7540 if (FAILED(rc))
7541 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7542 pid = (RTPROCESS)uPid;
7543 }
7544 else
7545#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7546 {
7547 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7548 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7549 if (RT_FAILURE(vrc))
7550 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7551 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7552 }
7553
7554 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7555 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7556
7557 if (!fSeparate)
7558 {
7559 /*
7560 * Note that we don't release the lock here before calling the client,
7561 * because it doesn't need to call us back if called with a NULL argument.
7562 * Releasing the lock here is dangerous because we didn't prepare the
7563 * launch data yet, but the client we've just started may happen to be
7564 * too fast and call LockMachine() that will fail (because of PID, etc.),
7565 * so that the Machine will never get out of the Spawning session state.
7566 */
7567
7568 /* inform the session that it will be a remote one */
7569 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7570#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7571 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7572#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7573 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7574#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7575 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7576
7577 if (FAILED(rc))
7578 {
7579 /* restore the session state */
7580 mData->mSession.mState = SessionState_Unlocked;
7581 alock.release();
7582 mParent->i_addProcessToReap(pid);
7583 /* The failure may occur w/o any error info (from RPC), so provide one */
7584 return setError(VBOX_E_VM_ERROR,
7585 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7586 }
7587
7588 /* attach launch data to the machine */
7589 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7590 mData->mSession.mRemoteControls.push_back(aControl);
7591 mData->mSession.mProgress = aProgress;
7592 mData->mSession.mPID = pid;
7593 mData->mSession.mState = SessionState_Spawning;
7594 Assert(strCanonicalName.isNotEmpty());
7595 mData->mSession.mName = strCanonicalName;
7596 }
7597 else
7598 {
7599 /* For separate UI process we declare the launch as completed instantly, as the
7600 * actual headless VM start may or may not come. No point in remembering anything
7601 * yet, as what matters for us is when the headless VM gets started. */
7602 aProgress->i_notifyComplete(S_OK);
7603 }
7604
7605 alock.release();
7606 mParent->i_addProcessToReap(pid);
7607
7608 LogFlowThisFuncLeave();
7609 return S_OK;
7610}
7611
7612/**
7613 * Returns @c true if the given session machine instance has an open direct
7614 * session (and optionally also for direct sessions which are closing) and
7615 * returns the session control machine instance if so.
7616 *
7617 * Note that when the method returns @c false, the arguments remain unchanged.
7618 *
7619 * @param aMachine Session machine object.
7620 * @param aControl Direct session control object (optional).
7621 * @param aRequireVM If true then only allow VM sessions.
7622 * @param aAllowClosing If true then additionally a session which is currently
7623 * being closed will also be allowed.
7624 *
7625 * @note locks this object for reading.
7626 */
7627bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7628 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7629 bool aRequireVM /*= false*/,
7630 bool aAllowClosing /*= false*/)
7631{
7632 AutoLimitedCaller autoCaller(this);
7633 AssertComRCReturn(autoCaller.rc(), false);
7634
7635 /* just return false for inaccessible machines */
7636 if (getObjectState().getState() != ObjectState::Ready)
7637 return false;
7638
7639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7640
7641 if ( ( mData->mSession.mState == SessionState_Locked
7642 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7643 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7644 )
7645 {
7646 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7647
7648 aMachine = mData->mSession.mMachine;
7649
7650 if (aControl != NULL)
7651 *aControl = mData->mSession.mDirectControl;
7652
7653 return true;
7654 }
7655
7656 return false;
7657}
7658
7659/**
7660 * Returns @c true if the given machine has an spawning direct session.
7661 *
7662 * @note locks this object for reading.
7663 */
7664bool Machine::i_isSessionSpawning()
7665{
7666 AutoLimitedCaller autoCaller(this);
7667 AssertComRCReturn(autoCaller.rc(), false);
7668
7669 /* just return false for inaccessible machines */
7670 if (getObjectState().getState() != ObjectState::Ready)
7671 return false;
7672
7673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7674
7675 if (mData->mSession.mState == SessionState_Spawning)
7676 return true;
7677
7678 return false;
7679}
7680
7681/**
7682 * Called from the client watcher thread to check for unexpected client process
7683 * death during Session_Spawning state (e.g. before it successfully opened a
7684 * direct session).
7685 *
7686 * On Win32 and on OS/2, this method is called only when we've got the
7687 * direct client's process termination notification, so it always returns @c
7688 * true.
7689 *
7690 * On other platforms, this method returns @c true if the client process is
7691 * terminated and @c false if it's still alive.
7692 *
7693 * @note Locks this object for writing.
7694 */
7695bool Machine::i_checkForSpawnFailure()
7696{
7697 AutoCaller autoCaller(this);
7698 if (!autoCaller.isOk())
7699 {
7700 /* nothing to do */
7701 LogFlowThisFunc(("Already uninitialized!\n"));
7702 return true;
7703 }
7704
7705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7706
7707 if (mData->mSession.mState != SessionState_Spawning)
7708 {
7709 /* nothing to do */
7710 LogFlowThisFunc(("Not spawning any more!\n"));
7711 return true;
7712 }
7713
7714 HRESULT rc = S_OK;
7715
7716 /* PID not yet initialized, skip check. */
7717 if (mData->mSession.mPID == NIL_RTPROCESS)
7718 return false;
7719
7720 RTPROCSTATUS status;
7721 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7722
7723 if (vrc != VERR_PROCESS_RUNNING)
7724 {
7725 Utf8Str strExtraInfo;
7726
7727#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7728 /* If the startup logfile exists and is of non-zero length, tell the
7729 user to look there for more details to encourage them to attach it
7730 when reporting startup issues. */
7731 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7732 uint64_t cbStartupLogFile = 0;
7733 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7734 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7735 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7736#endif
7737
7738 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7739 rc = setError(E_FAIL,
7740 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7741 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7742 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7743 rc = setError(E_FAIL,
7744 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7745 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7746 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7747 rc = setError(E_FAIL,
7748 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7749 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7750 else
7751 rc = setErrorBoth(E_FAIL, vrc,
7752 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7753 i_getName().c_str(), vrc, strExtraInfo.c_str());
7754 }
7755
7756 if (FAILED(rc))
7757 {
7758 /* Close the remote session, remove the remote control from the list
7759 * and reset session state to Closed (@note keep the code in sync with
7760 * the relevant part in LockMachine()). */
7761
7762 Assert(mData->mSession.mRemoteControls.size() == 1);
7763 if (mData->mSession.mRemoteControls.size() == 1)
7764 {
7765 ErrorInfoKeeper eik;
7766 mData->mSession.mRemoteControls.front()->Uninitialize();
7767 }
7768
7769 mData->mSession.mRemoteControls.clear();
7770 mData->mSession.mState = SessionState_Unlocked;
7771
7772 /* finalize the progress after setting the state */
7773 if (!mData->mSession.mProgress.isNull())
7774 {
7775 mData->mSession.mProgress->notifyComplete(rc);
7776 mData->mSession.mProgress.setNull();
7777 }
7778
7779 mData->mSession.mPID = NIL_RTPROCESS;
7780
7781 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7782 return true;
7783 }
7784
7785 return false;
7786}
7787
7788/**
7789 * Checks whether the machine can be registered. If so, commits and saves
7790 * all settings.
7791 *
7792 * @note Must be called from mParent's write lock. Locks this object and
7793 * children for writing.
7794 */
7795HRESULT Machine::i_prepareRegister()
7796{
7797 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7798
7799 AutoLimitedCaller autoCaller(this);
7800 AssertComRCReturnRC(autoCaller.rc());
7801
7802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7803
7804 /* wait for state dependents to drop to zero */
7805 i_ensureNoStateDependencies();
7806
7807 if (!mData->mAccessible)
7808 return setError(VBOX_E_INVALID_OBJECT_STATE,
7809 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7810 mUserData->s.strName.c_str(),
7811 mData->mUuid.toString().c_str());
7812
7813 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7814
7815 if (mData->mRegistered)
7816 return setError(VBOX_E_INVALID_OBJECT_STATE,
7817 tr("The machine '%s' with UUID {%s} is already registered"),
7818 mUserData->s.strName.c_str(),
7819 mData->mUuid.toString().c_str());
7820
7821 HRESULT rc = S_OK;
7822
7823 // Ensure the settings are saved. If we are going to be registered and
7824 // no config file exists yet, create it by calling i_saveSettings() too.
7825 if ( (mData->flModifications)
7826 || (!mData->pMachineConfigFile->fileExists())
7827 )
7828 {
7829 rc = i_saveSettings(NULL);
7830 // no need to check whether VirtualBox.xml needs saving too since
7831 // we can't have a machine XML file rename pending
7832 if (FAILED(rc)) return rc;
7833 }
7834
7835 /* more config checking goes here */
7836
7837 if (SUCCEEDED(rc))
7838 {
7839 /* we may have had implicit modifications we want to fix on success */
7840 i_commit();
7841
7842 mData->mRegistered = true;
7843 }
7844 else
7845 {
7846 /* we may have had implicit modifications we want to cancel on failure*/
7847 i_rollback(false /* aNotify */);
7848 }
7849
7850 return rc;
7851}
7852
7853/**
7854 * Increases the number of objects dependent on the machine state or on the
7855 * registered state. Guarantees that these two states will not change at least
7856 * until #i_releaseStateDependency() is called.
7857 *
7858 * Depending on the @a aDepType value, additional state checks may be made.
7859 * These checks will set extended error info on failure. See
7860 * #i_checkStateDependency() for more info.
7861 *
7862 * If this method returns a failure, the dependency is not added and the caller
7863 * is not allowed to rely on any particular machine state or registration state
7864 * value and may return the failed result code to the upper level.
7865 *
7866 * @param aDepType Dependency type to add.
7867 * @param aState Current machine state (NULL if not interested).
7868 * @param aRegistered Current registered state (NULL if not interested).
7869 *
7870 * @note Locks this object for writing.
7871 */
7872HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7873 MachineState_T *aState /* = NULL */,
7874 BOOL *aRegistered /* = NULL */)
7875{
7876 AutoCaller autoCaller(this);
7877 AssertComRCReturnRC(autoCaller.rc());
7878
7879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7880
7881 HRESULT rc = i_checkStateDependency(aDepType);
7882 if (FAILED(rc)) return rc;
7883
7884 {
7885 if (mData->mMachineStateChangePending != 0)
7886 {
7887 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7888 * drop to zero so don't add more. It may make sense to wait a bit
7889 * and retry before reporting an error (since the pending state
7890 * transition should be really quick) but let's just assert for
7891 * now to see if it ever happens on practice. */
7892
7893 AssertFailed();
7894
7895 return setError(E_ACCESSDENIED,
7896 tr("Machine state change is in progress. Please retry the operation later."));
7897 }
7898
7899 ++mData->mMachineStateDeps;
7900 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7901 }
7902
7903 if (aState)
7904 *aState = mData->mMachineState;
7905 if (aRegistered)
7906 *aRegistered = mData->mRegistered;
7907
7908 return S_OK;
7909}
7910
7911/**
7912 * Decreases the number of objects dependent on the machine state.
7913 * Must always complete the #i_addStateDependency() call after the state
7914 * dependency is no more necessary.
7915 */
7916void Machine::i_releaseStateDependency()
7917{
7918 AutoCaller autoCaller(this);
7919 AssertComRCReturnVoid(autoCaller.rc());
7920
7921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7922
7923 /* releaseStateDependency() w/o addStateDependency()? */
7924 AssertReturnVoid(mData->mMachineStateDeps != 0);
7925 -- mData->mMachineStateDeps;
7926
7927 if (mData->mMachineStateDeps == 0)
7928 {
7929 /* inform i_ensureNoStateDependencies() that there are no more deps */
7930 if (mData->mMachineStateChangePending != 0)
7931 {
7932 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7933 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7934 }
7935 }
7936}
7937
7938Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7939{
7940 /* start with nothing found */
7941 Utf8Str strResult("");
7942
7943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7944
7945 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7946 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7947 // found:
7948 strResult = it->second; // source is a Utf8Str
7949
7950 return strResult;
7951}
7952
7953// protected methods
7954/////////////////////////////////////////////////////////////////////////////
7955
7956/**
7957 * Performs machine state checks based on the @a aDepType value. If a check
7958 * fails, this method will set extended error info, otherwise it will return
7959 * S_OK. It is supposed, that on failure, the caller will immediately return
7960 * the return value of this method to the upper level.
7961 *
7962 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7963 *
7964 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7965 * current state of this machine object allows to change settings of the
7966 * machine (i.e. the machine is not registered, or registered but not running
7967 * and not saved). It is useful to call this method from Machine setters
7968 * before performing any change.
7969 *
7970 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7971 * as for MutableStateDep except that if the machine is saved, S_OK is also
7972 * returned. This is useful in setters which allow changing machine
7973 * properties when it is in the saved state.
7974 *
7975 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7976 * if the current state of this machine object allows to change runtime
7977 * changeable settings of the machine (i.e. the machine is not registered, or
7978 * registered but either running or not running and not saved). It is useful
7979 * to call this method from Machine setters before performing any changes to
7980 * runtime changeable settings.
7981 *
7982 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7983 * the same as for MutableOrRunningStateDep except that if the machine is
7984 * saved, S_OK is also returned. This is useful in setters which allow
7985 * changing runtime and saved state changeable machine properties.
7986 *
7987 * @param aDepType Dependency type to check.
7988 *
7989 * @note Non Machine based classes should use #i_addStateDependency() and
7990 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7991 * template.
7992 *
7993 * @note This method must be called from under this object's read or write
7994 * lock.
7995 */
7996HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7997{
7998 switch (aDepType)
7999 {
8000 case AnyStateDep:
8001 {
8002 break;
8003 }
8004 case MutableStateDep:
8005 {
8006 if ( mData->mRegistered
8007 && ( !i_isSessionMachine()
8008 || ( mData->mMachineState != MachineState_Aborted
8009 && mData->mMachineState != MachineState_Teleported
8010 && mData->mMachineState != MachineState_PoweredOff
8011 )
8012 )
8013 )
8014 return setError(VBOX_E_INVALID_VM_STATE,
8015 tr("The machine is not mutable (state is %s)"),
8016 Global::stringifyMachineState(mData->mMachineState));
8017 break;
8018 }
8019 case MutableOrSavedStateDep:
8020 {
8021 if ( mData->mRegistered
8022 && ( !i_isSessionMachine()
8023 || ( mData->mMachineState != MachineState_Aborted
8024 && mData->mMachineState != MachineState_Teleported
8025 && mData->mMachineState != MachineState_Saved
8026 && mData->mMachineState != MachineState_PoweredOff
8027 )
8028 )
8029 )
8030 return setError(VBOX_E_INVALID_VM_STATE,
8031 tr("The machine is not mutable or saved (state is %s)"),
8032 Global::stringifyMachineState(mData->mMachineState));
8033 break;
8034 }
8035 case MutableOrRunningStateDep:
8036 {
8037 if ( mData->mRegistered
8038 && ( !i_isSessionMachine()
8039 || ( mData->mMachineState != MachineState_Aborted
8040 && mData->mMachineState != MachineState_Teleported
8041 && mData->mMachineState != MachineState_PoweredOff
8042 && !Global::IsOnline(mData->mMachineState)
8043 )
8044 )
8045 )
8046 return setError(VBOX_E_INVALID_VM_STATE,
8047 tr("The machine is not mutable or running (state is %s)"),
8048 Global::stringifyMachineState(mData->mMachineState));
8049 break;
8050 }
8051 case MutableOrSavedOrRunningStateDep:
8052 {
8053 if ( mData->mRegistered
8054 && ( !i_isSessionMachine()
8055 || ( mData->mMachineState != MachineState_Aborted
8056 && mData->mMachineState != MachineState_Teleported
8057 && mData->mMachineState != MachineState_Saved
8058 && mData->mMachineState != MachineState_PoweredOff
8059 && !Global::IsOnline(mData->mMachineState)
8060 )
8061 )
8062 )
8063 return setError(VBOX_E_INVALID_VM_STATE,
8064 tr("The machine is not mutable, saved or running (state is %s)"),
8065 Global::stringifyMachineState(mData->mMachineState));
8066 break;
8067 }
8068 }
8069
8070 return S_OK;
8071}
8072
8073/**
8074 * Helper to initialize all associated child objects and allocate data
8075 * structures.
8076 *
8077 * This method must be called as a part of the object's initialization procedure
8078 * (usually done in the #init() method).
8079 *
8080 * @note Must be called only from #init() or from #i_registeredInit().
8081 */
8082HRESULT Machine::initDataAndChildObjects()
8083{
8084 AutoCaller autoCaller(this);
8085 AssertComRCReturnRC(autoCaller.rc());
8086 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8087 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8088
8089 AssertReturn(!mData->mAccessible, E_FAIL);
8090
8091 /* allocate data structures */
8092 mSSData.allocate();
8093 mUserData.allocate();
8094 mHWData.allocate();
8095 mMediumAttachments.allocate();
8096 mStorageControllers.allocate();
8097 mUSBControllers.allocate();
8098
8099 /* initialize mOSTypeId */
8100 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8101
8102/** @todo r=bird: init() methods never fails, right? Why don't we make them
8103 * return void then! */
8104
8105 /* create associated BIOS settings object */
8106 unconst(mBIOSSettings).createObject();
8107 mBIOSSettings->init(this);
8108
8109 /* create associated record settings object */
8110 unconst(mRecordingSettings).createObject();
8111 mRecordingSettings->init(this);
8112
8113 /* create the graphics adapter object (always present) */
8114 unconst(mGraphicsAdapter).createObject();
8115 mGraphicsAdapter->init(this);
8116
8117 /* create an associated VRDE object (default is disabled) */
8118 unconst(mVRDEServer).createObject();
8119 mVRDEServer->init(this);
8120
8121 /* create associated serial port objects */
8122 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8123 {
8124 unconst(mSerialPorts[slot]).createObject();
8125 mSerialPorts[slot]->init(this, slot);
8126 }
8127
8128 /* create associated parallel port objects */
8129 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8130 {
8131 unconst(mParallelPorts[slot]).createObject();
8132 mParallelPorts[slot]->init(this, slot);
8133 }
8134
8135 /* create the audio adapter object (always present, default is disabled) */
8136 unconst(mAudioAdapter).createObject();
8137 mAudioAdapter->init(this);
8138
8139 /* create the USB device filters object (always present) */
8140 unconst(mUSBDeviceFilters).createObject();
8141 mUSBDeviceFilters->init(this);
8142
8143 /* create associated network adapter objects */
8144 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8145 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8146 {
8147 unconst(mNetworkAdapters[slot]).createObject();
8148 mNetworkAdapters[slot]->init(this, slot);
8149 }
8150
8151 /* create the bandwidth control */
8152 unconst(mBandwidthControl).createObject();
8153 mBandwidthControl->init(this);
8154
8155 return S_OK;
8156}
8157
8158/**
8159 * Helper to uninitialize all associated child objects and to free all data
8160 * structures.
8161 *
8162 * This method must be called as a part of the object's uninitialization
8163 * procedure (usually done in the #uninit() method).
8164 *
8165 * @note Must be called only from #uninit() or from #i_registeredInit().
8166 */
8167void Machine::uninitDataAndChildObjects()
8168{
8169 AutoCaller autoCaller(this);
8170 AssertComRCReturnVoid(autoCaller.rc());
8171 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8172 || getObjectState().getState() == ObjectState::Limited);
8173
8174 /* tell all our other child objects we've been uninitialized */
8175 if (mBandwidthControl)
8176 {
8177 mBandwidthControl->uninit();
8178 unconst(mBandwidthControl).setNull();
8179 }
8180
8181 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8182 {
8183 if (mNetworkAdapters[slot])
8184 {
8185 mNetworkAdapters[slot]->uninit();
8186 unconst(mNetworkAdapters[slot]).setNull();
8187 }
8188 }
8189
8190 if (mUSBDeviceFilters)
8191 {
8192 mUSBDeviceFilters->uninit();
8193 unconst(mUSBDeviceFilters).setNull();
8194 }
8195
8196 if (mAudioAdapter)
8197 {
8198 mAudioAdapter->uninit();
8199 unconst(mAudioAdapter).setNull();
8200 }
8201
8202 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8203 {
8204 if (mParallelPorts[slot])
8205 {
8206 mParallelPorts[slot]->uninit();
8207 unconst(mParallelPorts[slot]).setNull();
8208 }
8209 }
8210
8211 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8212 {
8213 if (mSerialPorts[slot])
8214 {
8215 mSerialPorts[slot]->uninit();
8216 unconst(mSerialPorts[slot]).setNull();
8217 }
8218 }
8219
8220 if (mVRDEServer)
8221 {
8222 mVRDEServer->uninit();
8223 unconst(mVRDEServer).setNull();
8224 }
8225
8226 if (mGraphicsAdapter)
8227 {
8228 mGraphicsAdapter->uninit();
8229 unconst(mGraphicsAdapter).setNull();
8230 }
8231
8232 if (mBIOSSettings)
8233 {
8234 mBIOSSettings->uninit();
8235 unconst(mBIOSSettings).setNull();
8236 }
8237
8238 if (mRecordingSettings)
8239 {
8240 mRecordingSettings->uninit();
8241 unconst(mRecordingSettings).setNull();
8242 }
8243
8244 /* Deassociate media (only when a real Machine or a SnapshotMachine
8245 * instance is uninitialized; SessionMachine instances refer to real
8246 * Machine media). This is necessary for a clean re-initialization of
8247 * the VM after successfully re-checking the accessibility state. Note
8248 * that in case of normal Machine or SnapshotMachine uninitialization (as
8249 * a result of unregistering or deleting the snapshot), outdated media
8250 * attachments will already be uninitialized and deleted, so this
8251 * code will not affect them. */
8252 if ( !mMediumAttachments.isNull()
8253 && !i_isSessionMachine()
8254 )
8255 {
8256 for (MediumAttachmentList::const_iterator
8257 it = mMediumAttachments->begin();
8258 it != mMediumAttachments->end();
8259 ++it)
8260 {
8261 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8262 if (pMedium.isNull())
8263 continue;
8264 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8265 AssertComRC(rc);
8266 }
8267 }
8268
8269 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8270 {
8271 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8272 if (mData->mFirstSnapshot)
8273 {
8274 // snapshots tree is protected by machine write lock; strictly
8275 // this isn't necessary here since we're deleting the entire
8276 // machine, but otherwise we assert in Snapshot::uninit()
8277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8278 mData->mFirstSnapshot->uninit();
8279 mData->mFirstSnapshot.setNull();
8280 }
8281
8282 mData->mCurrentSnapshot.setNull();
8283 }
8284
8285 /* free data structures (the essential mData structure is not freed here
8286 * since it may be still in use) */
8287 mMediumAttachments.free();
8288 mStorageControllers.free();
8289 mUSBControllers.free();
8290 mHWData.free();
8291 mUserData.free();
8292 mSSData.free();
8293}
8294
8295/**
8296 * Returns a pointer to the Machine object for this machine that acts like a
8297 * parent for complex machine data objects such as shared folders, etc.
8298 *
8299 * For primary Machine objects and for SnapshotMachine objects, returns this
8300 * object's pointer itself. For SessionMachine objects, returns the peer
8301 * (primary) machine pointer.
8302 */
8303Machine *Machine::i_getMachine()
8304{
8305 if (i_isSessionMachine())
8306 return (Machine*)mPeer;
8307 return this;
8308}
8309
8310/**
8311 * Makes sure that there are no machine state dependents. If necessary, waits
8312 * for the number of dependents to drop to zero.
8313 *
8314 * Make sure this method is called from under this object's write lock to
8315 * guarantee that no new dependents may be added when this method returns
8316 * control to the caller.
8317 *
8318 * @note Locks this object for writing. The lock will be released while waiting
8319 * (if necessary).
8320 *
8321 * @warning To be used only in methods that change the machine state!
8322 */
8323void Machine::i_ensureNoStateDependencies()
8324{
8325 AssertReturnVoid(isWriteLockOnCurrentThread());
8326
8327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8328
8329 /* Wait for all state dependents if necessary */
8330 if (mData->mMachineStateDeps != 0)
8331 {
8332 /* lazy semaphore creation */
8333 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8334 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8335
8336 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8337 mData->mMachineStateDeps));
8338
8339 ++mData->mMachineStateChangePending;
8340
8341 /* reset the semaphore before waiting, the last dependent will signal
8342 * it */
8343 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8344
8345 alock.release();
8346
8347 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8348
8349 alock.acquire();
8350
8351 -- mData->mMachineStateChangePending;
8352 }
8353}
8354
8355/**
8356 * Changes the machine state and informs callbacks.
8357 *
8358 * This method is not intended to fail so it either returns S_OK or asserts (and
8359 * returns a failure).
8360 *
8361 * @note Locks this object for writing.
8362 */
8363HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8364{
8365 LogFlowThisFuncEnter();
8366 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8367 Assert(aMachineState != MachineState_Null);
8368
8369 AutoCaller autoCaller(this);
8370 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8371
8372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8373
8374 /* wait for state dependents to drop to zero */
8375 i_ensureNoStateDependencies();
8376
8377 MachineState_T const enmOldState = mData->mMachineState;
8378 if (enmOldState != aMachineState)
8379 {
8380 mData->mMachineState = aMachineState;
8381 RTTimeNow(&mData->mLastStateChange);
8382
8383#ifdef VBOX_WITH_DTRACE_R3_MAIN
8384 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8385#endif
8386 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8387 }
8388
8389 LogFlowThisFuncLeave();
8390 return S_OK;
8391}
8392
8393/**
8394 * Searches for a shared folder with the given logical name
8395 * in the collection of shared folders.
8396 *
8397 * @param aName logical name of the shared folder
8398 * @param aSharedFolder where to return the found object
8399 * @param aSetError whether to set the error info if the folder is
8400 * not found
8401 * @return
8402 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8403 *
8404 * @note
8405 * must be called from under the object's lock!
8406 */
8407HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8408 ComObjPtr<SharedFolder> &aSharedFolder,
8409 bool aSetError /* = false */)
8410{
8411 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8412 for (HWData::SharedFolderList::const_iterator
8413 it = mHWData->mSharedFolders.begin();
8414 it != mHWData->mSharedFolders.end();
8415 ++it)
8416 {
8417 SharedFolder *pSF = *it;
8418 AutoCaller autoCaller(pSF);
8419 if (pSF->i_getName() == aName)
8420 {
8421 aSharedFolder = pSF;
8422 rc = S_OK;
8423 break;
8424 }
8425 }
8426
8427 if (aSetError && FAILED(rc))
8428 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8429
8430 return rc;
8431}
8432
8433/**
8434 * Initializes all machine instance data from the given settings structures
8435 * from XML. The exception is the machine UUID which needs special handling
8436 * depending on the caller's use case, so the caller needs to set that herself.
8437 *
8438 * This gets called in several contexts during machine initialization:
8439 *
8440 * -- When machine XML exists on disk already and needs to be loaded into memory,
8441 * for example, from #i_registeredInit() to load all registered machines on
8442 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8443 * attached to the machine should be part of some media registry already.
8444 *
8445 * -- During OVF import, when a machine config has been constructed from an
8446 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8447 * ensure that the media listed as attachments in the config (which have
8448 * been imported from the OVF) receive the correct registry ID.
8449 *
8450 * -- During VM cloning.
8451 *
8452 * @param config Machine settings from XML.
8453 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8454 * for each attached medium in the config.
8455 * @return
8456 */
8457HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8458 const Guid *puuidRegistry)
8459{
8460 // copy name, description, OS type, teleporter, UTC etc.
8461 mUserData->s = config.machineUserData;
8462
8463 // look up the object by Id to check it is valid
8464 ComObjPtr<GuestOSType> pGuestOSType;
8465 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8466 if (!pGuestOSType.isNull())
8467 mUserData->s.strOsType = pGuestOSType->i_id();
8468
8469 // stateFile (optional)
8470 if (config.strStateFile.isEmpty())
8471 mSSData->strStateFilePath.setNull();
8472 else
8473 {
8474 Utf8Str stateFilePathFull(config.strStateFile);
8475 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8476 if (RT_FAILURE(vrc))
8477 return setErrorBoth(E_FAIL, vrc,
8478 tr("Invalid saved state file path '%s' (%Rrc)"),
8479 config.strStateFile.c_str(),
8480 vrc);
8481 mSSData->strStateFilePath = stateFilePathFull;
8482 }
8483
8484 // snapshot folder needs special processing so set it again
8485 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8486 if (FAILED(rc)) return rc;
8487
8488 /* Copy the extra data items (config may or may not be the same as
8489 * mData->pMachineConfigFile) if necessary. When loading the XML files
8490 * from disk they are the same, but not for OVF import. */
8491 if (mData->pMachineConfigFile != &config)
8492 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8493
8494 /* currentStateModified (optional, default is true) */
8495 mData->mCurrentStateModified = config.fCurrentStateModified;
8496
8497 mData->mLastStateChange = config.timeLastStateChange;
8498
8499 /*
8500 * note: all mUserData members must be assigned prior this point because
8501 * we need to commit changes in order to let mUserData be shared by all
8502 * snapshot machine instances.
8503 */
8504 mUserData.commitCopy();
8505
8506 // machine registry, if present (must be loaded before snapshots)
8507 if (config.canHaveOwnMediaRegistry())
8508 {
8509 // determine machine folder
8510 Utf8Str strMachineFolder = i_getSettingsFileFull();
8511 strMachineFolder.stripFilename();
8512 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8513 config.mediaRegistry,
8514 strMachineFolder);
8515 if (FAILED(rc)) return rc;
8516 }
8517
8518 /* Snapshot node (optional) */
8519 size_t cRootSnapshots;
8520 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8521 {
8522 // there must be only one root snapshot
8523 Assert(cRootSnapshots == 1);
8524
8525 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8526
8527 rc = i_loadSnapshot(snap,
8528 config.uuidCurrentSnapshot,
8529 NULL); // no parent == first snapshot
8530 if (FAILED(rc)) return rc;
8531 }
8532
8533 // hardware data
8534 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8535 if (FAILED(rc)) return rc;
8536
8537 /*
8538 * NOTE: the assignment below must be the last thing to do,
8539 * otherwise it will be not possible to change the settings
8540 * somewhere in the code above because all setters will be
8541 * blocked by i_checkStateDependency(MutableStateDep).
8542 */
8543
8544 /* set the machine state to Aborted or Saved when appropriate */
8545 if (config.fAborted)
8546 {
8547 mSSData->strStateFilePath.setNull();
8548
8549 /* no need to use i_setMachineState() during init() */
8550 mData->mMachineState = MachineState_Aborted;
8551 }
8552 else if (!mSSData->strStateFilePath.isEmpty())
8553 {
8554 /* no need to use i_setMachineState() during init() */
8555 mData->mMachineState = MachineState_Saved;
8556 }
8557
8558 // after loading settings, we are no longer different from the XML on disk
8559 mData->flModifications = 0;
8560
8561 return S_OK;
8562}
8563
8564/**
8565 * Recursively loads all snapshots starting from the given.
8566 *
8567 * @param data snapshot settings.
8568 * @param aCurSnapshotId Current snapshot ID from the settings file.
8569 * @param aParentSnapshot Parent snapshot.
8570 */
8571HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8572 const Guid &aCurSnapshotId,
8573 Snapshot *aParentSnapshot)
8574{
8575 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8576 AssertReturn(!i_isSessionMachine(), E_FAIL);
8577
8578 HRESULT rc = S_OK;
8579
8580 Utf8Str strStateFile;
8581 if (!data.strStateFile.isEmpty())
8582 {
8583 /* optional */
8584 strStateFile = data.strStateFile;
8585 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8586 if (RT_FAILURE(vrc))
8587 return setErrorBoth(E_FAIL, vrc,
8588 tr("Invalid saved state file path '%s' (%Rrc)"),
8589 strStateFile.c_str(),
8590 vrc);
8591 }
8592
8593 /* create a snapshot machine object */
8594 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8595 pSnapshotMachine.createObject();
8596 rc = pSnapshotMachine->initFromSettings(this,
8597 data.hardware,
8598 &data.debugging,
8599 &data.autostart,
8600 data.uuid.ref(),
8601 strStateFile);
8602 if (FAILED(rc)) return rc;
8603
8604 /* create a snapshot object */
8605 ComObjPtr<Snapshot> pSnapshot;
8606 pSnapshot.createObject();
8607 /* initialize the snapshot */
8608 rc = pSnapshot->init(mParent, // VirtualBox object
8609 data.uuid,
8610 data.strName,
8611 data.strDescription,
8612 data.timestamp,
8613 pSnapshotMachine,
8614 aParentSnapshot);
8615 if (FAILED(rc)) return rc;
8616
8617 /* memorize the first snapshot if necessary */
8618 if (!mData->mFirstSnapshot)
8619 mData->mFirstSnapshot = pSnapshot;
8620
8621 /* memorize the current snapshot when appropriate */
8622 if ( !mData->mCurrentSnapshot
8623 && pSnapshot->i_getId() == aCurSnapshotId
8624 )
8625 mData->mCurrentSnapshot = pSnapshot;
8626
8627 // now create the children
8628 for (settings::SnapshotsList::const_iterator
8629 it = data.llChildSnapshots.begin();
8630 it != data.llChildSnapshots.end();
8631 ++it)
8632 {
8633 const settings::Snapshot &childData = *it;
8634 // recurse
8635 rc = i_loadSnapshot(childData,
8636 aCurSnapshotId,
8637 pSnapshot); // parent = the one we created above
8638 if (FAILED(rc)) return rc;
8639 }
8640
8641 return rc;
8642}
8643
8644/**
8645 * Loads settings into mHWData.
8646 *
8647 * @param puuidRegistry Registry ID.
8648 * @param puuidSnapshot Snapshot ID
8649 * @param data Reference to the hardware settings.
8650 * @param pDbg Pointer to the debugging settings.
8651 * @param pAutostart Pointer to the autostart settings.
8652 */
8653HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8654 const Guid *puuidSnapshot,
8655 const settings::Hardware &data,
8656 const settings::Debugging *pDbg,
8657 const settings::Autostart *pAutostart)
8658{
8659 AssertReturn(!i_isSessionMachine(), E_FAIL);
8660
8661 HRESULT rc = S_OK;
8662
8663 try
8664 {
8665 ComObjPtr<GuestOSType> pGuestOSType;
8666 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8667
8668 /* The hardware version attribute (optional). */
8669 mHWData->mHWVersion = data.strVersion;
8670 mHWData->mHardwareUUID = data.uuid;
8671
8672 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8673 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8674 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8675 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8676 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8677 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8678 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8679 mHWData->mPAEEnabled = data.fPAE;
8680 mHWData->mLongMode = data.enmLongMode;
8681 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8682 mHWData->mAPIC = data.fAPIC;
8683 mHWData->mX2APIC = data.fX2APIC;
8684 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8685 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8686 mHWData->mSpecCtrl = data.fSpecCtrl;
8687 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8688 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8689 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8690 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8691 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8692 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8693 mHWData->mCPUCount = data.cCPUs;
8694 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8695 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8696 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8697 mHWData->mCpuProfile = data.strCpuProfile;
8698
8699 // cpu
8700 if (mHWData->mCPUHotPlugEnabled)
8701 {
8702 for (settings::CpuList::const_iterator
8703 it = data.llCpus.begin();
8704 it != data.llCpus.end();
8705 ++it)
8706 {
8707 const settings::Cpu &cpu = *it;
8708
8709 mHWData->mCPUAttached[cpu.ulId] = true;
8710 }
8711 }
8712
8713 // cpuid leafs
8714 for (settings::CpuIdLeafsList::const_iterator
8715 it = data.llCpuIdLeafs.begin();
8716 it != data.llCpuIdLeafs.end();
8717 ++it)
8718 {
8719 const settings::CpuIdLeaf &rLeaf= *it;
8720 if ( rLeaf.idx < UINT32_C(0x20)
8721 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8722 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8723 mHWData->mCpuIdLeafList.push_back(rLeaf);
8724 /* else: just ignore */
8725 }
8726
8727 mHWData->mMemorySize = data.ulMemorySizeMB;
8728 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8729
8730 // boot order
8731 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8732 {
8733 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8734 if (it == data.mapBootOrder.end())
8735 mHWData->mBootOrder[i] = DeviceType_Null;
8736 else
8737 mHWData->mBootOrder[i] = it->second;
8738 }
8739
8740 mHWData->mFirmwareType = data.firmwareType;
8741 mHWData->mPointingHIDType = data.pointingHIDType;
8742 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8743 mHWData->mChipsetType = data.chipsetType;
8744 mHWData->mParavirtProvider = data.paravirtProvider;
8745 mHWData->mParavirtDebug = data.strParavirtDebug;
8746 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8747 mHWData->mHPETEnabled = data.fHPETEnabled;
8748
8749 /* GraphicsAdapter */
8750 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8751 if (FAILED(rc)) return rc;
8752
8753 /* VRDEServer */
8754 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8755 if (FAILED(rc)) return rc;
8756
8757 /* BIOS */
8758 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8759 if (FAILED(rc)) return rc;
8760
8761 /* Recording settings */
8762 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8763 if (FAILED(rc)) return rc;
8764
8765 // Bandwidth control (must come before network adapters)
8766 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8767 if (FAILED(rc)) return rc;
8768
8769 /* USB controllers */
8770 for (settings::USBControllerList::const_iterator
8771 it = data.usbSettings.llUSBControllers.begin();
8772 it != data.usbSettings.llUSBControllers.end();
8773 ++it)
8774 {
8775 const settings::USBController &settingsCtrl = *it;
8776 ComObjPtr<USBController> newCtrl;
8777
8778 newCtrl.createObject();
8779 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8780 mUSBControllers->push_back(newCtrl);
8781 }
8782
8783 /* USB device filters */
8784 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8785 if (FAILED(rc)) return rc;
8786
8787 // network adapters (establish array size first and apply defaults, to
8788 // ensure reading the same settings as we saved, since the list skips
8789 // adapters having defaults)
8790 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8791 size_t oldCount = mNetworkAdapters.size();
8792 if (newCount > oldCount)
8793 {
8794 mNetworkAdapters.resize(newCount);
8795 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8796 {
8797 unconst(mNetworkAdapters[slot]).createObject();
8798 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8799 }
8800 }
8801 else if (newCount < oldCount)
8802 mNetworkAdapters.resize(newCount);
8803 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8804 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8805 for (settings::NetworkAdaptersList::const_iterator
8806 it = data.llNetworkAdapters.begin();
8807 it != data.llNetworkAdapters.end();
8808 ++it)
8809 {
8810 const settings::NetworkAdapter &nic = *it;
8811
8812 /* slot uniqueness is guaranteed by XML Schema */
8813 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8814 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8815 if (FAILED(rc)) return rc;
8816 }
8817
8818 // serial ports (establish defaults first, to ensure reading the same
8819 // settings as we saved, since the list skips ports having defaults)
8820 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8821 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8822 for (settings::SerialPortsList::const_iterator
8823 it = data.llSerialPorts.begin();
8824 it != data.llSerialPorts.end();
8825 ++it)
8826 {
8827 const settings::SerialPort &s = *it;
8828
8829 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8830 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8831 if (FAILED(rc)) return rc;
8832 }
8833
8834 // parallel ports (establish defaults first, to ensure reading the same
8835 // settings as we saved, since the list skips ports having defaults)
8836 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8837 mParallelPorts[i]->i_applyDefaults();
8838 for (settings::ParallelPortsList::const_iterator
8839 it = data.llParallelPorts.begin();
8840 it != data.llParallelPorts.end();
8841 ++it)
8842 {
8843 const settings::ParallelPort &p = *it;
8844
8845 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8846 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8847 if (FAILED(rc)) return rc;
8848 }
8849
8850 /* AudioAdapter */
8851 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8852 if (FAILED(rc)) return rc;
8853
8854 /* storage controllers */
8855 rc = i_loadStorageControllers(data.storage,
8856 puuidRegistry,
8857 puuidSnapshot);
8858 if (FAILED(rc)) return rc;
8859
8860 /* Shared folders */
8861 for (settings::SharedFoldersList::const_iterator
8862 it = data.llSharedFolders.begin();
8863 it != data.llSharedFolders.end();
8864 ++it)
8865 {
8866 const settings::SharedFolder &sf = *it;
8867
8868 ComObjPtr<SharedFolder> sharedFolder;
8869 /* Check for double entries. Not allowed! */
8870 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8871 if (SUCCEEDED(rc))
8872 return setError(VBOX_E_OBJECT_IN_USE,
8873 tr("Shared folder named '%s' already exists"),
8874 sf.strName.c_str());
8875
8876 /* Create the new shared folder. Don't break on error. This will be
8877 * reported when the machine starts. */
8878 sharedFolder.createObject();
8879 rc = sharedFolder->init(i_getMachine(),
8880 sf.strName,
8881 sf.strHostPath,
8882 RT_BOOL(sf.fWritable),
8883 RT_BOOL(sf.fAutoMount),
8884 sf.strAutoMountPoint,
8885 false /* fFailOnError */);
8886 if (FAILED(rc)) return rc;
8887 mHWData->mSharedFolders.push_back(sharedFolder);
8888 }
8889
8890 // Clipboard
8891 mHWData->mClipboardMode = data.clipboardMode;
8892 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8893
8894 // drag'n'drop
8895 mHWData->mDnDMode = data.dndMode;
8896
8897 // guest settings
8898 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8899
8900 // IO settings
8901 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8902 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8903
8904 // Host PCI devices
8905 for (settings::HostPCIDeviceAttachmentList::const_iterator
8906 it = data.pciAttachments.begin();
8907 it != data.pciAttachments.end();
8908 ++it)
8909 {
8910 const settings::HostPCIDeviceAttachment &hpda = *it;
8911 ComObjPtr<PCIDeviceAttachment> pda;
8912
8913 pda.createObject();
8914 pda->i_loadSettings(this, hpda);
8915 mHWData->mPCIDeviceAssignments.push_back(pda);
8916 }
8917
8918 /*
8919 * (The following isn't really real hardware, but it lives in HWData
8920 * for reasons of convenience.)
8921 */
8922
8923#ifdef VBOX_WITH_GUEST_PROPS
8924 /* Guest properties (optional) */
8925
8926 /* Only load transient guest properties for configs which have saved
8927 * state, because there shouldn't be any for powered off VMs. The same
8928 * logic applies for snapshots, as offline snapshots shouldn't have
8929 * any such properties. They confuse the code in various places.
8930 * Note: can't rely on the machine state, as it isn't set yet. */
8931 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8932 /* apologies for the hacky unconst() usage, but this needs hacking
8933 * actually inconsistent settings into consistency, otherwise there
8934 * will be some corner cases where the inconsistency survives
8935 * surprisingly long without getting fixed, especially for snapshots
8936 * as there are no config changes. */
8937 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8938 for (settings::GuestPropertiesList::iterator
8939 it = llGuestProperties.begin();
8940 it != llGuestProperties.end();
8941 /*nothing*/)
8942 {
8943 const settings::GuestProperty &prop = *it;
8944 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8945 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8946 if ( fSkipTransientGuestProperties
8947 && ( fFlags & GUEST_PROP_F_TRANSIENT
8948 || fFlags & GUEST_PROP_F_TRANSRESET))
8949 {
8950 it = llGuestProperties.erase(it);
8951 continue;
8952 }
8953 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8954 mHWData->mGuestProperties[prop.strName] = property;
8955 ++it;
8956 }
8957#endif /* VBOX_WITH_GUEST_PROPS defined */
8958
8959 rc = i_loadDebugging(pDbg);
8960 if (FAILED(rc))
8961 return rc;
8962
8963 mHWData->mAutostart = *pAutostart;
8964
8965 /* default frontend */
8966 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8967 }
8968 catch (std::bad_alloc &)
8969 {
8970 return E_OUTOFMEMORY;
8971 }
8972
8973 AssertComRC(rc);
8974 return rc;
8975}
8976
8977/**
8978 * Called from i_loadHardware() to load the debugging settings of the
8979 * machine.
8980 *
8981 * @param pDbg Pointer to the settings.
8982 */
8983HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8984{
8985 mHWData->mDebugging = *pDbg;
8986 /* no more processing currently required, this will probably change. */
8987 return S_OK;
8988}
8989
8990/**
8991 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8992 *
8993 * @param data storage settings.
8994 * @param puuidRegistry media registry ID to set media to or NULL;
8995 * see Machine::i_loadMachineDataFromSettings()
8996 * @param puuidSnapshot snapshot ID
8997 * @return
8998 */
8999HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9000 const Guid *puuidRegistry,
9001 const Guid *puuidSnapshot)
9002{
9003 AssertReturn(!i_isSessionMachine(), E_FAIL);
9004
9005 HRESULT rc = S_OK;
9006
9007 for (settings::StorageControllersList::const_iterator
9008 it = data.llStorageControllers.begin();
9009 it != data.llStorageControllers.end();
9010 ++it)
9011 {
9012 const settings::StorageController &ctlData = *it;
9013
9014 ComObjPtr<StorageController> pCtl;
9015 /* Try to find one with the name first. */
9016 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9017 if (SUCCEEDED(rc))
9018 return setError(VBOX_E_OBJECT_IN_USE,
9019 tr("Storage controller named '%s' already exists"),
9020 ctlData.strName.c_str());
9021
9022 pCtl.createObject();
9023 rc = pCtl->init(this,
9024 ctlData.strName,
9025 ctlData.storageBus,
9026 ctlData.ulInstance,
9027 ctlData.fBootable);
9028 if (FAILED(rc)) return rc;
9029
9030 mStorageControllers->push_back(pCtl);
9031
9032 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9033 if (FAILED(rc)) return rc;
9034
9035 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9036 if (FAILED(rc)) return rc;
9037
9038 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9039 if (FAILED(rc)) return rc;
9040
9041 /* Load the attached devices now. */
9042 rc = i_loadStorageDevices(pCtl,
9043 ctlData,
9044 puuidRegistry,
9045 puuidSnapshot);
9046 if (FAILED(rc)) return rc;
9047 }
9048
9049 return S_OK;
9050}
9051
9052/**
9053 * Called from i_loadStorageControllers for a controller's devices.
9054 *
9055 * @param aStorageController
9056 * @param data
9057 * @param puuidRegistry media registry ID to set media to or NULL; see
9058 * Machine::i_loadMachineDataFromSettings()
9059 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9060 * @return
9061 */
9062HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9063 const settings::StorageController &data,
9064 const Guid *puuidRegistry,
9065 const Guid *puuidSnapshot)
9066{
9067 HRESULT rc = S_OK;
9068
9069 /* paranoia: detect duplicate attachments */
9070 for (settings::AttachedDevicesList::const_iterator
9071 it = data.llAttachedDevices.begin();
9072 it != data.llAttachedDevices.end();
9073 ++it)
9074 {
9075 const settings::AttachedDevice &ad = *it;
9076
9077 for (settings::AttachedDevicesList::const_iterator it2 = it;
9078 it2 != data.llAttachedDevices.end();
9079 ++it2)
9080 {
9081 if (it == it2)
9082 continue;
9083
9084 const settings::AttachedDevice &ad2 = *it2;
9085
9086 if ( ad.lPort == ad2.lPort
9087 && ad.lDevice == ad2.lDevice)
9088 {
9089 return setError(E_FAIL,
9090 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9091 aStorageController->i_getName().c_str(),
9092 ad.lPort,
9093 ad.lDevice,
9094 mUserData->s.strName.c_str());
9095 }
9096 }
9097 }
9098
9099 for (settings::AttachedDevicesList::const_iterator
9100 it = data.llAttachedDevices.begin();
9101 it != data.llAttachedDevices.end();
9102 ++it)
9103 {
9104 const settings::AttachedDevice &dev = *it;
9105 ComObjPtr<Medium> medium;
9106
9107 switch (dev.deviceType)
9108 {
9109 case DeviceType_Floppy:
9110 case DeviceType_DVD:
9111 if (dev.strHostDriveSrc.isNotEmpty())
9112 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9113 false /* fRefresh */, medium);
9114 else
9115 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9116 dev.uuid,
9117 false /* fRefresh */,
9118 false /* aSetError */,
9119 medium);
9120 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9121 // This is not an error. The host drive or UUID might have vanished, so just go
9122 // ahead without this removeable medium attachment
9123 rc = S_OK;
9124 break;
9125
9126 case DeviceType_HardDisk:
9127 {
9128 /* find a hard disk by UUID */
9129 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9130 if (FAILED(rc))
9131 {
9132 if (i_isSnapshotMachine())
9133 {
9134 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9135 // so the user knows that the bad disk is in a snapshot somewhere
9136 com::ErrorInfo info;
9137 return setError(E_FAIL,
9138 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9139 puuidSnapshot->raw(),
9140 info.getText().raw());
9141 }
9142 else
9143 return rc;
9144 }
9145
9146 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9147
9148 if (medium->i_getType() == MediumType_Immutable)
9149 {
9150 if (i_isSnapshotMachine())
9151 return setError(E_FAIL,
9152 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9153 "of the virtual machine '%s' ('%s')"),
9154 medium->i_getLocationFull().c_str(),
9155 dev.uuid.raw(),
9156 puuidSnapshot->raw(),
9157 mUserData->s.strName.c_str(),
9158 mData->m_strConfigFileFull.c_str());
9159
9160 return setError(E_FAIL,
9161 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9162 medium->i_getLocationFull().c_str(),
9163 dev.uuid.raw(),
9164 mUserData->s.strName.c_str(),
9165 mData->m_strConfigFileFull.c_str());
9166 }
9167
9168 if (medium->i_getType() == MediumType_MultiAttach)
9169 {
9170 if (i_isSnapshotMachine())
9171 return setError(E_FAIL,
9172 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9173 "of the virtual machine '%s' ('%s')"),
9174 medium->i_getLocationFull().c_str(),
9175 dev.uuid.raw(),
9176 puuidSnapshot->raw(),
9177 mUserData->s.strName.c_str(),
9178 mData->m_strConfigFileFull.c_str());
9179
9180 return setError(E_FAIL,
9181 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9182 medium->i_getLocationFull().c_str(),
9183 dev.uuid.raw(),
9184 mUserData->s.strName.c_str(),
9185 mData->m_strConfigFileFull.c_str());
9186 }
9187
9188 if ( !i_isSnapshotMachine()
9189 && medium->i_getChildren().size() != 0
9190 )
9191 return setError(E_FAIL,
9192 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9193 "because it has %d differencing child hard disks"),
9194 medium->i_getLocationFull().c_str(),
9195 dev.uuid.raw(),
9196 mUserData->s.strName.c_str(),
9197 mData->m_strConfigFileFull.c_str(),
9198 medium->i_getChildren().size());
9199
9200 if (i_findAttachment(*mMediumAttachments.data(),
9201 medium))
9202 return setError(E_FAIL,
9203 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9204 medium->i_getLocationFull().c_str(),
9205 dev.uuid.raw(),
9206 mUserData->s.strName.c_str(),
9207 mData->m_strConfigFileFull.c_str());
9208
9209 break;
9210 }
9211
9212 default:
9213 return setError(E_FAIL,
9214 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9215 medium->i_getLocationFull().c_str(),
9216 mUserData->s.strName.c_str(),
9217 mData->m_strConfigFileFull.c_str());
9218 }
9219
9220 if (FAILED(rc))
9221 break;
9222
9223 /* Bandwidth groups are loaded at this point. */
9224 ComObjPtr<BandwidthGroup> pBwGroup;
9225
9226 if (!dev.strBwGroup.isEmpty())
9227 {
9228 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9229 if (FAILED(rc))
9230 return setError(E_FAIL,
9231 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9232 medium->i_getLocationFull().c_str(),
9233 dev.strBwGroup.c_str(),
9234 mUserData->s.strName.c_str(),
9235 mData->m_strConfigFileFull.c_str());
9236 pBwGroup->i_reference();
9237 }
9238
9239 const Utf8Str controllerName = aStorageController->i_getName();
9240 ComObjPtr<MediumAttachment> pAttachment;
9241 pAttachment.createObject();
9242 rc = pAttachment->init(this,
9243 medium,
9244 controllerName,
9245 dev.lPort,
9246 dev.lDevice,
9247 dev.deviceType,
9248 false,
9249 dev.fPassThrough,
9250 dev.fTempEject,
9251 dev.fNonRotational,
9252 dev.fDiscard,
9253 dev.fHotPluggable,
9254 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9255 if (FAILED(rc)) break;
9256
9257 /* associate the medium with this machine and snapshot */
9258 if (!medium.isNull())
9259 {
9260 AutoCaller medCaller(medium);
9261 if (FAILED(medCaller.rc())) return medCaller.rc();
9262 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9263
9264 if (i_isSnapshotMachine())
9265 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9266 else
9267 rc = medium->i_addBackReference(mData->mUuid);
9268 /* If the medium->addBackReference fails it sets an appropriate
9269 * error message, so no need to do any guesswork here. */
9270
9271 if (puuidRegistry)
9272 // caller wants registry ID to be set on all attached media (OVF import case)
9273 medium->i_addRegistry(*puuidRegistry);
9274 }
9275
9276 if (FAILED(rc))
9277 break;
9278
9279 /* back up mMediumAttachments to let registeredInit() properly rollback
9280 * on failure (= limited accessibility) */
9281 i_setModified(IsModified_Storage);
9282 mMediumAttachments.backup();
9283 mMediumAttachments->push_back(pAttachment);
9284 }
9285
9286 return rc;
9287}
9288
9289/**
9290 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9291 *
9292 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9293 * @param aSnapshot where to return the found snapshot
9294 * @param aSetError true to set extended error info on failure
9295 */
9296HRESULT Machine::i_findSnapshotById(const Guid &aId,
9297 ComObjPtr<Snapshot> &aSnapshot,
9298 bool aSetError /* = false */)
9299{
9300 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9301
9302 if (!mData->mFirstSnapshot)
9303 {
9304 if (aSetError)
9305 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9306 return E_FAIL;
9307 }
9308
9309 if (aId.isZero())
9310 aSnapshot = mData->mFirstSnapshot;
9311 else
9312 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9313
9314 if (!aSnapshot)
9315 {
9316 if (aSetError)
9317 return setError(E_FAIL,
9318 tr("Could not find a snapshot with UUID {%s}"),
9319 aId.toString().c_str());
9320 return E_FAIL;
9321 }
9322
9323 return S_OK;
9324}
9325
9326/**
9327 * Returns the snapshot with the given name or fails of no such snapshot.
9328 *
9329 * @param strName snapshot name to find
9330 * @param aSnapshot where to return the found snapshot
9331 * @param aSetError true to set extended error info on failure
9332 */
9333HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9334 ComObjPtr<Snapshot> &aSnapshot,
9335 bool aSetError /* = false */)
9336{
9337 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9338
9339 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9340
9341 if (!mData->mFirstSnapshot)
9342 {
9343 if (aSetError)
9344 return setError(VBOX_E_OBJECT_NOT_FOUND,
9345 tr("This machine does not have any snapshots"));
9346 return VBOX_E_OBJECT_NOT_FOUND;
9347 }
9348
9349 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9350
9351 if (!aSnapshot)
9352 {
9353 if (aSetError)
9354 return setError(VBOX_E_OBJECT_NOT_FOUND,
9355 tr("Could not find a snapshot named '%s'"), strName.c_str());
9356 return VBOX_E_OBJECT_NOT_FOUND;
9357 }
9358
9359 return S_OK;
9360}
9361
9362/**
9363 * Returns a storage controller object with the given name.
9364 *
9365 * @param aName storage controller name to find
9366 * @param aStorageController where to return the found storage controller
9367 * @param aSetError true to set extended error info on failure
9368 */
9369HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9370 ComObjPtr<StorageController> &aStorageController,
9371 bool aSetError /* = false */)
9372{
9373 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9374
9375 for (StorageControllerList::const_iterator
9376 it = mStorageControllers->begin();
9377 it != mStorageControllers->end();
9378 ++it)
9379 {
9380 if ((*it)->i_getName() == aName)
9381 {
9382 aStorageController = (*it);
9383 return S_OK;
9384 }
9385 }
9386
9387 if (aSetError)
9388 return setError(VBOX_E_OBJECT_NOT_FOUND,
9389 tr("Could not find a storage controller named '%s'"),
9390 aName.c_str());
9391 return VBOX_E_OBJECT_NOT_FOUND;
9392}
9393
9394/**
9395 * Returns a USB controller object with the given name.
9396 *
9397 * @param aName USB controller name to find
9398 * @param aUSBController where to return the found USB controller
9399 * @param aSetError true to set extended error info on failure
9400 */
9401HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9402 ComObjPtr<USBController> &aUSBController,
9403 bool aSetError /* = false */)
9404{
9405 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9406
9407 for (USBControllerList::const_iterator
9408 it = mUSBControllers->begin();
9409 it != mUSBControllers->end();
9410 ++it)
9411 {
9412 if ((*it)->i_getName() == aName)
9413 {
9414 aUSBController = (*it);
9415 return S_OK;
9416 }
9417 }
9418
9419 if (aSetError)
9420 return setError(VBOX_E_OBJECT_NOT_FOUND,
9421 tr("Could not find a storage controller named '%s'"),
9422 aName.c_str());
9423 return VBOX_E_OBJECT_NOT_FOUND;
9424}
9425
9426/**
9427 * Returns the number of USB controller instance of the given type.
9428 *
9429 * @param enmType USB controller type.
9430 */
9431ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9432{
9433 ULONG cCtrls = 0;
9434
9435 for (USBControllerList::const_iterator
9436 it = mUSBControllers->begin();
9437 it != mUSBControllers->end();
9438 ++it)
9439 {
9440 if ((*it)->i_getControllerType() == enmType)
9441 cCtrls++;
9442 }
9443
9444 return cCtrls;
9445}
9446
9447HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9448 MediumAttachmentList &atts)
9449{
9450 AutoCaller autoCaller(this);
9451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9452
9453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9454
9455 for (MediumAttachmentList::const_iterator
9456 it = mMediumAttachments->begin();
9457 it != mMediumAttachments->end();
9458 ++it)
9459 {
9460 const ComObjPtr<MediumAttachment> &pAtt = *it;
9461 // should never happen, but deal with NULL pointers in the list.
9462 AssertContinue(!pAtt.isNull());
9463
9464 // getControllerName() needs caller+read lock
9465 AutoCaller autoAttCaller(pAtt);
9466 if (FAILED(autoAttCaller.rc()))
9467 {
9468 atts.clear();
9469 return autoAttCaller.rc();
9470 }
9471 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9472
9473 if (pAtt->i_getControllerName() == aName)
9474 atts.push_back(pAtt);
9475 }
9476
9477 return S_OK;
9478}
9479
9480
9481/**
9482 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9483 * file if the machine name was changed and about creating a new settings file
9484 * if this is a new machine.
9485 *
9486 * @note Must be never called directly but only from #saveSettings().
9487 */
9488HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9489{
9490 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9491
9492 HRESULT rc = S_OK;
9493
9494 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9495
9496 /// @todo need to handle primary group change, too
9497
9498 /* attempt to rename the settings file if machine name is changed */
9499 if ( mUserData->s.fNameSync
9500 && mUserData.isBackedUp()
9501 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9502 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9503 )
9504 {
9505 bool dirRenamed = false;
9506 bool fileRenamed = false;
9507
9508 Utf8Str configFile, newConfigFile;
9509 Utf8Str configFilePrev, newConfigFilePrev;
9510 Utf8Str NVRAMFile, newNVRAMFile;
9511 Utf8Str configDir, newConfigDir;
9512
9513 do
9514 {
9515 int vrc = VINF_SUCCESS;
9516
9517 Utf8Str name = mUserData.backedUpData()->s.strName;
9518 Utf8Str newName = mUserData->s.strName;
9519 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9520 if (group == "/")
9521 group.setNull();
9522 Utf8Str newGroup = mUserData->s.llGroups.front();
9523 if (newGroup == "/")
9524 newGroup.setNull();
9525
9526 configFile = mData->m_strConfigFileFull;
9527
9528 /* first, rename the directory if it matches the group and machine name */
9529 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9530 /** @todo hack, make somehow use of ComposeMachineFilename */
9531 if (mUserData->s.fDirectoryIncludesUUID)
9532 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9533 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9534 /** @todo hack, make somehow use of ComposeMachineFilename */
9535 if (mUserData->s.fDirectoryIncludesUUID)
9536 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9537 configDir = configFile;
9538 configDir.stripFilename();
9539 newConfigDir = configDir;
9540 if ( configDir.length() >= groupPlusName.length()
9541 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9542 groupPlusName.c_str()))
9543 {
9544 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9545 Utf8Str newConfigBaseDir(newConfigDir);
9546 newConfigDir.append(newGroupPlusName);
9547 /* consistency: use \ if appropriate on the platform */
9548 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9549 /* new dir and old dir cannot be equal here because of 'if'
9550 * above and because name != newName */
9551 Assert(configDir != newConfigDir);
9552 if (!fSettingsFileIsNew)
9553 {
9554 /* perform real rename only if the machine is not new */
9555 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9556 if ( vrc == VERR_FILE_NOT_FOUND
9557 || vrc == VERR_PATH_NOT_FOUND)
9558 {
9559 /* create the parent directory, then retry renaming */
9560 Utf8Str parent(newConfigDir);
9561 parent.stripFilename();
9562 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9563 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9564 }
9565 if (RT_FAILURE(vrc))
9566 {
9567 rc = setErrorBoth(E_FAIL, vrc,
9568 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9569 configDir.c_str(),
9570 newConfigDir.c_str(),
9571 vrc);
9572 break;
9573 }
9574 /* delete subdirectories which are no longer needed */
9575 Utf8Str dir(configDir);
9576 dir.stripFilename();
9577 while (dir != newConfigBaseDir && dir != ".")
9578 {
9579 vrc = RTDirRemove(dir.c_str());
9580 if (RT_FAILURE(vrc))
9581 break;
9582 dir.stripFilename();
9583 }
9584 dirRenamed = true;
9585 }
9586 }
9587
9588 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9589
9590 /* then try to rename the settings file itself */
9591 if (newConfigFile != configFile)
9592 {
9593 /* get the path to old settings file in renamed directory */
9594 configFile.printf("%s%c%s",
9595 newConfigDir.c_str(),
9596 RTPATH_DELIMITER,
9597 RTPathFilename(configFile.c_str()));
9598 if (!fSettingsFileIsNew)
9599 {
9600 /* perform real rename only if the machine is not new */
9601 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9602 if (RT_FAILURE(vrc))
9603 {
9604 rc = setErrorBoth(E_FAIL, vrc,
9605 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9606 configFile.c_str(),
9607 newConfigFile.c_str(),
9608 vrc);
9609 break;
9610 }
9611 fileRenamed = true;
9612 configFilePrev = configFile;
9613 configFilePrev += "-prev";
9614 newConfigFilePrev = newConfigFile;
9615 newConfigFilePrev += "-prev";
9616 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9617 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9618 if (NVRAMFile.isNotEmpty())
9619 {
9620 // in the NVRAM file path, replace the old directory with the new directory
9621 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9622 {
9623 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9624 NVRAMFile = newConfigDir + strNVRAMFile;
9625 }
9626 newNVRAMFile = newConfigFile;
9627 newNVRAMFile.stripSuffix();
9628 newNVRAMFile += ".nvram";
9629 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9630 }
9631 }
9632 }
9633
9634 // update m_strConfigFileFull amd mConfigFile
9635 mData->m_strConfigFileFull = newConfigFile;
9636 // compute the relative path too
9637 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9638
9639 // store the old and new so that VirtualBox::i_saveSettings() can update
9640 // the media registry
9641 if ( mData->mRegistered
9642 && (configDir != newConfigDir || configFile != newConfigFile))
9643 {
9644 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9645
9646 if (pfNeedsGlobalSaveSettings)
9647 *pfNeedsGlobalSaveSettings = true;
9648 }
9649
9650 // in the saved state file path, replace the old directory with the new directory
9651 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9652 {
9653 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9654 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9655 }
9656 if (newNVRAMFile.isNotEmpty())
9657 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9658
9659 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9660 if (mData->mFirstSnapshot)
9661 {
9662 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9663 newConfigDir.c_str());
9664 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9665 newConfigDir.c_str());
9666 }
9667 }
9668 while (0);
9669
9670 if (FAILED(rc))
9671 {
9672 /* silently try to rename everything back */
9673 if (fileRenamed)
9674 {
9675 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9676 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9677 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9678 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9679 }
9680 if (dirRenamed)
9681 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9682 }
9683
9684 if (FAILED(rc)) return rc;
9685 }
9686
9687 if (fSettingsFileIsNew)
9688 {
9689 /* create a virgin config file */
9690 int vrc = VINF_SUCCESS;
9691
9692 /* ensure the settings directory exists */
9693 Utf8Str path(mData->m_strConfigFileFull);
9694 path.stripFilename();
9695 if (!RTDirExists(path.c_str()))
9696 {
9697 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9698 if (RT_FAILURE(vrc))
9699 {
9700 return setErrorBoth(E_FAIL, vrc,
9701 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9702 path.c_str(),
9703 vrc);
9704 }
9705 }
9706
9707 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9708 path = Utf8Str(mData->m_strConfigFileFull);
9709 RTFILE f = NIL_RTFILE;
9710 vrc = RTFileOpen(&f, path.c_str(),
9711 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9712 if (RT_FAILURE(vrc))
9713 return setErrorBoth(E_FAIL, vrc,
9714 tr("Could not create the settings file '%s' (%Rrc)"),
9715 path.c_str(),
9716 vrc);
9717 RTFileClose(f);
9718 }
9719
9720 return rc;
9721}
9722
9723/**
9724 * Saves and commits machine data, user data and hardware data.
9725 *
9726 * Note that on failure, the data remains uncommitted.
9727 *
9728 * @a aFlags may combine the following flags:
9729 *
9730 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9731 * Used when saving settings after an operation that makes them 100%
9732 * correspond to the settings from the current snapshot.
9733 * - SaveS_Force: settings will be saved without doing a deep compare of the
9734 * settings structures. This is used when this is called because snapshots
9735 * have changed to avoid the overhead of the deep compare.
9736 *
9737 * @note Must be called from under this object's write lock. Locks children for
9738 * writing.
9739 *
9740 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9741 * initialized to false and that will be set to true by this function if
9742 * the caller must invoke VirtualBox::i_saveSettings() because the global
9743 * settings have changed. This will happen if a machine rename has been
9744 * saved and the global machine and media registries will therefore need
9745 * updating.
9746 * @param aFlags Flags.
9747 */
9748HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9749 int aFlags /*= 0*/)
9750{
9751 LogFlowThisFuncEnter();
9752
9753 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9754
9755 /* make sure child objects are unable to modify the settings while we are
9756 * saving them */
9757 i_ensureNoStateDependencies();
9758
9759 AssertReturn(!i_isSnapshotMachine(),
9760 E_FAIL);
9761
9762 if (!mData->mAccessible)
9763 return setError(VBOX_E_INVALID_VM_STATE,
9764 tr("The machine is not accessible, so cannot save settings"));
9765
9766 HRESULT rc = S_OK;
9767 bool fNeedsWrite = false;
9768
9769 /* First, prepare to save settings. It will care about renaming the
9770 * settings directory and file if the machine name was changed and about
9771 * creating a new settings file if this is a new machine. */
9772 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9773 if (FAILED(rc)) return rc;
9774
9775 // keep a pointer to the current settings structures
9776 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9777 settings::MachineConfigFile *pNewConfig = NULL;
9778
9779 try
9780 {
9781 // make a fresh one to have everyone write stuff into
9782 pNewConfig = new settings::MachineConfigFile(NULL);
9783 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9784
9785 // now go and copy all the settings data from COM to the settings structures
9786 // (this calls i_saveSettings() on all the COM objects in the machine)
9787 i_copyMachineDataToSettings(*pNewConfig);
9788
9789 if (aFlags & SaveS_ResetCurStateModified)
9790 {
9791 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9792 mData->mCurrentStateModified = FALSE;
9793 fNeedsWrite = true; // always, no need to compare
9794 }
9795 else if (aFlags & SaveS_Force)
9796 {
9797 fNeedsWrite = true; // always, no need to compare
9798 }
9799 else
9800 {
9801 if (!mData->mCurrentStateModified)
9802 {
9803 // do a deep compare of the settings that we just saved with the settings
9804 // previously stored in the config file; this invokes MachineConfigFile::operator==
9805 // which does a deep compare of all the settings, which is expensive but less expensive
9806 // than writing out XML in vain
9807 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9808
9809 // could still be modified if any settings changed
9810 mData->mCurrentStateModified = fAnySettingsChanged;
9811
9812 fNeedsWrite = fAnySettingsChanged;
9813 }
9814 else
9815 fNeedsWrite = true;
9816 }
9817
9818 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9819
9820 if (fNeedsWrite)
9821 // now spit it all out!
9822 pNewConfig->write(mData->m_strConfigFileFull);
9823
9824 mData->pMachineConfigFile = pNewConfig;
9825 delete pOldConfig;
9826 i_commit();
9827
9828 // after saving settings, we are no longer different from the XML on disk
9829 mData->flModifications = 0;
9830 }
9831 catch (HRESULT err)
9832 {
9833 // we assume that error info is set by the thrower
9834 rc = err;
9835
9836 // restore old config
9837 delete pNewConfig;
9838 mData->pMachineConfigFile = pOldConfig;
9839 }
9840 catch (...)
9841 {
9842 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9843 }
9844
9845 if (fNeedsWrite)
9846 {
9847 /* Fire the data change event, even on failure (since we've already
9848 * committed all data). This is done only for SessionMachines because
9849 * mutable Machine instances are always not registered (i.e. private
9850 * to the client process that creates them) and thus don't need to
9851 * inform callbacks. */
9852 if (i_isSessionMachine())
9853 mParent->i_onMachineDataChange(mData->mUuid);
9854 }
9855
9856 LogFlowThisFunc(("rc=%08X\n", rc));
9857 LogFlowThisFuncLeave();
9858 return rc;
9859}
9860
9861/**
9862 * Implementation for saving the machine settings into the given
9863 * settings::MachineConfigFile instance. This copies machine extradata
9864 * from the previous machine config file in the instance data, if any.
9865 *
9866 * This gets called from two locations:
9867 *
9868 * -- Machine::i_saveSettings(), during the regular XML writing;
9869 *
9870 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9871 * exported to OVF and we write the VirtualBox proprietary XML
9872 * into a <vbox:Machine> tag.
9873 *
9874 * This routine fills all the fields in there, including snapshots, *except*
9875 * for the following:
9876 *
9877 * -- fCurrentStateModified. There is some special logic associated with that.
9878 *
9879 * The caller can then call MachineConfigFile::write() or do something else
9880 * with it.
9881 *
9882 * Caller must hold the machine lock!
9883 *
9884 * This throws XML errors and HRESULT, so the caller must have a catch block!
9885 */
9886void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9887{
9888 // deep copy extradata, being extra careful with self assignment (the STL
9889 // map assignment on Mac OS X clang based Xcode isn't checking)
9890 if (&config != mData->pMachineConfigFile)
9891 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9892
9893 config.uuid = mData->mUuid;
9894
9895 // copy name, description, OS type, teleport, UTC etc.
9896 config.machineUserData = mUserData->s;
9897
9898 if ( mData->mMachineState == MachineState_Saved
9899 || mData->mMachineState == MachineState_Restoring
9900 // when doing certain snapshot operations we may or may not have
9901 // a saved state in the current state, so keep everything as is
9902 || ( ( mData->mMachineState == MachineState_Snapshotting
9903 || mData->mMachineState == MachineState_DeletingSnapshot
9904 || mData->mMachineState == MachineState_RestoringSnapshot)
9905 && (!mSSData->strStateFilePath.isEmpty())
9906 )
9907 )
9908 {
9909 Assert(!mSSData->strStateFilePath.isEmpty());
9910 /* try to make the file name relative to the settings file dir */
9911 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9912 }
9913 else
9914 {
9915 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9916 config.strStateFile.setNull();
9917 }
9918
9919 if (mData->mCurrentSnapshot)
9920 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9921 else
9922 config.uuidCurrentSnapshot.clear();
9923
9924 config.timeLastStateChange = mData->mLastStateChange;
9925 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9926 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9927
9928 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9929 if (FAILED(rc)) throw rc;
9930
9931 // save machine's media registry if this is VirtualBox 4.0 or later
9932 if (config.canHaveOwnMediaRegistry())
9933 {
9934 // determine machine folder
9935 Utf8Str strMachineFolder = i_getSettingsFileFull();
9936 strMachineFolder.stripFilename();
9937 mParent->i_saveMediaRegistry(config.mediaRegistry,
9938 i_getId(), // only media with registry ID == machine UUID
9939 strMachineFolder);
9940 // this throws HRESULT
9941 }
9942
9943 // save snapshots
9944 rc = i_saveAllSnapshots(config);
9945 if (FAILED(rc)) throw rc;
9946}
9947
9948/**
9949 * Saves all snapshots of the machine into the given machine config file. Called
9950 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9951 * @param config
9952 * @return
9953 */
9954HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9955{
9956 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9957
9958 HRESULT rc = S_OK;
9959
9960 try
9961 {
9962 config.llFirstSnapshot.clear();
9963
9964 if (mData->mFirstSnapshot)
9965 {
9966 // the settings use a list for "the first snapshot"
9967 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9968
9969 // get reference to the snapshot on the list and work on that
9970 // element straight in the list to avoid excessive copying later
9971 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9972 if (FAILED(rc)) throw rc;
9973 }
9974
9975// if (mType == IsSessionMachine)
9976// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9977
9978 }
9979 catch (HRESULT err)
9980 {
9981 /* we assume that error info is set by the thrower */
9982 rc = err;
9983 }
9984 catch (...)
9985 {
9986 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9987 }
9988
9989 return rc;
9990}
9991
9992/**
9993 * Saves the VM hardware configuration. It is assumed that the
9994 * given node is empty.
9995 *
9996 * @param data Reference to the settings object for the hardware config.
9997 * @param pDbg Pointer to the settings object for the debugging config
9998 * which happens to live in mHWData.
9999 * @param pAutostart Pointer to the settings object for the autostart config
10000 * which happens to live in mHWData.
10001 */
10002HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10003 settings::Autostart *pAutostart)
10004{
10005 HRESULT rc = S_OK;
10006
10007 try
10008 {
10009 /* The hardware version attribute (optional).
10010 Automatically upgrade from 1 to current default hardware version
10011 when there is no saved state. (ugly!) */
10012 if ( mHWData->mHWVersion == "1"
10013 && mSSData->strStateFilePath.isEmpty()
10014 )
10015 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10016
10017 data.strVersion = mHWData->mHWVersion;
10018 data.uuid = mHWData->mHardwareUUID;
10019
10020 // CPU
10021 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10022 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10023 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10024 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10025 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10026 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10027 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10028 data.fPAE = !!mHWData->mPAEEnabled;
10029 data.enmLongMode = mHWData->mLongMode;
10030 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10031 data.fAPIC = !!mHWData->mAPIC;
10032 data.fX2APIC = !!mHWData->mX2APIC;
10033 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10034 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10035 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10036 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10037 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10038 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10039 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10040 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10041 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10042 data.cCPUs = mHWData->mCPUCount;
10043 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10044 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10045 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10046 data.strCpuProfile = mHWData->mCpuProfile;
10047
10048 data.llCpus.clear();
10049 if (data.fCpuHotPlug)
10050 {
10051 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10052 {
10053 if (mHWData->mCPUAttached[idx])
10054 {
10055 settings::Cpu cpu;
10056 cpu.ulId = idx;
10057 data.llCpus.push_back(cpu);
10058 }
10059 }
10060 }
10061
10062 /* Standard and Extended CPUID leafs. */
10063 data.llCpuIdLeafs.clear();
10064 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10065
10066 // memory
10067 data.ulMemorySizeMB = mHWData->mMemorySize;
10068 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10069
10070 // firmware
10071 data.firmwareType = mHWData->mFirmwareType;
10072
10073 // HID
10074 data.pointingHIDType = mHWData->mPointingHIDType;
10075 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10076
10077 // chipset
10078 data.chipsetType = mHWData->mChipsetType;
10079
10080 // paravirt
10081 data.paravirtProvider = mHWData->mParavirtProvider;
10082 data.strParavirtDebug = mHWData->mParavirtDebug;
10083
10084 // emulated USB card reader
10085 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10086
10087 // HPET
10088 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10089
10090 // boot order
10091 data.mapBootOrder.clear();
10092 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10093 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10094
10095 /* VRDEServer settings (optional) */
10096 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10097 if (FAILED(rc)) throw rc;
10098
10099 /* BIOS settings (required) */
10100 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10101 if (FAILED(rc)) throw rc;
10102
10103 /* Recording settings (required) */
10104 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10105 if (FAILED(rc)) throw rc;
10106
10107 /* GraphicsAdapter settings (required) */
10108 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10109 if (FAILED(rc)) throw rc;
10110
10111 /* USB Controller (required) */
10112 data.usbSettings.llUSBControllers.clear();
10113 for (USBControllerList::const_iterator
10114 it = mUSBControllers->begin();
10115 it != mUSBControllers->end();
10116 ++it)
10117 {
10118 ComObjPtr<USBController> ctrl = *it;
10119 settings::USBController settingsCtrl;
10120
10121 settingsCtrl.strName = ctrl->i_getName();
10122 settingsCtrl.enmType = ctrl->i_getControllerType();
10123
10124 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10125 }
10126
10127 /* USB device filters (required) */
10128 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10129 if (FAILED(rc)) throw rc;
10130
10131 /* Network adapters (required) */
10132 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10133 data.llNetworkAdapters.clear();
10134 /* Write out only the nominal number of network adapters for this
10135 * chipset type. Since Machine::commit() hasn't been called there
10136 * may be extra NIC settings in the vector. */
10137 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10138 {
10139 settings::NetworkAdapter nic;
10140 nic.ulSlot = (uint32_t)slot;
10141 /* paranoia check... must not be NULL, but must not crash either. */
10142 if (mNetworkAdapters[slot])
10143 {
10144 if (mNetworkAdapters[slot]->i_hasDefaults())
10145 continue;
10146
10147 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10148 if (FAILED(rc)) throw rc;
10149
10150 data.llNetworkAdapters.push_back(nic);
10151 }
10152 }
10153
10154 /* Serial ports */
10155 data.llSerialPorts.clear();
10156 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10157 {
10158 if (mSerialPorts[slot]->i_hasDefaults())
10159 continue;
10160
10161 settings::SerialPort s;
10162 s.ulSlot = slot;
10163 rc = mSerialPorts[slot]->i_saveSettings(s);
10164 if (FAILED(rc)) return rc;
10165
10166 data.llSerialPorts.push_back(s);
10167 }
10168
10169 /* Parallel ports */
10170 data.llParallelPorts.clear();
10171 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10172 {
10173 if (mParallelPorts[slot]->i_hasDefaults())
10174 continue;
10175
10176 settings::ParallelPort p;
10177 p.ulSlot = slot;
10178 rc = mParallelPorts[slot]->i_saveSettings(p);
10179 if (FAILED(rc)) return rc;
10180
10181 data.llParallelPorts.push_back(p);
10182 }
10183
10184 /* Audio adapter */
10185 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10186 if (FAILED(rc)) return rc;
10187
10188 rc = i_saveStorageControllers(data.storage);
10189 if (FAILED(rc)) return rc;
10190
10191 /* Shared folders */
10192 data.llSharedFolders.clear();
10193 for (HWData::SharedFolderList::const_iterator
10194 it = mHWData->mSharedFolders.begin();
10195 it != mHWData->mSharedFolders.end();
10196 ++it)
10197 {
10198 SharedFolder *pSF = *it;
10199 AutoCaller sfCaller(pSF);
10200 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10201 settings::SharedFolder sf;
10202 sf.strName = pSF->i_getName();
10203 sf.strHostPath = pSF->i_getHostPath();
10204 sf.fWritable = !!pSF->i_isWritable();
10205 sf.fAutoMount = !!pSF->i_isAutoMounted();
10206 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10207
10208 data.llSharedFolders.push_back(sf);
10209 }
10210
10211 // clipboard
10212 data.clipboardMode = mHWData->mClipboardMode;
10213 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10214
10215 // drag'n'drop
10216 data.dndMode = mHWData->mDnDMode;
10217
10218 /* Guest */
10219 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10220
10221 // IO settings
10222 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10223 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10224
10225 /* BandwidthControl (required) */
10226 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10227 if (FAILED(rc)) throw rc;
10228
10229 /* Host PCI devices */
10230 data.pciAttachments.clear();
10231 for (HWData::PCIDeviceAssignmentList::const_iterator
10232 it = mHWData->mPCIDeviceAssignments.begin();
10233 it != mHWData->mPCIDeviceAssignments.end();
10234 ++it)
10235 {
10236 ComObjPtr<PCIDeviceAttachment> pda = *it;
10237 settings::HostPCIDeviceAttachment hpda;
10238
10239 rc = pda->i_saveSettings(hpda);
10240 if (FAILED(rc)) throw rc;
10241
10242 data.pciAttachments.push_back(hpda);
10243 }
10244
10245 // guest properties
10246 data.llGuestProperties.clear();
10247#ifdef VBOX_WITH_GUEST_PROPS
10248 for (HWData::GuestPropertyMap::const_iterator
10249 it = mHWData->mGuestProperties.begin();
10250 it != mHWData->mGuestProperties.end();
10251 ++it)
10252 {
10253 HWData::GuestProperty property = it->second;
10254
10255 /* Remove transient guest properties at shutdown unless we
10256 * are saving state. Note that restoring snapshot intentionally
10257 * keeps them, they will be removed if appropriate once the final
10258 * machine state is set (as crashes etc. need to work). */
10259 if ( ( mData->mMachineState == MachineState_PoweredOff
10260 || mData->mMachineState == MachineState_Aborted
10261 || mData->mMachineState == MachineState_Teleported)
10262 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10263 continue;
10264 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10265 prop.strName = it->first;
10266 prop.strValue = property.strValue;
10267 prop.timestamp = (uint64_t)property.mTimestamp;
10268 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10269 GuestPropWriteFlags(property.mFlags, szFlags);
10270 prop.strFlags = szFlags;
10271
10272 data.llGuestProperties.push_back(prop);
10273 }
10274
10275 /* I presume this doesn't require a backup(). */
10276 mData->mGuestPropertiesModified = FALSE;
10277#endif /* VBOX_WITH_GUEST_PROPS defined */
10278
10279 *pDbg = mHWData->mDebugging;
10280 *pAutostart = mHWData->mAutostart;
10281
10282 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10283 }
10284 catch (std::bad_alloc &)
10285 {
10286 return E_OUTOFMEMORY;
10287 }
10288
10289 AssertComRC(rc);
10290 return rc;
10291}
10292
10293/**
10294 * Saves the storage controller configuration.
10295 *
10296 * @param data storage settings.
10297 */
10298HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10299{
10300 data.llStorageControllers.clear();
10301
10302 for (StorageControllerList::const_iterator
10303 it = mStorageControllers->begin();
10304 it != mStorageControllers->end();
10305 ++it)
10306 {
10307 HRESULT rc;
10308 ComObjPtr<StorageController> pCtl = *it;
10309
10310 settings::StorageController ctl;
10311 ctl.strName = pCtl->i_getName();
10312 ctl.controllerType = pCtl->i_getControllerType();
10313 ctl.storageBus = pCtl->i_getStorageBus();
10314 ctl.ulInstance = pCtl->i_getInstance();
10315 ctl.fBootable = pCtl->i_getBootable();
10316
10317 /* Save the port count. */
10318 ULONG portCount;
10319 rc = pCtl->COMGETTER(PortCount)(&portCount);
10320 ComAssertComRCRet(rc, rc);
10321 ctl.ulPortCount = portCount;
10322
10323 /* Save fUseHostIOCache */
10324 BOOL fUseHostIOCache;
10325 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10326 ComAssertComRCRet(rc, rc);
10327 ctl.fUseHostIOCache = !!fUseHostIOCache;
10328
10329 /* save the devices now. */
10330 rc = i_saveStorageDevices(pCtl, ctl);
10331 ComAssertComRCRet(rc, rc);
10332
10333 data.llStorageControllers.push_back(ctl);
10334 }
10335
10336 return S_OK;
10337}
10338
10339/**
10340 * Saves the hard disk configuration.
10341 */
10342HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10343 settings::StorageController &data)
10344{
10345 MediumAttachmentList atts;
10346
10347 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10348 if (FAILED(rc)) return rc;
10349
10350 data.llAttachedDevices.clear();
10351 for (MediumAttachmentList::const_iterator
10352 it = atts.begin();
10353 it != atts.end();
10354 ++it)
10355 {
10356 settings::AttachedDevice dev;
10357 IMediumAttachment *iA = *it;
10358 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10359 Medium *pMedium = pAttach->i_getMedium();
10360
10361 dev.deviceType = pAttach->i_getType();
10362 dev.lPort = pAttach->i_getPort();
10363 dev.lDevice = pAttach->i_getDevice();
10364 dev.fPassThrough = pAttach->i_getPassthrough();
10365 dev.fHotPluggable = pAttach->i_getHotPluggable();
10366 if (pMedium)
10367 {
10368 if (pMedium->i_isHostDrive())
10369 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10370 else
10371 dev.uuid = pMedium->i_getId();
10372 dev.fTempEject = pAttach->i_getTempEject();
10373 dev.fNonRotational = pAttach->i_getNonRotational();
10374 dev.fDiscard = pAttach->i_getDiscard();
10375 }
10376
10377 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10378
10379 data.llAttachedDevices.push_back(dev);
10380 }
10381
10382 return S_OK;
10383}
10384
10385/**
10386 * Saves machine state settings as defined by aFlags
10387 * (SaveSTS_* values).
10388 *
10389 * @param aFlags Combination of SaveSTS_* flags.
10390 *
10391 * @note Locks objects for writing.
10392 */
10393HRESULT Machine::i_saveStateSettings(int aFlags)
10394{
10395 if (aFlags == 0)
10396 return S_OK;
10397
10398 AutoCaller autoCaller(this);
10399 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10400
10401 /* This object's write lock is also necessary to serialize file access
10402 * (prevent concurrent reads and writes) */
10403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10404
10405 HRESULT rc = S_OK;
10406
10407 Assert(mData->pMachineConfigFile);
10408
10409 try
10410 {
10411 if (aFlags & SaveSTS_CurStateModified)
10412 mData->pMachineConfigFile->fCurrentStateModified = true;
10413
10414 if (aFlags & SaveSTS_StateFilePath)
10415 {
10416 if (!mSSData->strStateFilePath.isEmpty())
10417 /* try to make the file name relative to the settings file dir */
10418 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10419 else
10420 mData->pMachineConfigFile->strStateFile.setNull();
10421 }
10422
10423 if (aFlags & SaveSTS_StateTimeStamp)
10424 {
10425 Assert( mData->mMachineState != MachineState_Aborted
10426 || mSSData->strStateFilePath.isEmpty());
10427
10428 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10429
10430 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10431/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10432 }
10433
10434 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10435 }
10436 catch (...)
10437 {
10438 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10439 }
10440
10441 return rc;
10442}
10443
10444/**
10445 * Ensures that the given medium is added to a media registry. If this machine
10446 * was created with 4.0 or later, then the machine registry is used. Otherwise
10447 * the global VirtualBox media registry is used.
10448 *
10449 * Caller must NOT hold machine lock, media tree or any medium locks!
10450 *
10451 * @param pMedium
10452 */
10453void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10454{
10455 /* Paranoia checks: do not hold machine or media tree locks. */
10456 AssertReturnVoid(!isWriteLockOnCurrentThread());
10457 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10458
10459 ComObjPtr<Medium> pBase;
10460 {
10461 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10462 pBase = pMedium->i_getBase();
10463 }
10464
10465 /* Paranoia checks: do not hold medium locks. */
10466 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10467 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10468
10469 // decide which medium registry to use now that the medium is attached:
10470 Guid uuid;
10471 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10472 if (fCanHaveOwnMediaRegistry)
10473 // machine XML is VirtualBox 4.0 or higher:
10474 uuid = i_getId(); // machine UUID
10475 else
10476 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10477
10478 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10479 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10480 if (pMedium->i_addRegistry(uuid))
10481 mParent->i_markRegistryModified(uuid);
10482
10483 /* For more complex hard disk structures it can happen that the base
10484 * medium isn't yet associated with any medium registry. Do that now. */
10485 if (pMedium != pBase)
10486 {
10487 /* Tree lock needed by Medium::addRegistry when recursing. */
10488 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10489 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10490 {
10491 treeLock.release();
10492 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10493 treeLock.acquire();
10494 }
10495 if (pBase->i_addRegistryRecursive(uuid))
10496 {
10497 treeLock.release();
10498 mParent->i_markRegistryModified(uuid);
10499 }
10500 }
10501}
10502
10503/**
10504 * Creates differencing hard disks for all normal hard disks attached to this
10505 * machine and a new set of attachments to refer to created disks.
10506 *
10507 * Used when taking a snapshot or when deleting the current state. Gets called
10508 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10509 *
10510 * This method assumes that mMediumAttachments contains the original hard disk
10511 * attachments it needs to create diffs for. On success, these attachments will
10512 * be replaced with the created diffs.
10513 *
10514 * Attachments with non-normal hard disks are left as is.
10515 *
10516 * If @a aOnline is @c false then the original hard disks that require implicit
10517 * diffs will be locked for reading. Otherwise it is assumed that they are
10518 * already locked for writing (when the VM was started). Note that in the latter
10519 * case it is responsibility of the caller to lock the newly created diffs for
10520 * writing if this method succeeds.
10521 *
10522 * @param aProgress Progress object to run (must contain at least as
10523 * many operations left as the number of hard disks
10524 * attached).
10525 * @param aWeight Weight of this operation.
10526 * @param aOnline Whether the VM was online prior to this operation.
10527 *
10528 * @note The progress object is not marked as completed, neither on success nor
10529 * on failure. This is a responsibility of the caller.
10530 *
10531 * @note Locks this object and the media tree for writing.
10532 */
10533HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10534 ULONG aWeight,
10535 bool aOnline)
10536{
10537 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10538
10539 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10540 AssertReturn(!!pProgressControl, E_INVALIDARG);
10541
10542 AutoCaller autoCaller(this);
10543 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10544
10545 AutoMultiWriteLock2 alock(this->lockHandle(),
10546 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10547
10548 /* must be in a protective state because we release the lock below */
10549 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10550 || mData->mMachineState == MachineState_OnlineSnapshotting
10551 || mData->mMachineState == MachineState_LiveSnapshotting
10552 || mData->mMachineState == MachineState_RestoringSnapshot
10553 || mData->mMachineState == MachineState_DeletingSnapshot
10554 , E_FAIL);
10555
10556 HRESULT rc = S_OK;
10557
10558 // use appropriate locked media map (online or offline)
10559 MediumLockListMap lockedMediaOffline;
10560 MediumLockListMap *lockedMediaMap;
10561 if (aOnline)
10562 lockedMediaMap = &mData->mSession.mLockedMedia;
10563 else
10564 lockedMediaMap = &lockedMediaOffline;
10565
10566 try
10567 {
10568 if (!aOnline)
10569 {
10570 /* lock all attached hard disks early to detect "in use"
10571 * situations before creating actual diffs */
10572 for (MediumAttachmentList::const_iterator
10573 it = mMediumAttachments->begin();
10574 it != mMediumAttachments->end();
10575 ++it)
10576 {
10577 MediumAttachment *pAtt = *it;
10578 if (pAtt->i_getType() == DeviceType_HardDisk)
10579 {
10580 Medium *pMedium = pAtt->i_getMedium();
10581 Assert(pMedium);
10582
10583 MediumLockList *pMediumLockList(new MediumLockList());
10584 alock.release();
10585 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10586 NULL /* pToLockWrite */,
10587 false /* fMediumLockWriteAll */,
10588 NULL,
10589 *pMediumLockList);
10590 alock.acquire();
10591 if (FAILED(rc))
10592 {
10593 delete pMediumLockList;
10594 throw rc;
10595 }
10596 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10597 if (FAILED(rc))
10598 {
10599 throw setError(rc,
10600 tr("Collecting locking information for all attached media failed"));
10601 }
10602 }
10603 }
10604
10605 /* Now lock all media. If this fails, nothing is locked. */
10606 alock.release();
10607 rc = lockedMediaMap->Lock();
10608 alock.acquire();
10609 if (FAILED(rc))
10610 {
10611 throw setError(rc,
10612 tr("Locking of attached media failed"));
10613 }
10614 }
10615
10616 /* remember the current list (note that we don't use backup() since
10617 * mMediumAttachments may be already backed up) */
10618 MediumAttachmentList atts = *mMediumAttachments.data();
10619
10620 /* start from scratch */
10621 mMediumAttachments->clear();
10622
10623 /* go through remembered attachments and create diffs for normal hard
10624 * disks and attach them */
10625 for (MediumAttachmentList::const_iterator
10626 it = atts.begin();
10627 it != atts.end();
10628 ++it)
10629 {
10630 MediumAttachment *pAtt = *it;
10631
10632 DeviceType_T devType = pAtt->i_getType();
10633 Medium *pMedium = pAtt->i_getMedium();
10634
10635 if ( devType != DeviceType_HardDisk
10636 || pMedium == NULL
10637 || pMedium->i_getType() != MediumType_Normal)
10638 {
10639 /* copy the attachment as is */
10640
10641 /** @todo the progress object created in SessionMachine::TakeSnaphot
10642 * only expects operations for hard disks. Later other
10643 * device types need to show up in the progress as well. */
10644 if (devType == DeviceType_HardDisk)
10645 {
10646 if (pMedium == NULL)
10647 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10648 aWeight); // weight
10649 else
10650 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10651 pMedium->i_getBase()->i_getName().c_str()).raw(),
10652 aWeight); // weight
10653 }
10654
10655 mMediumAttachments->push_back(pAtt);
10656 continue;
10657 }
10658
10659 /* need a diff */
10660 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10661 pMedium->i_getBase()->i_getName().c_str()).raw(),
10662 aWeight); // weight
10663
10664 Utf8Str strFullSnapshotFolder;
10665 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10666
10667 ComObjPtr<Medium> diff;
10668 diff.createObject();
10669 // store the diff in the same registry as the parent
10670 // (this cannot fail here because we can't create implicit diffs for
10671 // unregistered images)
10672 Guid uuidRegistryParent;
10673 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10674 Assert(fInRegistry); NOREF(fInRegistry);
10675 rc = diff->init(mParent,
10676 pMedium->i_getPreferredDiffFormat(),
10677 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10678 uuidRegistryParent,
10679 DeviceType_HardDisk);
10680 if (FAILED(rc)) throw rc;
10681
10682 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10683 * the push_back? Looks like we're going to release medium with the
10684 * wrong kind of lock (general issue with if we fail anywhere at all)
10685 * and an orphaned VDI in the snapshots folder. */
10686
10687 /* update the appropriate lock list */
10688 MediumLockList *pMediumLockList;
10689 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10690 AssertComRCThrowRC(rc);
10691 if (aOnline)
10692 {
10693 alock.release();
10694 /* The currently attached medium will be read-only, change
10695 * the lock type to read. */
10696 rc = pMediumLockList->Update(pMedium, false);
10697 alock.acquire();
10698 AssertComRCThrowRC(rc);
10699 }
10700
10701 /* release the locks before the potentially lengthy operation */
10702 alock.release();
10703 rc = pMedium->i_createDiffStorage(diff,
10704 pMedium->i_getPreferredDiffVariant(),
10705 pMediumLockList,
10706 NULL /* aProgress */,
10707 true /* aWait */,
10708 false /* aNotify */);
10709 alock.acquire();
10710 if (FAILED(rc)) throw rc;
10711
10712 /* actual lock list update is done in Machine::i_commitMedia */
10713
10714 rc = diff->i_addBackReference(mData->mUuid);
10715 AssertComRCThrowRC(rc);
10716
10717 /* add a new attachment */
10718 ComObjPtr<MediumAttachment> attachment;
10719 attachment.createObject();
10720 rc = attachment->init(this,
10721 diff,
10722 pAtt->i_getControllerName(),
10723 pAtt->i_getPort(),
10724 pAtt->i_getDevice(),
10725 DeviceType_HardDisk,
10726 true /* aImplicit */,
10727 false /* aPassthrough */,
10728 false /* aTempEject */,
10729 pAtt->i_getNonRotational(),
10730 pAtt->i_getDiscard(),
10731 pAtt->i_getHotPluggable(),
10732 pAtt->i_getBandwidthGroup());
10733 if (FAILED(rc)) throw rc;
10734
10735 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10736 AssertComRCThrowRC(rc);
10737 mMediumAttachments->push_back(attachment);
10738 }
10739 }
10740 catch (HRESULT aRC) { rc = aRC; }
10741
10742 /* unlock all hard disks we locked when there is no VM */
10743 if (!aOnline)
10744 {
10745 ErrorInfoKeeper eik;
10746
10747 HRESULT rc1 = lockedMediaMap->Clear();
10748 AssertComRC(rc1);
10749 }
10750
10751 return rc;
10752}
10753
10754/**
10755 * Deletes implicit differencing hard disks created either by
10756 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10757 * mMediumAttachments.
10758 *
10759 * Note that to delete hard disks created by #attachDevice() this method is
10760 * called from #i_rollbackMedia() when the changes are rolled back.
10761 *
10762 * @note Locks this object and the media tree for writing.
10763 */
10764HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10765{
10766 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10767
10768 AutoCaller autoCaller(this);
10769 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10770
10771 AutoMultiWriteLock2 alock(this->lockHandle(),
10772 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10773
10774 /* We absolutely must have backed up state. */
10775 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10776
10777 /* Check if there are any implicitly created diff images. */
10778 bool fImplicitDiffs = false;
10779 for (MediumAttachmentList::const_iterator
10780 it = mMediumAttachments->begin();
10781 it != mMediumAttachments->end();
10782 ++it)
10783 {
10784 const ComObjPtr<MediumAttachment> &pAtt = *it;
10785 if (pAtt->i_isImplicit())
10786 {
10787 fImplicitDiffs = true;
10788 break;
10789 }
10790 }
10791 /* If there is nothing to do, leave early. This saves lots of image locking
10792 * effort. It also avoids a MachineStateChanged event without real reason.
10793 * This is important e.g. when loading a VM config, because there should be
10794 * no events. Otherwise API clients can become thoroughly confused for
10795 * inaccessible VMs (the code for loading VM configs uses this method for
10796 * cleanup if the config makes no sense), as they take such events as an
10797 * indication that the VM is alive, and they would force the VM config to
10798 * be reread, leading to an endless loop. */
10799 if (!fImplicitDiffs)
10800 return S_OK;
10801
10802 HRESULT rc = S_OK;
10803 MachineState_T oldState = mData->mMachineState;
10804
10805 /* will release the lock before the potentially lengthy operation,
10806 * so protect with the special state (unless already protected) */
10807 if ( oldState != MachineState_Snapshotting
10808 && oldState != MachineState_OnlineSnapshotting
10809 && oldState != MachineState_LiveSnapshotting
10810 && oldState != MachineState_RestoringSnapshot
10811 && oldState != MachineState_DeletingSnapshot
10812 && oldState != MachineState_DeletingSnapshotOnline
10813 && oldState != MachineState_DeletingSnapshotPaused
10814 )
10815 i_setMachineState(MachineState_SettingUp);
10816
10817 // use appropriate locked media map (online or offline)
10818 MediumLockListMap lockedMediaOffline;
10819 MediumLockListMap *lockedMediaMap;
10820 if (aOnline)
10821 lockedMediaMap = &mData->mSession.mLockedMedia;
10822 else
10823 lockedMediaMap = &lockedMediaOffline;
10824
10825 try
10826 {
10827 if (!aOnline)
10828 {
10829 /* lock all attached hard disks early to detect "in use"
10830 * situations before deleting actual diffs */
10831 for (MediumAttachmentList::const_iterator
10832 it = mMediumAttachments->begin();
10833 it != mMediumAttachments->end();
10834 ++it)
10835 {
10836 MediumAttachment *pAtt = *it;
10837 if (pAtt->i_getType() == DeviceType_HardDisk)
10838 {
10839 Medium *pMedium = pAtt->i_getMedium();
10840 Assert(pMedium);
10841
10842 MediumLockList *pMediumLockList(new MediumLockList());
10843 alock.release();
10844 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10845 NULL /* pToLockWrite */,
10846 false /* fMediumLockWriteAll */,
10847 NULL,
10848 *pMediumLockList);
10849 alock.acquire();
10850
10851 if (FAILED(rc))
10852 {
10853 delete pMediumLockList;
10854 throw rc;
10855 }
10856
10857 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10858 if (FAILED(rc))
10859 throw rc;
10860 }
10861 }
10862
10863 if (FAILED(rc))
10864 throw rc;
10865 } // end of offline
10866
10867 /* Lock lists are now up to date and include implicitly created media */
10868
10869 /* Go through remembered attachments and delete all implicitly created
10870 * diffs and fix up the attachment information */
10871 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10872 MediumAttachmentList implicitAtts;
10873 for (MediumAttachmentList::const_iterator
10874 it = mMediumAttachments->begin();
10875 it != mMediumAttachments->end();
10876 ++it)
10877 {
10878 ComObjPtr<MediumAttachment> pAtt = *it;
10879 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10880 if (pMedium.isNull())
10881 continue;
10882
10883 // Implicit attachments go on the list for deletion and back references are removed.
10884 if (pAtt->i_isImplicit())
10885 {
10886 /* Deassociate and mark for deletion */
10887 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10888 rc = pMedium->i_removeBackReference(mData->mUuid);
10889 if (FAILED(rc))
10890 throw rc;
10891 implicitAtts.push_back(pAtt);
10892 continue;
10893 }
10894
10895 /* Was this medium attached before? */
10896 if (!i_findAttachment(oldAtts, pMedium))
10897 {
10898 /* no: de-associate */
10899 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10900 rc = pMedium->i_removeBackReference(mData->mUuid);
10901 if (FAILED(rc))
10902 throw rc;
10903 continue;
10904 }
10905 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10906 }
10907
10908 /* If there are implicit attachments to delete, throw away the lock
10909 * map contents (which will unlock all media) since the medium
10910 * attachments will be rolled back. Below we need to completely
10911 * recreate the lock map anyway since it is infinitely complex to
10912 * do this incrementally (would need reconstructing each attachment
10913 * change, which would be extremely hairy). */
10914 if (implicitAtts.size() != 0)
10915 {
10916 ErrorInfoKeeper eik;
10917
10918 HRESULT rc1 = lockedMediaMap->Clear();
10919 AssertComRC(rc1);
10920 }
10921
10922 /* rollback hard disk changes */
10923 mMediumAttachments.rollback();
10924
10925 MultiResult mrc(S_OK);
10926
10927 // Delete unused implicit diffs.
10928 if (implicitAtts.size() != 0)
10929 {
10930 alock.release();
10931
10932 for (MediumAttachmentList::const_iterator
10933 it = implicitAtts.begin();
10934 it != implicitAtts.end();
10935 ++it)
10936 {
10937 // Remove medium associated with this attachment.
10938 ComObjPtr<MediumAttachment> pAtt = *it;
10939 Assert(pAtt);
10940 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10941 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10942 Assert(pMedium);
10943
10944 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10945 // continue on delete failure, just collect error messages
10946 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10947 pMedium->i_getLocationFull().c_str() ));
10948 mrc = rc;
10949 }
10950 // Clear the list of deleted implicit attachments now, while not
10951 // holding the lock, as it will ultimately trigger Medium::uninit()
10952 // calls which assume that the media tree lock isn't held.
10953 implicitAtts.clear();
10954
10955 alock.acquire();
10956
10957 /* if there is a VM recreate media lock map as mentioned above,
10958 * otherwise it is a waste of time and we leave things unlocked */
10959 if (aOnline)
10960 {
10961 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10962 /* must never be NULL, but better safe than sorry */
10963 if (!pMachine.isNull())
10964 {
10965 alock.release();
10966 rc = mData->mSession.mMachine->i_lockMedia();
10967 alock.acquire();
10968 if (FAILED(rc))
10969 throw rc;
10970 }
10971 }
10972 }
10973 }
10974 catch (HRESULT aRC) {rc = aRC;}
10975
10976 if (mData->mMachineState == MachineState_SettingUp)
10977 i_setMachineState(oldState);
10978
10979 /* unlock all hard disks we locked when there is no VM */
10980 if (!aOnline)
10981 {
10982 ErrorInfoKeeper eik;
10983
10984 HRESULT rc1 = lockedMediaMap->Clear();
10985 AssertComRC(rc1);
10986 }
10987
10988 return rc;
10989}
10990
10991
10992/**
10993 * Looks through the given list of media attachments for one with the given parameters
10994 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10995 * can be searched as well if needed.
10996 *
10997 * @param ll
10998 * @param aControllerName
10999 * @param aControllerPort
11000 * @param aDevice
11001 * @return
11002 */
11003MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11004 const Utf8Str &aControllerName,
11005 LONG aControllerPort,
11006 LONG aDevice)
11007{
11008 for (MediumAttachmentList::const_iterator
11009 it = ll.begin();
11010 it != ll.end();
11011 ++it)
11012 {
11013 MediumAttachment *pAttach = *it;
11014 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11015 return pAttach;
11016 }
11017
11018 return NULL;
11019}
11020
11021/**
11022 * Looks through the given list of media attachments for one with the given parameters
11023 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11024 * can be searched as well if needed.
11025 *
11026 * @param ll
11027 * @param pMedium
11028 * @return
11029 */
11030MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11031 ComObjPtr<Medium> pMedium)
11032{
11033 for (MediumAttachmentList::const_iterator
11034 it = ll.begin();
11035 it != ll.end();
11036 ++it)
11037 {
11038 MediumAttachment *pAttach = *it;
11039 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11040 if (pMediumThis == pMedium)
11041 return pAttach;
11042 }
11043
11044 return NULL;
11045}
11046
11047/**
11048 * Looks through the given list of media attachments for one with the given parameters
11049 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11050 * can be searched as well if needed.
11051 *
11052 * @param ll
11053 * @param id
11054 * @return
11055 */
11056MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11057 Guid &id)
11058{
11059 for (MediumAttachmentList::const_iterator
11060 it = ll.begin();
11061 it != ll.end();
11062 ++it)
11063 {
11064 MediumAttachment *pAttach = *it;
11065 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11066 if (pMediumThis->i_getId() == id)
11067 return pAttach;
11068 }
11069
11070 return NULL;
11071}
11072
11073/**
11074 * Main implementation for Machine::DetachDevice. This also gets called
11075 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11076 *
11077 * @param pAttach Medium attachment to detach.
11078 * @param writeLock Machine write lock which the caller must have locked once.
11079 * This may be released temporarily in here.
11080 * @param pSnapshot If NULL, then the detachment is for the current machine.
11081 * Otherwise this is for a SnapshotMachine, and this must be
11082 * its snapshot.
11083 * @return
11084 */
11085HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11086 AutoWriteLock &writeLock,
11087 Snapshot *pSnapshot)
11088{
11089 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11090 DeviceType_T mediumType = pAttach->i_getType();
11091
11092 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11093
11094 if (pAttach->i_isImplicit())
11095 {
11096 /* attempt to implicitly delete the implicitly created diff */
11097
11098 /// @todo move the implicit flag from MediumAttachment to Medium
11099 /// and forbid any hard disk operation when it is implicit. Or maybe
11100 /// a special media state for it to make it even more simple.
11101
11102 Assert(mMediumAttachments.isBackedUp());
11103
11104 /* will release the lock before the potentially lengthy operation, so
11105 * protect with the special state */
11106 MachineState_T oldState = mData->mMachineState;
11107 i_setMachineState(MachineState_SettingUp);
11108
11109 writeLock.release();
11110
11111 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11112 true /*aWait*/,
11113 false /*aNotify*/);
11114
11115 writeLock.acquire();
11116
11117 i_setMachineState(oldState);
11118
11119 if (FAILED(rc)) return rc;
11120 }
11121
11122 i_setModified(IsModified_Storage);
11123 mMediumAttachments.backup();
11124 mMediumAttachments->remove(pAttach);
11125
11126 if (!oldmedium.isNull())
11127 {
11128 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11129 if (pSnapshot)
11130 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11131 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11132 else if (mediumType != DeviceType_HardDisk)
11133 oldmedium->i_removeBackReference(mData->mUuid);
11134 }
11135
11136 return S_OK;
11137}
11138
11139/**
11140 * Goes thru all media of the given list and
11141 *
11142 * 1) calls i_detachDevice() on each of them for this machine and
11143 * 2) adds all Medium objects found in the process to the given list,
11144 * depending on cleanupMode.
11145 *
11146 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11147 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11148 * media to the list.
11149 *
11150 * This gets called from Machine::Unregister, both for the actual Machine and
11151 * the SnapshotMachine objects that might be found in the snapshots.
11152 *
11153 * Requires caller and locking. The machine lock must be passed in because it
11154 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11155 *
11156 * @param writeLock Machine lock from top-level caller; this gets passed to
11157 * i_detachDevice.
11158 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11159 * object if called for a SnapshotMachine.
11160 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11161 * added to llMedia; if Full, then all media get added;
11162 * otherwise no media get added.
11163 * @param llMedia Caller's list to receive Medium objects which got detached so
11164 * caller can close() them, depending on cleanupMode.
11165 * @return
11166 */
11167HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11168 Snapshot *pSnapshot,
11169 CleanupMode_T cleanupMode,
11170 MediaList &llMedia)
11171{
11172 Assert(isWriteLockOnCurrentThread());
11173
11174 HRESULT rc;
11175
11176 // make a temporary list because i_detachDevice invalidates iterators into
11177 // mMediumAttachments
11178 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11179
11180 for (MediumAttachmentList::iterator
11181 it = llAttachments2.begin();
11182 it != llAttachments2.end();
11183 ++it)
11184 {
11185 ComObjPtr<MediumAttachment> &pAttach = *it;
11186 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11187
11188 if (!pMedium.isNull())
11189 {
11190 AutoCaller mac(pMedium);
11191 if (FAILED(mac.rc())) return mac.rc();
11192 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11193 DeviceType_T devType = pMedium->i_getDeviceType();
11194 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11195 && devType == DeviceType_HardDisk)
11196 || (cleanupMode == CleanupMode_Full)
11197 )
11198 {
11199 llMedia.push_back(pMedium);
11200 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11201 /* Not allowed to keep this lock as below we need the parent
11202 * medium lock, and the lock order is parent to child. */
11203 lock.release();
11204 /*
11205 * Search for medias which are not attached to any machine, but
11206 * in the chain to an attached disk. Mediums are only consided
11207 * if they are:
11208 * - have only one child
11209 * - no references to any machines
11210 * - are of normal medium type
11211 */
11212 while (!pParent.isNull())
11213 {
11214 AutoCaller mac1(pParent);
11215 if (FAILED(mac1.rc())) return mac1.rc();
11216 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11217 if (pParent->i_getChildren().size() == 1)
11218 {
11219 if ( pParent->i_getMachineBackRefCount() == 0
11220 && pParent->i_getType() == MediumType_Normal
11221 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11222 llMedia.push_back(pParent);
11223 }
11224 else
11225 break;
11226 pParent = pParent->i_getParent();
11227 }
11228 }
11229 }
11230
11231 // real machine: then we need to use the proper method
11232 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11233
11234 if (FAILED(rc))
11235 return rc;
11236 }
11237
11238 return S_OK;
11239}
11240
11241/**
11242 * Perform deferred hard disk detachments.
11243 *
11244 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11245 * changed (not backed up).
11246 *
11247 * If @a aOnline is @c true then this method will also unlock the old hard
11248 * disks for which the new implicit diffs were created and will lock these new
11249 * diffs for writing.
11250 *
11251 * @param aOnline Whether the VM was online prior to this operation.
11252 *
11253 * @note Locks this object for writing!
11254 */
11255void Machine::i_commitMedia(bool aOnline /*= false*/)
11256{
11257 AutoCaller autoCaller(this);
11258 AssertComRCReturnVoid(autoCaller.rc());
11259
11260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11261
11262 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11263
11264 HRESULT rc = S_OK;
11265
11266 /* no attach/detach operations -- nothing to do */
11267 if (!mMediumAttachments.isBackedUp())
11268 return;
11269
11270 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11271 bool fMediaNeedsLocking = false;
11272
11273 /* enumerate new attachments */
11274 for (MediumAttachmentList::const_iterator
11275 it = mMediumAttachments->begin();
11276 it != mMediumAttachments->end();
11277 ++it)
11278 {
11279 MediumAttachment *pAttach = *it;
11280
11281 pAttach->i_commit();
11282
11283 Medium *pMedium = pAttach->i_getMedium();
11284 bool fImplicit = pAttach->i_isImplicit();
11285
11286 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11287 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11288 fImplicit));
11289
11290 /** @todo convert all this Machine-based voodoo to MediumAttachment
11291 * based commit logic. */
11292 if (fImplicit)
11293 {
11294 /* convert implicit attachment to normal */
11295 pAttach->i_setImplicit(false);
11296
11297 if ( aOnline
11298 && pMedium
11299 && pAttach->i_getType() == DeviceType_HardDisk
11300 )
11301 {
11302 /* update the appropriate lock list */
11303 MediumLockList *pMediumLockList;
11304 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11305 AssertComRC(rc);
11306 if (pMediumLockList)
11307 {
11308 /* unlock if there's a need to change the locking */
11309 if (!fMediaNeedsLocking)
11310 {
11311 rc = mData->mSession.mLockedMedia.Unlock();
11312 AssertComRC(rc);
11313 fMediaNeedsLocking = true;
11314 }
11315 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11316 AssertComRC(rc);
11317 rc = pMediumLockList->Append(pMedium, true);
11318 AssertComRC(rc);
11319 }
11320 }
11321
11322 continue;
11323 }
11324
11325 if (pMedium)
11326 {
11327 /* was this medium attached before? */
11328 for (MediumAttachmentList::iterator
11329 oldIt = oldAtts.begin();
11330 oldIt != oldAtts.end();
11331 ++oldIt)
11332 {
11333 MediumAttachment *pOldAttach = *oldIt;
11334 if (pOldAttach->i_getMedium() == pMedium)
11335 {
11336 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11337
11338 /* yes: remove from old to avoid de-association */
11339 oldAtts.erase(oldIt);
11340 break;
11341 }
11342 }
11343 }
11344 }
11345
11346 /* enumerate remaining old attachments and de-associate from the
11347 * current machine state */
11348 for (MediumAttachmentList::const_iterator
11349 it = oldAtts.begin();
11350 it != oldAtts.end();
11351 ++it)
11352 {
11353 MediumAttachment *pAttach = *it;
11354 Medium *pMedium = pAttach->i_getMedium();
11355
11356 /* Detach only hard disks, since DVD/floppy media is detached
11357 * instantly in MountMedium. */
11358 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11359 {
11360 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11361
11362 /* now de-associate from the current machine state */
11363 rc = pMedium->i_removeBackReference(mData->mUuid);
11364 AssertComRC(rc);
11365
11366 if (aOnline)
11367 {
11368 /* unlock since medium is not used anymore */
11369 MediumLockList *pMediumLockList;
11370 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11371 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11372 {
11373 /* this happens for online snapshots, there the attachment
11374 * is changing, but only to a diff image created under
11375 * the old one, so there is no separate lock list */
11376 Assert(!pMediumLockList);
11377 }
11378 else
11379 {
11380 AssertComRC(rc);
11381 if (pMediumLockList)
11382 {
11383 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11384 AssertComRC(rc);
11385 }
11386 }
11387 }
11388 }
11389 }
11390
11391 /* take media locks again so that the locking state is consistent */
11392 if (fMediaNeedsLocking)
11393 {
11394 Assert(aOnline);
11395 rc = mData->mSession.mLockedMedia.Lock();
11396 AssertComRC(rc);
11397 }
11398
11399 /* commit the hard disk changes */
11400 mMediumAttachments.commit();
11401
11402 if (i_isSessionMachine())
11403 {
11404 /*
11405 * Update the parent machine to point to the new owner.
11406 * This is necessary because the stored parent will point to the
11407 * session machine otherwise and cause crashes or errors later
11408 * when the session machine gets invalid.
11409 */
11410 /** @todo Change the MediumAttachment class to behave like any other
11411 * class in this regard by creating peer MediumAttachment
11412 * objects for session machines and share the data with the peer
11413 * machine.
11414 */
11415 for (MediumAttachmentList::const_iterator
11416 it = mMediumAttachments->begin();
11417 it != mMediumAttachments->end();
11418 ++it)
11419 (*it)->i_updateParentMachine(mPeer);
11420
11421 /* attach new data to the primary machine and reshare it */
11422 mPeer->mMediumAttachments.attach(mMediumAttachments);
11423 }
11424
11425 return;
11426}
11427
11428/**
11429 * Perform deferred deletion of implicitly created diffs.
11430 *
11431 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11432 * changed (not backed up).
11433 *
11434 * @note Locks this object for writing!
11435 */
11436void Machine::i_rollbackMedia()
11437{
11438 AutoCaller autoCaller(this);
11439 AssertComRCReturnVoid(autoCaller.rc());
11440
11441 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11442 LogFlowThisFunc(("Entering rollbackMedia\n"));
11443
11444 HRESULT rc = S_OK;
11445
11446 /* no attach/detach operations -- nothing to do */
11447 if (!mMediumAttachments.isBackedUp())
11448 return;
11449
11450 /* enumerate new attachments */
11451 for (MediumAttachmentList::const_iterator
11452 it = mMediumAttachments->begin();
11453 it != mMediumAttachments->end();
11454 ++it)
11455 {
11456 MediumAttachment *pAttach = *it;
11457 /* Fix up the backrefs for DVD/floppy media. */
11458 if (pAttach->i_getType() != DeviceType_HardDisk)
11459 {
11460 Medium *pMedium = pAttach->i_getMedium();
11461 if (pMedium)
11462 {
11463 rc = pMedium->i_removeBackReference(mData->mUuid);
11464 AssertComRC(rc);
11465 }
11466 }
11467
11468 (*it)->i_rollback();
11469
11470 pAttach = *it;
11471 /* Fix up the backrefs for DVD/floppy media. */
11472 if (pAttach->i_getType() != DeviceType_HardDisk)
11473 {
11474 Medium *pMedium = pAttach->i_getMedium();
11475 if (pMedium)
11476 {
11477 rc = pMedium->i_addBackReference(mData->mUuid);
11478 AssertComRC(rc);
11479 }
11480 }
11481 }
11482
11483 /** @todo convert all this Machine-based voodoo to MediumAttachment
11484 * based rollback logic. */
11485 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11486
11487 return;
11488}
11489
11490/**
11491 * Returns true if the settings file is located in the directory named exactly
11492 * as the machine; this means, among other things, that the machine directory
11493 * should be auto-renamed.
11494 *
11495 * @param aSettingsDir if not NULL, the full machine settings file directory
11496 * name will be assigned there.
11497 *
11498 * @note Doesn't lock anything.
11499 * @note Not thread safe (must be called from this object's lock).
11500 */
11501bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11502{
11503 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11504 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11505 if (aSettingsDir)
11506 *aSettingsDir = strMachineDirName;
11507 strMachineDirName.stripPath(); // vmname
11508 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11509 strConfigFileOnly.stripPath() // vmname.vbox
11510 .stripSuffix(); // vmname
11511 /** @todo hack, make somehow use of ComposeMachineFilename */
11512 if (mUserData->s.fDirectoryIncludesUUID)
11513 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11514
11515 AssertReturn(!strMachineDirName.isEmpty(), false);
11516 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11517
11518 return strMachineDirName == strConfigFileOnly;
11519}
11520
11521/**
11522 * Discards all changes to machine settings.
11523 *
11524 * @param aNotify Whether to notify the direct session about changes or not.
11525 *
11526 * @note Locks objects for writing!
11527 */
11528void Machine::i_rollback(bool aNotify)
11529{
11530 AutoCaller autoCaller(this);
11531 AssertComRCReturn(autoCaller.rc(), (void)0);
11532
11533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11534
11535 if (!mStorageControllers.isNull())
11536 {
11537 if (mStorageControllers.isBackedUp())
11538 {
11539 /* unitialize all new devices (absent in the backed up list). */
11540 StorageControllerList *backedList = mStorageControllers.backedUpData();
11541 for (StorageControllerList::const_iterator
11542 it = mStorageControllers->begin();
11543 it != mStorageControllers->end();
11544 ++it)
11545 {
11546 if ( std::find(backedList->begin(), backedList->end(), *it)
11547 == backedList->end()
11548 )
11549 {
11550 (*it)->uninit();
11551 }
11552 }
11553
11554 /* restore the list */
11555 mStorageControllers.rollback();
11556 }
11557
11558 /* rollback any changes to devices after restoring the list */
11559 if (mData->flModifications & IsModified_Storage)
11560 {
11561 for (StorageControllerList::const_iterator
11562 it = mStorageControllers->begin();
11563 it != mStorageControllers->end();
11564 ++it)
11565 {
11566 (*it)->i_rollback();
11567 }
11568 }
11569 }
11570
11571 if (!mUSBControllers.isNull())
11572 {
11573 if (mUSBControllers.isBackedUp())
11574 {
11575 /* unitialize all new devices (absent in the backed up list). */
11576 USBControllerList *backedList = mUSBControllers.backedUpData();
11577 for (USBControllerList::const_iterator
11578 it = mUSBControllers->begin();
11579 it != mUSBControllers->end();
11580 ++it)
11581 {
11582 if ( std::find(backedList->begin(), backedList->end(), *it)
11583 == backedList->end()
11584 )
11585 {
11586 (*it)->uninit();
11587 }
11588 }
11589
11590 /* restore the list */
11591 mUSBControllers.rollback();
11592 }
11593
11594 /* rollback any changes to devices after restoring the list */
11595 if (mData->flModifications & IsModified_USB)
11596 {
11597 for (USBControllerList::const_iterator
11598 it = mUSBControllers->begin();
11599 it != mUSBControllers->end();
11600 ++it)
11601 {
11602 (*it)->i_rollback();
11603 }
11604 }
11605 }
11606
11607 mUserData.rollback();
11608
11609 mHWData.rollback();
11610
11611 if (mData->flModifications & IsModified_Storage)
11612 i_rollbackMedia();
11613
11614 if (mBIOSSettings)
11615 mBIOSSettings->i_rollback();
11616
11617 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11618 mRecordingSettings->i_rollback();
11619
11620 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11621 mGraphicsAdapter->i_rollback();
11622
11623 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11624 mVRDEServer->i_rollback();
11625
11626 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11627 mAudioAdapter->i_rollback();
11628
11629 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11630 mUSBDeviceFilters->i_rollback();
11631
11632 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11633 mBandwidthControl->i_rollback();
11634
11635 if (!mHWData.isNull())
11636 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11637 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11638 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11639 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11640
11641 if (mData->flModifications & IsModified_NetworkAdapters)
11642 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11643 if ( mNetworkAdapters[slot]
11644 && mNetworkAdapters[slot]->i_isModified())
11645 {
11646 mNetworkAdapters[slot]->i_rollback();
11647 networkAdapters[slot] = mNetworkAdapters[slot];
11648 }
11649
11650 if (mData->flModifications & IsModified_SerialPorts)
11651 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11652 if ( mSerialPorts[slot]
11653 && mSerialPorts[slot]->i_isModified())
11654 {
11655 mSerialPorts[slot]->i_rollback();
11656 serialPorts[slot] = mSerialPorts[slot];
11657 }
11658
11659 if (mData->flModifications & IsModified_ParallelPorts)
11660 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11661 if ( mParallelPorts[slot]
11662 && mParallelPorts[slot]->i_isModified())
11663 {
11664 mParallelPorts[slot]->i_rollback();
11665 parallelPorts[slot] = mParallelPorts[slot];
11666 }
11667
11668 if (aNotify)
11669 {
11670 /* inform the direct session about changes */
11671
11672 ComObjPtr<Machine> that = this;
11673 uint32_t flModifications = mData->flModifications;
11674 alock.release();
11675
11676 if (flModifications & IsModified_SharedFolders)
11677 that->i_onSharedFolderChange();
11678
11679 if (flModifications & IsModified_VRDEServer)
11680 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11681 if (flModifications & IsModified_USB)
11682 that->i_onUSBControllerChange();
11683
11684 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11685 if (networkAdapters[slot])
11686 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11687 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11688 if (serialPorts[slot])
11689 that->i_onSerialPortChange(serialPorts[slot]);
11690 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11691 if (parallelPorts[slot])
11692 that->i_onParallelPortChange(parallelPorts[slot]);
11693
11694 if (flModifications & IsModified_Storage)
11695 {
11696 for (StorageControllerList::const_iterator
11697 it = mStorageControllers->begin();
11698 it != mStorageControllers->end();
11699 ++it)
11700 {
11701 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11702 }
11703 }
11704
11705
11706#if 0
11707 if (flModifications & IsModified_BandwidthControl)
11708 that->onBandwidthControlChange();
11709#endif
11710 }
11711}
11712
11713/**
11714 * Commits all the changes to machine settings.
11715 *
11716 * Note that this operation is supposed to never fail.
11717 *
11718 * @note Locks this object and children for writing.
11719 */
11720void Machine::i_commit()
11721{
11722 AutoCaller autoCaller(this);
11723 AssertComRCReturnVoid(autoCaller.rc());
11724
11725 AutoCaller peerCaller(mPeer);
11726 AssertComRCReturnVoid(peerCaller.rc());
11727
11728 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11729
11730 /*
11731 * use safe commit to ensure Snapshot machines (that share mUserData)
11732 * will still refer to a valid memory location
11733 */
11734 mUserData.commitCopy();
11735
11736 mHWData.commit();
11737
11738 if (mMediumAttachments.isBackedUp())
11739 i_commitMedia(Global::IsOnline(mData->mMachineState));
11740
11741 mBIOSSettings->i_commit();
11742 mRecordingSettings->i_commit();
11743 mGraphicsAdapter->i_commit();
11744 mVRDEServer->i_commit();
11745 mAudioAdapter->i_commit();
11746 mUSBDeviceFilters->i_commit();
11747 mBandwidthControl->i_commit();
11748
11749 /* Since mNetworkAdapters is a list which might have been changed (resized)
11750 * without using the Backupable<> template we need to handle the copying
11751 * of the list entries manually, including the creation of peers for the
11752 * new objects. */
11753 bool commitNetworkAdapters = false;
11754 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11755 if (mPeer)
11756 {
11757 /* commit everything, even the ones which will go away */
11758 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11759 mNetworkAdapters[slot]->i_commit();
11760 /* copy over the new entries, creating a peer and uninit the original */
11761 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11762 for (size_t slot = 0; slot < newSize; slot++)
11763 {
11764 /* look if this adapter has a peer device */
11765 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11766 if (!peer)
11767 {
11768 /* no peer means the adapter is a newly created one;
11769 * create a peer owning data this data share it with */
11770 peer.createObject();
11771 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11772 }
11773 mPeer->mNetworkAdapters[slot] = peer;
11774 }
11775 /* uninit any no longer needed network adapters */
11776 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11777 mNetworkAdapters[slot]->uninit();
11778 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11779 {
11780 if (mPeer->mNetworkAdapters[slot])
11781 mPeer->mNetworkAdapters[slot]->uninit();
11782 }
11783 /* Keep the original network adapter count until this point, so that
11784 * discarding a chipset type change will not lose settings. */
11785 mNetworkAdapters.resize(newSize);
11786 mPeer->mNetworkAdapters.resize(newSize);
11787 }
11788 else
11789 {
11790 /* we have no peer (our parent is the newly created machine);
11791 * just commit changes to the network adapters */
11792 commitNetworkAdapters = true;
11793 }
11794 if (commitNetworkAdapters)
11795 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11796 mNetworkAdapters[slot]->i_commit();
11797
11798 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11799 mSerialPorts[slot]->i_commit();
11800 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11801 mParallelPorts[slot]->i_commit();
11802
11803 bool commitStorageControllers = false;
11804
11805 if (mStorageControllers.isBackedUp())
11806 {
11807 mStorageControllers.commit();
11808
11809 if (mPeer)
11810 {
11811 /* Commit all changes to new controllers (this will reshare data with
11812 * peers for those who have peers) */
11813 StorageControllerList *newList = new StorageControllerList();
11814 for (StorageControllerList::const_iterator
11815 it = mStorageControllers->begin();
11816 it != mStorageControllers->end();
11817 ++it)
11818 {
11819 (*it)->i_commit();
11820
11821 /* look if this controller has a peer device */
11822 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11823 if (!peer)
11824 {
11825 /* no peer means the device is a newly created one;
11826 * create a peer owning data this device share it with */
11827 peer.createObject();
11828 peer->init(mPeer, *it, true /* aReshare */);
11829 }
11830 else
11831 {
11832 /* remove peer from the old list */
11833 mPeer->mStorageControllers->remove(peer);
11834 }
11835 /* and add it to the new list */
11836 newList->push_back(peer);
11837 }
11838
11839 /* uninit old peer's controllers that are left */
11840 for (StorageControllerList::const_iterator
11841 it = mPeer->mStorageControllers->begin();
11842 it != mPeer->mStorageControllers->end();
11843 ++it)
11844 {
11845 (*it)->uninit();
11846 }
11847
11848 /* attach new list of controllers to our peer */
11849 mPeer->mStorageControllers.attach(newList);
11850 }
11851 else
11852 {
11853 /* we have no peer (our parent is the newly created machine);
11854 * just commit changes to devices */
11855 commitStorageControllers = true;
11856 }
11857 }
11858 else
11859 {
11860 /* the list of controllers itself is not changed,
11861 * just commit changes to controllers themselves */
11862 commitStorageControllers = true;
11863 }
11864
11865 if (commitStorageControllers)
11866 {
11867 for (StorageControllerList::const_iterator
11868 it = mStorageControllers->begin();
11869 it != mStorageControllers->end();
11870 ++it)
11871 {
11872 (*it)->i_commit();
11873 }
11874 }
11875
11876 bool commitUSBControllers = false;
11877
11878 if (mUSBControllers.isBackedUp())
11879 {
11880 mUSBControllers.commit();
11881
11882 if (mPeer)
11883 {
11884 /* Commit all changes to new controllers (this will reshare data with
11885 * peers for those who have peers) */
11886 USBControllerList *newList = new USBControllerList();
11887 for (USBControllerList::const_iterator
11888 it = mUSBControllers->begin();
11889 it != mUSBControllers->end();
11890 ++it)
11891 {
11892 (*it)->i_commit();
11893
11894 /* look if this controller has a peer device */
11895 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11896 if (!peer)
11897 {
11898 /* no peer means the device is a newly created one;
11899 * create a peer owning data this device share it with */
11900 peer.createObject();
11901 peer->init(mPeer, *it, true /* aReshare */);
11902 }
11903 else
11904 {
11905 /* remove peer from the old list */
11906 mPeer->mUSBControllers->remove(peer);
11907 }
11908 /* and add it to the new list */
11909 newList->push_back(peer);
11910 }
11911
11912 /* uninit old peer's controllers that are left */
11913 for (USBControllerList::const_iterator
11914 it = mPeer->mUSBControllers->begin();
11915 it != mPeer->mUSBControllers->end();
11916 ++it)
11917 {
11918 (*it)->uninit();
11919 }
11920
11921 /* attach new list of controllers to our peer */
11922 mPeer->mUSBControllers.attach(newList);
11923 }
11924 else
11925 {
11926 /* we have no peer (our parent is the newly created machine);
11927 * just commit changes to devices */
11928 commitUSBControllers = true;
11929 }
11930 }
11931 else
11932 {
11933 /* the list of controllers itself is not changed,
11934 * just commit changes to controllers themselves */
11935 commitUSBControllers = true;
11936 }
11937
11938 if (commitUSBControllers)
11939 {
11940 for (USBControllerList::const_iterator
11941 it = mUSBControllers->begin();
11942 it != mUSBControllers->end();
11943 ++it)
11944 {
11945 (*it)->i_commit();
11946 }
11947 }
11948
11949 if (i_isSessionMachine())
11950 {
11951 /* attach new data to the primary machine and reshare it */
11952 mPeer->mUserData.attach(mUserData);
11953 mPeer->mHWData.attach(mHWData);
11954 /* mmMediumAttachments is reshared by fixupMedia */
11955 // mPeer->mMediumAttachments.attach(mMediumAttachments);
11956 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
11957 }
11958}
11959
11960/**
11961 * Copies all the hardware data from the given machine.
11962 *
11963 * Currently, only called when the VM is being restored from a snapshot. In
11964 * particular, this implies that the VM is not running during this method's
11965 * call.
11966 *
11967 * @note This method must be called from under this object's lock.
11968 *
11969 * @note This method doesn't call #i_commit(), so all data remains backed up and
11970 * unsaved.
11971 */
11972void Machine::i_copyFrom(Machine *aThat)
11973{
11974 AssertReturnVoid(!i_isSnapshotMachine());
11975 AssertReturnVoid(aThat->i_isSnapshotMachine());
11976
11977 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11978
11979 mHWData.assignCopy(aThat->mHWData);
11980
11981 // create copies of all shared folders (mHWData after attaching a copy
11982 // contains just references to original objects)
11983 for (HWData::SharedFolderList::iterator
11984 it = mHWData->mSharedFolders.begin();
11985 it != mHWData->mSharedFolders.end();
11986 ++it)
11987 {
11988 ComObjPtr<SharedFolder> folder;
11989 folder.createObject();
11990 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11991 AssertComRC(rc);
11992 *it = folder;
11993 }
11994
11995 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11996 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
11997 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
11998 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11999 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12000 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12001 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12002
12003 /* create private copies of all controllers */
12004 mStorageControllers.backup();
12005 mStorageControllers->clear();
12006 for (StorageControllerList::const_iterator
12007 it = aThat->mStorageControllers->begin();
12008 it != aThat->mStorageControllers->end();
12009 ++it)
12010 {
12011 ComObjPtr<StorageController> ctrl;
12012 ctrl.createObject();
12013 ctrl->initCopy(this, *it);
12014 mStorageControllers->push_back(ctrl);
12015 }
12016
12017 /* create private copies of all USB controllers */
12018 mUSBControllers.backup();
12019 mUSBControllers->clear();
12020 for (USBControllerList::const_iterator
12021 it = aThat->mUSBControllers->begin();
12022 it != aThat->mUSBControllers->end();
12023 ++it)
12024 {
12025 ComObjPtr<USBController> ctrl;
12026 ctrl.createObject();
12027 ctrl->initCopy(this, *it);
12028 mUSBControllers->push_back(ctrl);
12029 }
12030
12031 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12032 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12033 {
12034 if (mNetworkAdapters[slot].isNotNull())
12035 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12036 else
12037 {
12038 unconst(mNetworkAdapters[slot]).createObject();
12039 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12040 }
12041 }
12042 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12043 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12044 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12045 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12046}
12047
12048/**
12049 * Returns whether the given storage controller is hotplug capable.
12050 *
12051 * @returns true if the controller supports hotplugging
12052 * false otherwise.
12053 * @param enmCtrlType The controller type to check for.
12054 */
12055bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12056{
12057 ComPtr<ISystemProperties> systemProperties;
12058 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12059 if (FAILED(rc))
12060 return false;
12061
12062 BOOL aHotplugCapable = FALSE;
12063 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12064
12065 return RT_BOOL(aHotplugCapable);
12066}
12067
12068#ifdef VBOX_WITH_RESOURCE_USAGE_API
12069
12070void Machine::i_getDiskList(MediaList &list)
12071{
12072 for (MediumAttachmentList::const_iterator
12073 it = mMediumAttachments->begin();
12074 it != mMediumAttachments->end();
12075 ++it)
12076 {
12077 MediumAttachment *pAttach = *it;
12078 /* just in case */
12079 AssertContinue(pAttach);
12080
12081 AutoCaller localAutoCallerA(pAttach);
12082 if (FAILED(localAutoCallerA.rc())) continue;
12083
12084 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12085
12086 if (pAttach->i_getType() == DeviceType_HardDisk)
12087 list.push_back(pAttach->i_getMedium());
12088 }
12089}
12090
12091void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12092{
12093 AssertReturnVoid(isWriteLockOnCurrentThread());
12094 AssertPtrReturnVoid(aCollector);
12095
12096 pm::CollectorHAL *hal = aCollector->getHAL();
12097 /* Create sub metrics */
12098 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12099 "Percentage of processor time spent in user mode by the VM process.");
12100 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12101 "Percentage of processor time spent in kernel mode by the VM process.");
12102 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12103 "Size of resident portion of VM process in memory.");
12104 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12105 "Actual size of all VM disks combined.");
12106 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12107 "Network receive rate.");
12108 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12109 "Network transmit rate.");
12110 /* Create and register base metrics */
12111 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12112 cpuLoadUser, cpuLoadKernel);
12113 aCollector->registerBaseMetric(cpuLoad);
12114 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12115 ramUsageUsed);
12116 aCollector->registerBaseMetric(ramUsage);
12117 MediaList disks;
12118 i_getDiskList(disks);
12119 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12120 diskUsageUsed);
12121 aCollector->registerBaseMetric(diskUsage);
12122
12123 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12124 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12125 new pm::AggregateAvg()));
12126 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12127 new pm::AggregateMin()));
12128 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12129 new pm::AggregateMax()));
12130 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12131 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12132 new pm::AggregateAvg()));
12133 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12134 new pm::AggregateMin()));
12135 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12136 new pm::AggregateMax()));
12137
12138 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12139 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12140 new pm::AggregateAvg()));
12141 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12142 new pm::AggregateMin()));
12143 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12144 new pm::AggregateMax()));
12145
12146 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12147 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12148 new pm::AggregateAvg()));
12149 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12150 new pm::AggregateMin()));
12151 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12152 new pm::AggregateMax()));
12153
12154
12155 /* Guest metrics collector */
12156 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12157 aCollector->registerGuest(mCollectorGuest);
12158 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12159
12160 /* Create sub metrics */
12161 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12162 "Percentage of processor time spent in user mode as seen by the guest.");
12163 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12164 "Percentage of processor time spent in kernel mode as seen by the guest.");
12165 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12166 "Percentage of processor time spent idling as seen by the guest.");
12167
12168 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12169 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12170 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12171 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12172 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12173 pm::SubMetric *guestMemCache = new pm::SubMetric(
12174 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12175
12176 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12177 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12178
12179 /* Create and register base metrics */
12180 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12181 machineNetRx, machineNetTx);
12182 aCollector->registerBaseMetric(machineNetRate);
12183
12184 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12185 guestLoadUser, guestLoadKernel, guestLoadIdle);
12186 aCollector->registerBaseMetric(guestCpuLoad);
12187
12188 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12189 guestMemTotal, guestMemFree,
12190 guestMemBalloon, guestMemShared,
12191 guestMemCache, guestPagedTotal);
12192 aCollector->registerBaseMetric(guestCpuMem);
12193
12194 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12195 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12196 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12197 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12198
12199 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12200 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12201 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12202 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12203
12204 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12205 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12206 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12207 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12208
12209 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12210 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12211 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12212 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12213
12214 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12215 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12216 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12217 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12218
12219 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12220 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12221 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12222 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12223
12224 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12225 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12226 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12227 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12228
12229 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12230 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12231 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12233
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12238
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12243
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12246 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12248}
12249
12250void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12251{
12252 AssertReturnVoid(isWriteLockOnCurrentThread());
12253
12254 if (aCollector)
12255 {
12256 aCollector->unregisterMetricsFor(aMachine);
12257 aCollector->unregisterBaseMetricsFor(aMachine);
12258 }
12259}
12260
12261#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12262
12263
12264////////////////////////////////////////////////////////////////////////////////
12265
12266DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12267
12268HRESULT SessionMachine::FinalConstruct()
12269{
12270 LogFlowThisFunc(("\n"));
12271
12272 mClientToken = NULL;
12273
12274 return BaseFinalConstruct();
12275}
12276
12277void SessionMachine::FinalRelease()
12278{
12279 LogFlowThisFunc(("\n"));
12280
12281 Assert(!mClientToken);
12282 /* paranoia, should not hang around any more */
12283 if (mClientToken)
12284 {
12285 delete mClientToken;
12286 mClientToken = NULL;
12287 }
12288
12289 uninit(Uninit::Unexpected);
12290
12291 BaseFinalRelease();
12292}
12293
12294/**
12295 * @note Must be called only by Machine::LockMachine() from its own write lock.
12296 */
12297HRESULT SessionMachine::init(Machine *aMachine)
12298{
12299 LogFlowThisFuncEnter();
12300 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12301
12302 AssertReturn(aMachine, E_INVALIDARG);
12303
12304 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12305
12306 /* Enclose the state transition NotReady->InInit->Ready */
12307 AutoInitSpan autoInitSpan(this);
12308 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12309
12310 HRESULT rc = S_OK;
12311
12312 RT_ZERO(mAuthLibCtx);
12313
12314 /* create the machine client token */
12315 try
12316 {
12317 mClientToken = new ClientToken(aMachine, this);
12318 if (!mClientToken->isReady())
12319 {
12320 delete mClientToken;
12321 mClientToken = NULL;
12322 rc = E_FAIL;
12323 }
12324 }
12325 catch (std::bad_alloc &)
12326 {
12327 rc = E_OUTOFMEMORY;
12328 }
12329 if (FAILED(rc))
12330 return rc;
12331
12332 /* memorize the peer Machine */
12333 unconst(mPeer) = aMachine;
12334 /* share the parent pointer */
12335 unconst(mParent) = aMachine->mParent;
12336
12337 /* take the pointers to data to share */
12338 mData.share(aMachine->mData);
12339 mSSData.share(aMachine->mSSData);
12340
12341 mUserData.share(aMachine->mUserData);
12342 mHWData.share(aMachine->mHWData);
12343 mMediumAttachments.share(aMachine->mMediumAttachments);
12344
12345 mStorageControllers.allocate();
12346 for (StorageControllerList::const_iterator
12347 it = aMachine->mStorageControllers->begin();
12348 it != aMachine->mStorageControllers->end();
12349 ++it)
12350 {
12351 ComObjPtr<StorageController> ctl;
12352 ctl.createObject();
12353 ctl->init(this, *it);
12354 mStorageControllers->push_back(ctl);
12355 }
12356
12357 mUSBControllers.allocate();
12358 for (USBControllerList::const_iterator
12359 it = aMachine->mUSBControllers->begin();
12360 it != aMachine->mUSBControllers->end();
12361 ++it)
12362 {
12363 ComObjPtr<USBController> ctl;
12364 ctl.createObject();
12365 ctl->init(this, *it);
12366 mUSBControllers->push_back(ctl);
12367 }
12368
12369 unconst(mBIOSSettings).createObject();
12370 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12371 unconst(mRecordingSettings).createObject();
12372 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12373 /* create another GraphicsAdapter object that will be mutable */
12374 unconst(mGraphicsAdapter).createObject();
12375 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12376 /* create another VRDEServer object that will be mutable */
12377 unconst(mVRDEServer).createObject();
12378 mVRDEServer->init(this, aMachine->mVRDEServer);
12379 /* create another audio adapter object that will be mutable */
12380 unconst(mAudioAdapter).createObject();
12381 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12382 /* create a list of serial ports that will be mutable */
12383 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12384 {
12385 unconst(mSerialPorts[slot]).createObject();
12386 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12387 }
12388 /* create a list of parallel ports that will be mutable */
12389 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12390 {
12391 unconst(mParallelPorts[slot]).createObject();
12392 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12393 }
12394
12395 /* create another USB device filters object that will be mutable */
12396 unconst(mUSBDeviceFilters).createObject();
12397 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12398
12399 /* create a list of network adapters that will be mutable */
12400 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12401 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12402 {
12403 unconst(mNetworkAdapters[slot]).createObject();
12404 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12405 }
12406
12407 /* create another bandwidth control object that will be mutable */
12408 unconst(mBandwidthControl).createObject();
12409 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12410
12411 /* default is to delete saved state on Saved -> PoweredOff transition */
12412 mRemoveSavedState = true;
12413
12414 /* Confirm a successful initialization when it's the case */
12415 autoInitSpan.setSucceeded();
12416
12417 miNATNetworksStarted = 0;
12418
12419 LogFlowThisFuncLeave();
12420 return rc;
12421}
12422
12423/**
12424 * Uninitializes this session object. If the reason is other than
12425 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12426 * or the client watcher code.
12427 *
12428 * @param aReason uninitialization reason
12429 *
12430 * @note Locks mParent + this object for writing.
12431 */
12432void SessionMachine::uninit(Uninit::Reason aReason)
12433{
12434 LogFlowThisFuncEnter();
12435 LogFlowThisFunc(("reason=%d\n", aReason));
12436
12437 /*
12438 * Strongly reference ourselves to prevent this object deletion after
12439 * mData->mSession.mMachine.setNull() below (which can release the last
12440 * reference and call the destructor). Important: this must be done before
12441 * accessing any members (and before AutoUninitSpan that does it as well).
12442 * This self reference will be released as the very last step on return.
12443 */
12444 ComObjPtr<SessionMachine> selfRef;
12445 if (aReason != Uninit::Unexpected)
12446 selfRef = this;
12447
12448 /* Enclose the state transition Ready->InUninit->NotReady */
12449 AutoUninitSpan autoUninitSpan(this);
12450 if (autoUninitSpan.uninitDone())
12451 {
12452 LogFlowThisFunc(("Already uninitialized\n"));
12453 LogFlowThisFuncLeave();
12454 return;
12455 }
12456
12457 if (autoUninitSpan.initFailed())
12458 {
12459 /* We've been called by init() because it's failed. It's not really
12460 * necessary (nor it's safe) to perform the regular uninit sequence
12461 * below, the following is enough.
12462 */
12463 LogFlowThisFunc(("Initialization failed.\n"));
12464 /* destroy the machine client token */
12465 if (mClientToken)
12466 {
12467 delete mClientToken;
12468 mClientToken = NULL;
12469 }
12470 uninitDataAndChildObjects();
12471 mData.free();
12472 unconst(mParent) = NULL;
12473 unconst(mPeer) = NULL;
12474 LogFlowThisFuncLeave();
12475 return;
12476 }
12477
12478 MachineState_T lastState;
12479 {
12480 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12481 lastState = mData->mMachineState;
12482 }
12483 NOREF(lastState);
12484
12485#ifdef VBOX_WITH_USB
12486 // release all captured USB devices, but do this before requesting the locks below
12487 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12488 {
12489 /* Console::captureUSBDevices() is called in the VM process only after
12490 * setting the machine state to Starting or Restoring.
12491 * Console::detachAllUSBDevices() will be called upon successful
12492 * termination. So, we need to release USB devices only if there was
12493 * an abnormal termination of a running VM.
12494 *
12495 * This is identical to SessionMachine::DetachAllUSBDevices except
12496 * for the aAbnormal argument. */
12497 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12498 AssertComRC(rc);
12499 NOREF(rc);
12500
12501 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12502 if (service)
12503 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12504 }
12505#endif /* VBOX_WITH_USB */
12506
12507 // we need to lock this object in uninit() because the lock is shared
12508 // with mPeer (as well as data we modify below). mParent lock is needed
12509 // by several calls to it.
12510 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12511
12512#ifdef VBOX_WITH_RESOURCE_USAGE_API
12513 /*
12514 * It is safe to call Machine::i_unregisterMetrics() here because
12515 * PerformanceCollector::samplerCallback no longer accesses guest methods
12516 * holding the lock.
12517 */
12518 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12519 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12520 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12521 if (mCollectorGuest)
12522 {
12523 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12524 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12525 mCollectorGuest = NULL;
12526 }
12527#endif
12528
12529 if (aReason == Uninit::Abnormal)
12530 {
12531 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12532
12533 /* reset the state to Aborted */
12534 if (mData->mMachineState != MachineState_Aborted)
12535 i_setMachineState(MachineState_Aborted);
12536 }
12537
12538 // any machine settings modified?
12539 if (mData->flModifications)
12540 {
12541 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12542 i_rollback(false /* aNotify */);
12543 }
12544
12545 mData->mSession.mPID = NIL_RTPROCESS;
12546
12547 if (aReason == Uninit::Unexpected)
12548 {
12549 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12550 * client watcher thread to update the set of machines that have open
12551 * sessions. */
12552 mParent->i_updateClientWatcher();
12553 }
12554
12555 /* uninitialize all remote controls */
12556 if (mData->mSession.mRemoteControls.size())
12557 {
12558 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12559 mData->mSession.mRemoteControls.size()));
12560
12561 /* Always restart a the beginning, since the iterator is invalidated
12562 * by using erase(). */
12563 for (Data::Session::RemoteControlList::iterator
12564 it = mData->mSession.mRemoteControls.begin();
12565 it != mData->mSession.mRemoteControls.end();
12566 it = mData->mSession.mRemoteControls.begin())
12567 {
12568 ComPtr<IInternalSessionControl> pControl = *it;
12569 mData->mSession.mRemoteControls.erase(it);
12570 multilock.release();
12571 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12572 HRESULT rc = pControl->Uninitialize();
12573 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12574 if (FAILED(rc))
12575 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12576 multilock.acquire();
12577 }
12578 mData->mSession.mRemoteControls.clear();
12579 }
12580
12581 /* Remove all references to the NAT network service. The service will stop
12582 * if all references (also from other VMs) are removed. */
12583 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12584 {
12585 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12586 {
12587 BOOL enabled;
12588 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12589 if ( FAILED(hrc)
12590 || !enabled)
12591 continue;
12592
12593 NetworkAttachmentType_T type;
12594 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12595 if ( SUCCEEDED(hrc)
12596 && type == NetworkAttachmentType_NATNetwork)
12597 {
12598 Bstr name;
12599 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12600 if (SUCCEEDED(hrc))
12601 {
12602 multilock.release();
12603 Utf8Str strName(name);
12604 LogRel(("VM '%s' stops using NAT network '%s'\n",
12605 mUserData->s.strName.c_str(), strName.c_str()));
12606 mParent->i_natNetworkRefDec(strName);
12607 multilock.acquire();
12608 }
12609 }
12610 }
12611 }
12612
12613 /*
12614 * An expected uninitialization can come only from #i_checkForDeath().
12615 * Otherwise it means that something's gone really wrong (for example,
12616 * the Session implementation has released the VirtualBox reference
12617 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12618 * etc). However, it's also possible, that the client releases the IPC
12619 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12620 * but the VirtualBox release event comes first to the server process.
12621 * This case is practically possible, so we should not assert on an
12622 * unexpected uninit, just log a warning.
12623 */
12624
12625 if (aReason == Uninit::Unexpected)
12626 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12627
12628 if (aReason != Uninit::Normal)
12629 {
12630 mData->mSession.mDirectControl.setNull();
12631 }
12632 else
12633 {
12634 /* this must be null here (see #OnSessionEnd()) */
12635 Assert(mData->mSession.mDirectControl.isNull());
12636 Assert(mData->mSession.mState == SessionState_Unlocking);
12637 Assert(!mData->mSession.mProgress.isNull());
12638 }
12639 if (mData->mSession.mProgress)
12640 {
12641 if (aReason == Uninit::Normal)
12642 mData->mSession.mProgress->i_notifyComplete(S_OK);
12643 else
12644 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12645 COM_IIDOF(ISession),
12646 getComponentName(),
12647 tr("The VM session was aborted"));
12648 mData->mSession.mProgress.setNull();
12649 }
12650
12651 if (mConsoleTaskData.mProgress)
12652 {
12653 Assert(aReason == Uninit::Abnormal);
12654 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12655 COM_IIDOF(ISession),
12656 getComponentName(),
12657 tr("The VM session was aborted"));
12658 mConsoleTaskData.mProgress.setNull();
12659 }
12660
12661 /* remove the association between the peer machine and this session machine */
12662 Assert( (SessionMachine*)mData->mSession.mMachine == this
12663 || aReason == Uninit::Unexpected);
12664
12665 /* reset the rest of session data */
12666 mData->mSession.mLockType = LockType_Null;
12667 mData->mSession.mMachine.setNull();
12668 mData->mSession.mState = SessionState_Unlocked;
12669 mData->mSession.mName.setNull();
12670
12671 /* destroy the machine client token before leaving the exclusive lock */
12672 if (mClientToken)
12673 {
12674 delete mClientToken;
12675 mClientToken = NULL;
12676 }
12677
12678 /* fire an event */
12679 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12680
12681 uninitDataAndChildObjects();
12682
12683 /* free the essential data structure last */
12684 mData.free();
12685
12686 /* release the exclusive lock before setting the below two to NULL */
12687 multilock.release();
12688
12689 unconst(mParent) = NULL;
12690 unconst(mPeer) = NULL;
12691
12692 AuthLibUnload(&mAuthLibCtx);
12693
12694 LogFlowThisFuncLeave();
12695}
12696
12697// util::Lockable interface
12698////////////////////////////////////////////////////////////////////////////////
12699
12700/**
12701 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12702 * with the primary Machine instance (mPeer).
12703 */
12704RWLockHandle *SessionMachine::lockHandle() const
12705{
12706 AssertReturn(mPeer != NULL, NULL);
12707 return mPeer->lockHandle();
12708}
12709
12710// IInternalMachineControl methods
12711////////////////////////////////////////////////////////////////////////////////
12712
12713/**
12714 * Passes collected guest statistics to performance collector object
12715 */
12716HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12717 ULONG aCpuKernel, ULONG aCpuIdle,
12718 ULONG aMemTotal, ULONG aMemFree,
12719 ULONG aMemBalloon, ULONG aMemShared,
12720 ULONG aMemCache, ULONG aPageTotal,
12721 ULONG aAllocVMM, ULONG aFreeVMM,
12722 ULONG aBalloonedVMM, ULONG aSharedVMM,
12723 ULONG aVmNetRx, ULONG aVmNetTx)
12724{
12725#ifdef VBOX_WITH_RESOURCE_USAGE_API
12726 if (mCollectorGuest)
12727 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12728 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12729 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12730 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12731
12732 return S_OK;
12733#else
12734 NOREF(aValidStats);
12735 NOREF(aCpuUser);
12736 NOREF(aCpuKernel);
12737 NOREF(aCpuIdle);
12738 NOREF(aMemTotal);
12739 NOREF(aMemFree);
12740 NOREF(aMemBalloon);
12741 NOREF(aMemShared);
12742 NOREF(aMemCache);
12743 NOREF(aPageTotal);
12744 NOREF(aAllocVMM);
12745 NOREF(aFreeVMM);
12746 NOREF(aBalloonedVMM);
12747 NOREF(aSharedVMM);
12748 NOREF(aVmNetRx);
12749 NOREF(aVmNetTx);
12750 return E_NOTIMPL;
12751#endif
12752}
12753
12754////////////////////////////////////////////////////////////////////////////////
12755//
12756// SessionMachine task records
12757//
12758////////////////////////////////////////////////////////////////////////////////
12759
12760/**
12761 * Task record for saving the machine state.
12762 */
12763class SessionMachine::SaveStateTask
12764 : public Machine::Task
12765{
12766public:
12767 SaveStateTask(SessionMachine *m,
12768 Progress *p,
12769 const Utf8Str &t,
12770 Reason_T enmReason,
12771 const Utf8Str &strStateFilePath)
12772 : Task(m, p, t),
12773 m_enmReason(enmReason),
12774 m_strStateFilePath(strStateFilePath)
12775 {}
12776
12777private:
12778 void handler()
12779 {
12780 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12781 }
12782
12783 Reason_T m_enmReason;
12784 Utf8Str m_strStateFilePath;
12785
12786 friend class SessionMachine;
12787};
12788
12789/**
12790 * Task thread implementation for SessionMachine::SaveState(), called from
12791 * SessionMachine::taskHandler().
12792 *
12793 * @note Locks this object for writing.
12794 *
12795 * @param task
12796 * @return
12797 */
12798void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12799{
12800 LogFlowThisFuncEnter();
12801
12802 AutoCaller autoCaller(this);
12803 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12804 if (FAILED(autoCaller.rc()))
12805 {
12806 /* we might have been uninitialized because the session was accidentally
12807 * closed by the client, so don't assert */
12808 HRESULT rc = setError(E_FAIL,
12809 tr("The session has been accidentally closed"));
12810 task.m_pProgress->i_notifyComplete(rc);
12811 LogFlowThisFuncLeave();
12812 return;
12813 }
12814
12815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12816
12817 HRESULT rc = S_OK;
12818
12819 try
12820 {
12821 ComPtr<IInternalSessionControl> directControl;
12822 if (mData->mSession.mLockType == LockType_VM)
12823 directControl = mData->mSession.mDirectControl;
12824 if (directControl.isNull())
12825 throw setError(VBOX_E_INVALID_VM_STATE,
12826 tr("Trying to save state without a running VM"));
12827 alock.release();
12828 BOOL fSuspendedBySave;
12829 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12830 Assert(!fSuspendedBySave);
12831 alock.acquire();
12832
12833 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12834 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12835 throw E_FAIL);
12836
12837 if (SUCCEEDED(rc))
12838 {
12839 mSSData->strStateFilePath = task.m_strStateFilePath;
12840
12841 /* save all VM settings */
12842 rc = i_saveSettings(NULL);
12843 // no need to check whether VirtualBox.xml needs saving also since
12844 // we can't have a name change pending at this point
12845 }
12846 else
12847 {
12848 // On failure, set the state to the state we had at the beginning.
12849 i_setMachineState(task.m_machineStateBackup);
12850 i_updateMachineStateOnClient();
12851
12852 // Delete the saved state file (might have been already created).
12853 // No need to check whether this is shared with a snapshot here
12854 // because we certainly created a fresh saved state file here.
12855 RTFileDelete(task.m_strStateFilePath.c_str());
12856 }
12857 }
12858 catch (HRESULT aRC) { rc = aRC; }
12859
12860 task.m_pProgress->i_notifyComplete(rc);
12861
12862 LogFlowThisFuncLeave();
12863}
12864
12865/**
12866 * @note Locks this object for writing.
12867 */
12868HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12869{
12870 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12871}
12872
12873HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12874{
12875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12876
12877 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12878 if (FAILED(rc)) return rc;
12879
12880 if ( mData->mMachineState != MachineState_Running
12881 && mData->mMachineState != MachineState_Paused
12882 )
12883 return setError(VBOX_E_INVALID_VM_STATE,
12884 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12885 Global::stringifyMachineState(mData->mMachineState));
12886
12887 ComObjPtr<Progress> pProgress;
12888 pProgress.createObject();
12889 rc = pProgress->init(i_getVirtualBox(),
12890 static_cast<IMachine *>(this) /* aInitiator */,
12891 tr("Saving the execution state of the virtual machine"),
12892 FALSE /* aCancelable */);
12893 if (FAILED(rc))
12894 return rc;
12895
12896 Utf8Str strStateFilePath;
12897 i_composeSavedStateFilename(strStateFilePath);
12898
12899 /* create and start the task on a separate thread (note that it will not
12900 * start working until we release alock) */
12901 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12902 rc = pTask->createThread();
12903 if (FAILED(rc))
12904 return rc;
12905
12906 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12907 i_setMachineState(MachineState_Saving);
12908 i_updateMachineStateOnClient();
12909
12910 pProgress.queryInterfaceTo(aProgress.asOutParam());
12911
12912 return S_OK;
12913}
12914
12915/**
12916 * @note Locks this object for writing.
12917 */
12918HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12919{
12920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12921
12922 HRESULT rc = i_checkStateDependency(MutableStateDep);
12923 if (FAILED(rc)) return rc;
12924
12925 if ( mData->mMachineState != MachineState_PoweredOff
12926 && mData->mMachineState != MachineState_Teleported
12927 && mData->mMachineState != MachineState_Aborted
12928 )
12929 return setError(VBOX_E_INVALID_VM_STATE,
12930 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12931 Global::stringifyMachineState(mData->mMachineState));
12932
12933 com::Utf8Str stateFilePathFull;
12934 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12935 if (RT_FAILURE(vrc))
12936 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12937 tr("Invalid saved state file path '%s' (%Rrc)"),
12938 aSavedStateFile.c_str(),
12939 vrc);
12940
12941 mSSData->strStateFilePath = stateFilePathFull;
12942
12943 /* The below i_setMachineState() will detect the state transition and will
12944 * update the settings file */
12945
12946 return i_setMachineState(MachineState_Saved);
12947}
12948
12949/**
12950 * @note Locks this object for writing.
12951 */
12952HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12953{
12954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12955
12956 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12957 if (FAILED(rc)) return rc;
12958
12959 if (mData->mMachineState != MachineState_Saved)
12960 return setError(VBOX_E_INVALID_VM_STATE,
12961 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12962 Global::stringifyMachineState(mData->mMachineState));
12963
12964 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12965
12966 /*
12967 * Saved -> PoweredOff transition will be detected in the SessionMachine
12968 * and properly handled.
12969 */
12970 rc = i_setMachineState(MachineState_PoweredOff);
12971 return rc;
12972}
12973
12974
12975/**
12976 * @note Locks the same as #i_setMachineState() does.
12977 */
12978HRESULT SessionMachine::updateState(MachineState_T aState)
12979{
12980 return i_setMachineState(aState);
12981}
12982
12983/**
12984 * @note Locks this object for writing.
12985 */
12986HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12987{
12988 IProgress *pProgress(aProgress);
12989
12990 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12991
12992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12993
12994 if (mData->mSession.mState != SessionState_Locked)
12995 return VBOX_E_INVALID_OBJECT_STATE;
12996
12997 if (!mData->mSession.mProgress.isNull())
12998 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12999
13000 /* If we didn't reference the NAT network service yet, add a reference to
13001 * force a start */
13002 if (miNATNetworksStarted < 1)
13003 {
13004 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13005 {
13006 BOOL enabled;
13007 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13008 if ( FAILED(hrc)
13009 || !enabled)
13010 continue;
13011
13012 NetworkAttachmentType_T type;
13013 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13014 if ( SUCCEEDED(hrc)
13015 && type == NetworkAttachmentType_NATNetwork)
13016 {
13017 Bstr name;
13018 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13019 if (SUCCEEDED(hrc))
13020 {
13021 Utf8Str strName(name);
13022 LogRel(("VM '%s' starts using NAT network '%s'\n",
13023 mUserData->s.strName.c_str(), strName.c_str()));
13024 mPeer->lockHandle()->unlockWrite();
13025 mParent->i_natNetworkRefInc(strName);
13026#ifdef RT_LOCK_STRICT
13027 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13028#else
13029 mPeer->lockHandle()->lockWrite();
13030#endif
13031 }
13032 }
13033 }
13034 miNATNetworksStarted++;
13035 }
13036
13037 LogFlowThisFunc(("returns S_OK.\n"));
13038 return S_OK;
13039}
13040
13041/**
13042 * @note Locks this object for writing.
13043 */
13044HRESULT SessionMachine::endPowerUp(LONG aResult)
13045{
13046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13047
13048 if (mData->mSession.mState != SessionState_Locked)
13049 return VBOX_E_INVALID_OBJECT_STATE;
13050
13051 /* Finalize the LaunchVMProcess progress object. */
13052 if (mData->mSession.mProgress)
13053 {
13054 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13055 mData->mSession.mProgress.setNull();
13056 }
13057
13058 if (SUCCEEDED((HRESULT)aResult))
13059 {
13060#ifdef VBOX_WITH_RESOURCE_USAGE_API
13061 /* The VM has been powered up successfully, so it makes sense
13062 * now to offer the performance metrics for a running machine
13063 * object. Doing it earlier wouldn't be safe. */
13064 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13065 mData->mSession.mPID);
13066#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13067 }
13068
13069 return S_OK;
13070}
13071
13072/**
13073 * @note Locks this object for writing.
13074 */
13075HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13076{
13077 LogFlowThisFuncEnter();
13078
13079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13080
13081 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13082 E_FAIL);
13083
13084 /* create a progress object to track operation completion */
13085 ComObjPtr<Progress> pProgress;
13086 pProgress.createObject();
13087 pProgress->init(i_getVirtualBox(),
13088 static_cast<IMachine *>(this) /* aInitiator */,
13089 tr("Stopping the virtual machine"),
13090 FALSE /* aCancelable */);
13091
13092 /* fill in the console task data */
13093 mConsoleTaskData.mLastState = mData->mMachineState;
13094 mConsoleTaskData.mProgress = pProgress;
13095
13096 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13097 i_setMachineState(MachineState_Stopping);
13098
13099 pProgress.queryInterfaceTo(aProgress.asOutParam());
13100
13101 return S_OK;
13102}
13103
13104/**
13105 * @note Locks this object for writing.
13106 */
13107HRESULT SessionMachine::endPoweringDown(LONG aResult,
13108 const com::Utf8Str &aErrMsg)
13109{
13110 HRESULT const hrcResult = (HRESULT)aResult;
13111 LogFlowThisFuncEnter();
13112
13113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13114
13115 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13116 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13117 && mConsoleTaskData.mLastState != MachineState_Null,
13118 E_FAIL);
13119
13120 /*
13121 * On failure, set the state to the state we had when BeginPoweringDown()
13122 * was called (this is expected by Console::PowerDown() and the associated
13123 * task). On success the VM process already changed the state to
13124 * MachineState_PoweredOff, so no need to do anything.
13125 */
13126 if (FAILED(hrcResult))
13127 i_setMachineState(mConsoleTaskData.mLastState);
13128
13129 /* notify the progress object about operation completion */
13130 Assert(mConsoleTaskData.mProgress);
13131 if (SUCCEEDED(hrcResult))
13132 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13133 else
13134 {
13135 if (aErrMsg.length())
13136 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13137 COM_IIDOF(ISession),
13138 getComponentName(),
13139 aErrMsg.c_str());
13140 else
13141 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13142 }
13143
13144 /* clear out the temporary saved state data */
13145 mConsoleTaskData.mLastState = MachineState_Null;
13146 mConsoleTaskData.mProgress.setNull();
13147
13148 LogFlowThisFuncLeave();
13149 return S_OK;
13150}
13151
13152
13153/**
13154 * Goes through the USB filters of the given machine to see if the given
13155 * device matches any filter or not.
13156 *
13157 * @note Locks the same as USBController::hasMatchingFilter() does.
13158 */
13159HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13160 BOOL *aMatched,
13161 ULONG *aMaskedInterfaces)
13162{
13163 LogFlowThisFunc(("\n"));
13164
13165#ifdef VBOX_WITH_USB
13166 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13167#else
13168 NOREF(aDevice);
13169 NOREF(aMaskedInterfaces);
13170 *aMatched = FALSE;
13171#endif
13172
13173 return S_OK;
13174}
13175
13176/**
13177 * @note Locks the same as Host::captureUSBDevice() does.
13178 */
13179HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13180{
13181 LogFlowThisFunc(("\n"));
13182
13183#ifdef VBOX_WITH_USB
13184 /* if captureDeviceForVM() fails, it must have set extended error info */
13185 clearError();
13186 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13187 if (FAILED(rc)) return rc;
13188
13189 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13190 AssertReturn(service, E_FAIL);
13191 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13192#else
13193 RT_NOREF(aId, aCaptureFilename);
13194 return E_NOTIMPL;
13195#endif
13196}
13197
13198/**
13199 * @note Locks the same as Host::detachUSBDevice() does.
13200 */
13201HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13202 BOOL aDone)
13203{
13204 LogFlowThisFunc(("\n"));
13205
13206#ifdef VBOX_WITH_USB
13207 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13208 AssertReturn(service, E_FAIL);
13209 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13210#else
13211 NOREF(aId);
13212 NOREF(aDone);
13213 return E_NOTIMPL;
13214#endif
13215}
13216
13217/**
13218 * Inserts all machine filters to the USB proxy service and then calls
13219 * Host::autoCaptureUSBDevices().
13220 *
13221 * Called by Console from the VM process upon VM startup.
13222 *
13223 * @note Locks what called methods lock.
13224 */
13225HRESULT SessionMachine::autoCaptureUSBDevices()
13226{
13227 LogFlowThisFunc(("\n"));
13228
13229#ifdef VBOX_WITH_USB
13230 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13231 AssertComRC(rc);
13232 NOREF(rc);
13233
13234 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13235 AssertReturn(service, E_FAIL);
13236 return service->autoCaptureDevicesForVM(this);
13237#else
13238 return S_OK;
13239#endif
13240}
13241
13242/**
13243 * Removes all machine filters from the USB proxy service and then calls
13244 * Host::detachAllUSBDevices().
13245 *
13246 * Called by Console from the VM process upon normal VM termination or by
13247 * SessionMachine::uninit() upon abnormal VM termination (from under the
13248 * Machine/SessionMachine lock).
13249 *
13250 * @note Locks what called methods lock.
13251 */
13252HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13253{
13254 LogFlowThisFunc(("\n"));
13255
13256#ifdef VBOX_WITH_USB
13257 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13258 AssertComRC(rc);
13259 NOREF(rc);
13260
13261 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13262 AssertReturn(service, E_FAIL);
13263 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13264#else
13265 NOREF(aDone);
13266 return S_OK;
13267#endif
13268}
13269
13270/**
13271 * @note Locks this object for writing.
13272 */
13273HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13274 ComPtr<IProgress> &aProgress)
13275{
13276 LogFlowThisFuncEnter();
13277
13278 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13279 /*
13280 * We don't assert below because it might happen that a non-direct session
13281 * informs us it is closed right after we've been uninitialized -- it's ok.
13282 */
13283
13284 /* get IInternalSessionControl interface */
13285 ComPtr<IInternalSessionControl> control(aSession);
13286
13287 ComAssertRet(!control.isNull(), E_INVALIDARG);
13288
13289 /* Creating a Progress object requires the VirtualBox lock, and
13290 * thus locking it here is required by the lock order rules. */
13291 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13292
13293 if (control == mData->mSession.mDirectControl)
13294 {
13295 /* The direct session is being normally closed by the client process
13296 * ----------------------------------------------------------------- */
13297
13298 /* go to the closing state (essential for all open*Session() calls and
13299 * for #i_checkForDeath()) */
13300 Assert(mData->mSession.mState == SessionState_Locked);
13301 mData->mSession.mState = SessionState_Unlocking;
13302
13303 /* set direct control to NULL to release the remote instance */
13304 mData->mSession.mDirectControl.setNull();
13305 LogFlowThisFunc(("Direct control is set to NULL\n"));
13306
13307 if (mData->mSession.mProgress)
13308 {
13309 /* finalize the progress, someone might wait if a frontend
13310 * closes the session before powering on the VM. */
13311 mData->mSession.mProgress->notifyComplete(E_FAIL,
13312 COM_IIDOF(ISession),
13313 getComponentName(),
13314 tr("The VM session was closed before any attempt to power it on"));
13315 mData->mSession.mProgress.setNull();
13316 }
13317
13318 /* Create the progress object the client will use to wait until
13319 * #i_checkForDeath() is called to uninitialize this session object after
13320 * it releases the IPC semaphore.
13321 * Note! Because we're "reusing" mProgress here, this must be a proxy
13322 * object just like for LaunchVMProcess. */
13323 Assert(mData->mSession.mProgress.isNull());
13324 ComObjPtr<ProgressProxy> progress;
13325 progress.createObject();
13326 ComPtr<IUnknown> pPeer(mPeer);
13327 progress->init(mParent, pPeer,
13328 Bstr(tr("Closing session")).raw(),
13329 FALSE /* aCancelable */);
13330 progress.queryInterfaceTo(aProgress.asOutParam());
13331 mData->mSession.mProgress = progress;
13332 }
13333 else
13334 {
13335 /* the remote session is being normally closed */
13336 bool found = false;
13337 for (Data::Session::RemoteControlList::iterator
13338 it = mData->mSession.mRemoteControls.begin();
13339 it != mData->mSession.mRemoteControls.end();
13340 ++it)
13341 {
13342 if (control == *it)
13343 {
13344 found = true;
13345 // This MUST be erase(it), not remove(*it) as the latter
13346 // triggers a very nasty use after free due to the place where
13347 // the value "lives".
13348 mData->mSession.mRemoteControls.erase(it);
13349 break;
13350 }
13351 }
13352 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13353 E_INVALIDARG);
13354 }
13355
13356 /* signal the client watcher thread, because the client is going away */
13357 mParent->i_updateClientWatcher();
13358
13359 LogFlowThisFuncLeave();
13360 return S_OK;
13361}
13362
13363HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13364{
13365#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13366 ULONG uID;
13367 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13368 if (RT_SUCCESS(rc))
13369 {
13370 if (aID)
13371 *aID = uID;
13372 return S_OK;
13373 }
13374 return E_FAIL;
13375#else
13376 RT_NOREF(aParms, aID);
13377 ReturnComNotImplemented();
13378#endif
13379}
13380
13381HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13382{
13383#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13384 return mParent->i_onClipboardAreaUnregister(aID);
13385#else
13386 RT_NOREF(aID);
13387 ReturnComNotImplemented();
13388#endif
13389}
13390
13391HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13392{
13393#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13394 return mParent->i_onClipboardAreaAttach(aID);
13395#else
13396 RT_NOREF(aID);
13397 ReturnComNotImplemented();
13398#endif
13399}
13400HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13401{
13402#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13403 return mParent->i_onClipboardAreaDetach(aID);
13404#else
13405 RT_NOREF(aID);
13406 ReturnComNotImplemented();
13407#endif
13408}
13409
13410HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13411{
13412#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13413 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13414 if (aID)
13415 *aID = uID;
13416 return S_OK;
13417#else
13418 RT_NOREF(aID);
13419 ReturnComNotImplemented();
13420#endif
13421}
13422
13423HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13424{
13425#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13426 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13427 if (aRefCount)
13428 *aRefCount = uRefCount;
13429 return S_OK;
13430#else
13431 RT_NOREF(aID, aRefCount);
13432 ReturnComNotImplemented();
13433#endif
13434}
13435
13436HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13437 std::vector<com::Utf8Str> &aValues,
13438 std::vector<LONG64> &aTimestamps,
13439 std::vector<com::Utf8Str> &aFlags)
13440{
13441 LogFlowThisFunc(("\n"));
13442
13443#ifdef VBOX_WITH_GUEST_PROPS
13444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13445
13446 size_t cEntries = mHWData->mGuestProperties.size();
13447 aNames.resize(cEntries);
13448 aValues.resize(cEntries);
13449 aTimestamps.resize(cEntries);
13450 aFlags.resize(cEntries);
13451
13452 size_t i = 0;
13453 for (HWData::GuestPropertyMap::const_iterator
13454 it = mHWData->mGuestProperties.begin();
13455 it != mHWData->mGuestProperties.end();
13456 ++it, ++i)
13457 {
13458 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13459 aNames[i] = it->first;
13460 aValues[i] = it->second.strValue;
13461 aTimestamps[i] = it->second.mTimestamp;
13462
13463 /* If it is NULL, keep it NULL. */
13464 if (it->second.mFlags)
13465 {
13466 GuestPropWriteFlags(it->second.mFlags, szFlags);
13467 aFlags[i] = szFlags;
13468 }
13469 else
13470 aFlags[i] = "";
13471 }
13472 return S_OK;
13473#else
13474 ReturnComNotImplemented();
13475#endif
13476}
13477
13478HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13479 const com::Utf8Str &aValue,
13480 LONG64 aTimestamp,
13481 const com::Utf8Str &aFlags)
13482{
13483 LogFlowThisFunc(("\n"));
13484
13485#ifdef VBOX_WITH_GUEST_PROPS
13486 try
13487 {
13488 /*
13489 * Convert input up front.
13490 */
13491 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13492 if (aFlags.length())
13493 {
13494 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13495 AssertRCReturn(vrc, E_INVALIDARG);
13496 }
13497
13498 /*
13499 * Now grab the object lock, validate the state and do the update.
13500 */
13501
13502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13503
13504 if (!Global::IsOnline(mData->mMachineState))
13505 {
13506 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13507 VBOX_E_INVALID_VM_STATE);
13508 }
13509
13510 i_setModified(IsModified_MachineData);
13511 mHWData.backup();
13512
13513 bool fDelete = !aValue.length();
13514 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13515 if (it != mHWData->mGuestProperties.end())
13516 {
13517 if (!fDelete)
13518 {
13519 it->second.strValue = aValue;
13520 it->second.mTimestamp = aTimestamp;
13521 it->second.mFlags = fFlags;
13522 }
13523 else
13524 mHWData->mGuestProperties.erase(it);
13525
13526 mData->mGuestPropertiesModified = TRUE;
13527 }
13528 else if (!fDelete)
13529 {
13530 HWData::GuestProperty prop;
13531 prop.strValue = aValue;
13532 prop.mTimestamp = aTimestamp;
13533 prop.mFlags = fFlags;
13534
13535 mHWData->mGuestProperties[aName] = prop;
13536 mData->mGuestPropertiesModified = TRUE;
13537 }
13538
13539 alock.release();
13540
13541 mParent->i_onGuestPropertyChange(mData->mUuid,
13542 Bstr(aName).raw(),
13543 Bstr(aValue).raw(),
13544 Bstr(aFlags).raw());
13545 }
13546 catch (...)
13547 {
13548 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13549 }
13550 return S_OK;
13551#else
13552 ReturnComNotImplemented();
13553#endif
13554}
13555
13556
13557HRESULT SessionMachine::lockMedia()
13558{
13559 AutoMultiWriteLock2 alock(this->lockHandle(),
13560 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13561
13562 AssertReturn( mData->mMachineState == MachineState_Starting
13563 || mData->mMachineState == MachineState_Restoring
13564 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13565
13566 clearError();
13567 alock.release();
13568 return i_lockMedia();
13569}
13570
13571HRESULT SessionMachine::unlockMedia()
13572{
13573 HRESULT hrc = i_unlockMedia();
13574 return hrc;
13575}
13576
13577HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13578 ComPtr<IMediumAttachment> &aNewAttachment)
13579{
13580 // request the host lock first, since might be calling Host methods for getting host drives;
13581 // next, protect the media tree all the while we're in here, as well as our member variables
13582 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13583 this->lockHandle(),
13584 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13585
13586 IMediumAttachment *iAttach = aAttachment;
13587 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13588
13589 Utf8Str ctrlName;
13590 LONG lPort;
13591 LONG lDevice;
13592 bool fTempEject;
13593 {
13594 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13595
13596 /* Need to query the details first, as the IMediumAttachment reference
13597 * might be to the original settings, which we are going to change. */
13598 ctrlName = pAttach->i_getControllerName();
13599 lPort = pAttach->i_getPort();
13600 lDevice = pAttach->i_getDevice();
13601 fTempEject = pAttach->i_getTempEject();
13602 }
13603
13604 if (!fTempEject)
13605 {
13606 /* Remember previously mounted medium. The medium before taking the
13607 * backup is not necessarily the same thing. */
13608 ComObjPtr<Medium> oldmedium;
13609 oldmedium = pAttach->i_getMedium();
13610
13611 i_setModified(IsModified_Storage);
13612 mMediumAttachments.backup();
13613
13614 // The backup operation makes the pAttach reference point to the
13615 // old settings. Re-get the correct reference.
13616 pAttach = i_findAttachment(*mMediumAttachments.data(),
13617 ctrlName,
13618 lPort,
13619 lDevice);
13620
13621 {
13622 AutoCaller autoAttachCaller(this);
13623 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13624
13625 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13626 if (!oldmedium.isNull())
13627 oldmedium->i_removeBackReference(mData->mUuid);
13628
13629 pAttach->i_updateMedium(NULL);
13630 pAttach->i_updateEjected();
13631 }
13632
13633 i_setModified(IsModified_Storage);
13634 }
13635 else
13636 {
13637 {
13638 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13639 pAttach->i_updateEjected();
13640 }
13641 }
13642
13643 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13644
13645 return S_OK;
13646}
13647
13648HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13649 com::Utf8Str &aResult)
13650{
13651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13652
13653 HRESULT hr = S_OK;
13654
13655 if (!mAuthLibCtx.hAuthLibrary)
13656 {
13657 /* Load the external authentication library. */
13658 Bstr authLibrary;
13659 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13660
13661 Utf8Str filename = authLibrary;
13662
13663 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13664 if (RT_FAILURE(vrc))
13665 hr = setErrorBoth(E_FAIL, vrc,
13666 tr("Could not load the external authentication library '%s' (%Rrc)"),
13667 filename.c_str(), vrc);
13668 }
13669
13670 /* The auth library might need the machine lock. */
13671 alock.release();
13672
13673 if (FAILED(hr))
13674 return hr;
13675
13676 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13677 {
13678 enum VRDEAuthParams
13679 {
13680 parmUuid = 1,
13681 parmGuestJudgement,
13682 parmUser,
13683 parmPassword,
13684 parmDomain,
13685 parmClientId
13686 };
13687
13688 AuthResult result = AuthResultAccessDenied;
13689
13690 Guid uuid(aAuthParams[parmUuid]);
13691 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13692 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13693
13694 result = AuthLibAuthenticate(&mAuthLibCtx,
13695 uuid.raw(), guestJudgement,
13696 aAuthParams[parmUser].c_str(),
13697 aAuthParams[parmPassword].c_str(),
13698 aAuthParams[parmDomain].c_str(),
13699 u32ClientId);
13700
13701 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13702 size_t cbPassword = aAuthParams[parmPassword].length();
13703 if (cbPassword)
13704 {
13705 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13706 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13707 }
13708
13709 if (result == AuthResultAccessGranted)
13710 aResult = "granted";
13711 else
13712 aResult = "denied";
13713
13714 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13715 aAuthParams[parmUser].c_str(), aResult.c_str()));
13716 }
13717 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13718 {
13719 enum VRDEAuthDisconnectParams
13720 {
13721 parmUuid = 1,
13722 parmClientId
13723 };
13724
13725 Guid uuid(aAuthParams[parmUuid]);
13726 uint32_t u32ClientId = 0;
13727 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13728 }
13729 else
13730 {
13731 hr = E_INVALIDARG;
13732 }
13733
13734 return hr;
13735}
13736
13737// public methods only for internal purposes
13738/////////////////////////////////////////////////////////////////////////////
13739
13740#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13741/**
13742 * Called from the client watcher thread to check for expected or unexpected
13743 * death of the client process that has a direct session to this machine.
13744 *
13745 * On Win32 and on OS/2, this method is called only when we've got the
13746 * mutex (i.e. the client has either died or terminated normally) so it always
13747 * returns @c true (the client is terminated, the session machine is
13748 * uninitialized).
13749 *
13750 * On other platforms, the method returns @c true if the client process has
13751 * terminated normally or abnormally and the session machine was uninitialized,
13752 * and @c false if the client process is still alive.
13753 *
13754 * @note Locks this object for writing.
13755 */
13756bool SessionMachine::i_checkForDeath()
13757{
13758 Uninit::Reason reason;
13759 bool terminated = false;
13760
13761 /* Enclose autoCaller with a block because calling uninit() from under it
13762 * will deadlock. */
13763 {
13764 AutoCaller autoCaller(this);
13765 if (!autoCaller.isOk())
13766 {
13767 /* return true if not ready, to cause the client watcher to exclude
13768 * the corresponding session from watching */
13769 LogFlowThisFunc(("Already uninitialized!\n"));
13770 return true;
13771 }
13772
13773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13774
13775 /* Determine the reason of death: if the session state is Closing here,
13776 * everything is fine. Otherwise it means that the client did not call
13777 * OnSessionEnd() before it released the IPC semaphore. This may happen
13778 * either because the client process has abnormally terminated, or
13779 * because it simply forgot to call ISession::Close() before exiting. We
13780 * threat the latter also as an abnormal termination (see
13781 * Session::uninit() for details). */
13782 reason = mData->mSession.mState == SessionState_Unlocking ?
13783 Uninit::Normal :
13784 Uninit::Abnormal;
13785
13786 if (mClientToken)
13787 terminated = mClientToken->release();
13788 } /* AutoCaller block */
13789
13790 if (terminated)
13791 uninit(reason);
13792
13793 return terminated;
13794}
13795
13796void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13797{
13798 LogFlowThisFunc(("\n"));
13799
13800 strTokenId.setNull();
13801
13802 AutoCaller autoCaller(this);
13803 AssertComRCReturnVoid(autoCaller.rc());
13804
13805 Assert(mClientToken);
13806 if (mClientToken)
13807 mClientToken->getId(strTokenId);
13808}
13809#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13810IToken *SessionMachine::i_getToken()
13811{
13812 LogFlowThisFunc(("\n"));
13813
13814 AutoCaller autoCaller(this);
13815 AssertComRCReturn(autoCaller.rc(), NULL);
13816
13817 Assert(mClientToken);
13818 if (mClientToken)
13819 return mClientToken->getToken();
13820 else
13821 return NULL;
13822}
13823#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13824
13825Machine::ClientToken *SessionMachine::i_getClientToken()
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829 AutoCaller autoCaller(this);
13830 AssertComRCReturn(autoCaller.rc(), NULL);
13831
13832 return mClientToken;
13833}
13834
13835
13836/**
13837 * @note Locks this object for reading.
13838 */
13839HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13840{
13841 LogFlowThisFunc(("\n"));
13842
13843 AutoCaller autoCaller(this);
13844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13845
13846 ComPtr<IInternalSessionControl> directControl;
13847 {
13848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13849 if (mData->mSession.mLockType == LockType_VM)
13850 directControl = mData->mSession.mDirectControl;
13851 }
13852
13853 /* ignore notifications sent after #OnSessionEnd() is called */
13854 if (!directControl)
13855 return S_OK;
13856
13857 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13858}
13859
13860/**
13861 * @note Locks this object for reading.
13862 */
13863HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13864 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13865 IN_BSTR aGuestIp, LONG aGuestPort)
13866{
13867 LogFlowThisFunc(("\n"));
13868
13869 AutoCaller autoCaller(this);
13870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13871
13872 ComPtr<IInternalSessionControl> directControl;
13873 {
13874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13875 if (mData->mSession.mLockType == LockType_VM)
13876 directControl = mData->mSession.mDirectControl;
13877 }
13878
13879 /* ignore notifications sent after #OnSessionEnd() is called */
13880 if (!directControl)
13881 return S_OK;
13882 /*
13883 * instead acting like callback we ask IVirtualBox deliver corresponding event
13884 */
13885
13886 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13887 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13888 return S_OK;
13889}
13890
13891/**
13892 * @note Locks this object for reading.
13893 */
13894HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13895{
13896 LogFlowThisFunc(("\n"));
13897
13898 AutoCaller autoCaller(this);
13899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13900
13901 ComPtr<IInternalSessionControl> directControl;
13902 {
13903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13904 if (mData->mSession.mLockType == LockType_VM)
13905 directControl = mData->mSession.mDirectControl;
13906 }
13907
13908 /* ignore notifications sent after #OnSessionEnd() is called */
13909 if (!directControl)
13910 return S_OK;
13911
13912 return directControl->OnAudioAdapterChange(audioAdapter);
13913}
13914
13915/**
13916 * @note Locks this object for reading.
13917 */
13918HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13919{
13920 LogFlowThisFunc(("\n"));
13921
13922 AutoCaller autoCaller(this);
13923 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13924
13925 ComPtr<IInternalSessionControl> directControl;
13926 {
13927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13928 if (mData->mSession.mLockType == LockType_VM)
13929 directControl = mData->mSession.mDirectControl;
13930 }
13931
13932 /* ignore notifications sent after #OnSessionEnd() is called */
13933 if (!directControl)
13934 return S_OK;
13935
13936 return directControl->OnSerialPortChange(serialPort);
13937}
13938
13939/**
13940 * @note Locks this object for reading.
13941 */
13942HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13943{
13944 LogFlowThisFunc(("\n"));
13945
13946 AutoCaller autoCaller(this);
13947 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13948
13949 ComPtr<IInternalSessionControl> directControl;
13950 {
13951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13952 if (mData->mSession.mLockType == LockType_VM)
13953 directControl = mData->mSession.mDirectControl;
13954 }
13955
13956 /* ignore notifications sent after #OnSessionEnd() is called */
13957 if (!directControl)
13958 return S_OK;
13959
13960 return directControl->OnParallelPortChange(parallelPort);
13961}
13962
13963/**
13964 * @note Locks this object for reading.
13965 */
13966HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13967{
13968 LogFlowThisFunc(("\n"));
13969
13970 AutoCaller autoCaller(this);
13971 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13972
13973 ComPtr<IInternalSessionControl> directControl;
13974 {
13975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13976 if (mData->mSession.mLockType == LockType_VM)
13977 directControl = mData->mSession.mDirectControl;
13978 }
13979
13980 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
13981
13982 /* ignore notifications sent after #OnSessionEnd() is called */
13983 if (!directControl)
13984 return S_OK;
13985
13986 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
13987}
13988
13989/**
13990 * @note Locks this object for reading.
13991 */
13992HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13993{
13994 LogFlowThisFunc(("\n"));
13995
13996 AutoCaller autoCaller(this);
13997 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13998
13999 ComPtr<IInternalSessionControl> directControl;
14000 {
14001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14002 if (mData->mSession.mLockType == LockType_VM)
14003 directControl = mData->mSession.mDirectControl;
14004 }
14005
14006 mParent->i_onMediumChanged(aAttachment);
14007
14008 /* ignore notifications sent after #OnSessionEnd() is called */
14009 if (!directControl)
14010 return S_OK;
14011
14012 return directControl->OnMediumChange(aAttachment, aForce);
14013}
14014
14015HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14016{
14017 LogFlowThisFunc(("\n"));
14018
14019 AutoCaller autoCaller(this);
14020 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14021
14022 ComPtr<IInternalSessionControl> directControl;
14023 {
14024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14025 if (mData->mSession.mLockType == LockType_VM)
14026 directControl = mData->mSession.mDirectControl;
14027 }
14028
14029 /* ignore notifications sent after #OnSessionEnd() is called */
14030 if (!directControl)
14031 return S_OK;
14032
14033 return directControl->OnVMProcessPriorityChange(aPriority);
14034}
14035
14036/**
14037 * @note Locks this object for reading.
14038 */
14039HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14040{
14041 LogFlowThisFunc(("\n"));
14042
14043 AutoCaller autoCaller(this);
14044 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14045
14046 ComPtr<IInternalSessionControl> directControl;
14047 {
14048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14049 if (mData->mSession.mLockType == LockType_VM)
14050 directControl = mData->mSession.mDirectControl;
14051 }
14052
14053 /* ignore notifications sent after #OnSessionEnd() is called */
14054 if (!directControl)
14055 return S_OK;
14056
14057 return directControl->OnCPUChange(aCPU, aRemove);
14058}
14059
14060HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14061{
14062 LogFlowThisFunc(("\n"));
14063
14064 AutoCaller autoCaller(this);
14065 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14066
14067 ComPtr<IInternalSessionControl> directControl;
14068 {
14069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14070 if (mData->mSession.mLockType == LockType_VM)
14071 directControl = mData->mSession.mDirectControl;
14072 }
14073
14074 /* ignore notifications sent after #OnSessionEnd() is called */
14075 if (!directControl)
14076 return S_OK;
14077
14078 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14079}
14080
14081/**
14082 * @note Locks this object for reading.
14083 */
14084HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14085{
14086 LogFlowThisFunc(("\n"));
14087
14088 AutoCaller autoCaller(this);
14089 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14090
14091 ComPtr<IInternalSessionControl> directControl;
14092 {
14093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14094 if (mData->mSession.mLockType == LockType_VM)
14095 directControl = mData->mSession.mDirectControl;
14096 }
14097
14098 /* ignore notifications sent after #OnSessionEnd() is called */
14099 if (!directControl)
14100 return S_OK;
14101
14102 return directControl->OnVRDEServerChange(aRestart);
14103}
14104
14105/**
14106 * @note Locks this object for reading.
14107 */
14108HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14109{
14110 LogFlowThisFunc(("\n"));
14111
14112 AutoCaller autoCaller(this);
14113 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14114
14115 ComPtr<IInternalSessionControl> directControl;
14116 {
14117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14118 if (mData->mSession.mLockType == LockType_VM)
14119 directControl = mData->mSession.mDirectControl;
14120 }
14121
14122 /* ignore notifications sent after #OnSessionEnd() is called */
14123 if (!directControl)
14124 return S_OK;
14125
14126 return directControl->OnRecordingChange(aEnable);
14127}
14128
14129/**
14130 * @note Locks this object for reading.
14131 */
14132HRESULT SessionMachine::i_onUSBControllerChange()
14133{
14134 LogFlowThisFunc(("\n"));
14135
14136 AutoCaller autoCaller(this);
14137 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14138
14139 ComPtr<IInternalSessionControl> directControl;
14140 {
14141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14142 if (mData->mSession.mLockType == LockType_VM)
14143 directControl = mData->mSession.mDirectControl;
14144 }
14145
14146 /* ignore notifications sent after #OnSessionEnd() is called */
14147 if (!directControl)
14148 return S_OK;
14149
14150 return directControl->OnUSBControllerChange();
14151}
14152
14153/**
14154 * @note Locks this object for reading.
14155 */
14156HRESULT SessionMachine::i_onSharedFolderChange()
14157{
14158 LogFlowThisFunc(("\n"));
14159
14160 AutoCaller autoCaller(this);
14161 AssertComRCReturnRC(autoCaller.rc());
14162
14163 ComPtr<IInternalSessionControl> directControl;
14164 {
14165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14166 if (mData->mSession.mLockType == LockType_VM)
14167 directControl = mData->mSession.mDirectControl;
14168 }
14169
14170 /* ignore notifications sent after #OnSessionEnd() is called */
14171 if (!directControl)
14172 return S_OK;
14173
14174 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14175}
14176
14177/**
14178 * @note Locks this object for reading.
14179 */
14180HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14181{
14182 LogFlowThisFunc(("\n"));
14183
14184 AutoCaller autoCaller(this);
14185 AssertComRCReturnRC(autoCaller.rc());
14186
14187 ComPtr<IInternalSessionControl> directControl;
14188 {
14189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14190 if (mData->mSession.mLockType == LockType_VM)
14191 directControl = mData->mSession.mDirectControl;
14192 }
14193
14194 /* ignore notifications sent after #OnSessionEnd() is called */
14195 if (!directControl)
14196 return S_OK;
14197
14198 return directControl->OnClipboardModeChange(aClipboardMode);
14199}
14200
14201/**
14202 * @note Locks this object for reading.
14203 */
14204HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14205{
14206 LogFlowThisFunc(("\n"));
14207
14208 AutoCaller autoCaller(this);
14209 AssertComRCReturnRC(autoCaller.rc());
14210
14211 ComPtr<IInternalSessionControl> directControl;
14212 {
14213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14214 if (mData->mSession.mLockType == LockType_VM)
14215 directControl = mData->mSession.mDirectControl;
14216 }
14217
14218 /* ignore notifications sent after #OnSessionEnd() is called */
14219 if (!directControl)
14220 return S_OK;
14221
14222 return directControl->OnClipboardFileTransferModeChange(aEnable);
14223}
14224
14225/**
14226 * @note Locks this object for reading.
14227 */
14228HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14229{
14230 LogFlowThisFunc(("\n"));
14231
14232 AutoCaller autoCaller(this);
14233 AssertComRCReturnRC(autoCaller.rc());
14234
14235 ComPtr<IInternalSessionControl> directControl;
14236 {
14237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14238 if (mData->mSession.mLockType == LockType_VM)
14239 directControl = mData->mSession.mDirectControl;
14240 }
14241
14242 /* ignore notifications sent after #OnSessionEnd() is called */
14243 if (!directControl)
14244 return S_OK;
14245
14246 return directControl->OnDnDModeChange(aDnDMode);
14247}
14248
14249/**
14250 * @note Locks this object for reading.
14251 */
14252HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14253{
14254 LogFlowThisFunc(("\n"));
14255
14256 AutoCaller autoCaller(this);
14257 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14258
14259 ComPtr<IInternalSessionControl> directControl;
14260 {
14261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14262 if (mData->mSession.mLockType == LockType_VM)
14263 directControl = mData->mSession.mDirectControl;
14264 }
14265
14266 /* ignore notifications sent after #OnSessionEnd() is called */
14267 if (!directControl)
14268 return S_OK;
14269
14270 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14271}
14272
14273/**
14274 * @note Locks this object for reading.
14275 */
14276HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14277{
14278 LogFlowThisFunc(("\n"));
14279
14280 AutoCaller autoCaller(this);
14281 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14282
14283 ComPtr<IInternalSessionControl> directControl;
14284 {
14285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14286 if (mData->mSession.mLockType == LockType_VM)
14287 directControl = mData->mSession.mDirectControl;
14288 }
14289
14290 /* ignore notifications sent after #OnSessionEnd() is called */
14291 if (!directControl)
14292 return S_OK;
14293
14294 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14295}
14296
14297/**
14298 * Returns @c true if this machine's USB controller reports it has a matching
14299 * filter for the given USB device and @c false otherwise.
14300 *
14301 * @note locks this object for reading.
14302 */
14303bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14304{
14305 AutoCaller autoCaller(this);
14306 /* silently return if not ready -- this method may be called after the
14307 * direct machine session has been called */
14308 if (!autoCaller.isOk())
14309 return false;
14310
14311#ifdef VBOX_WITH_USB
14312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14313
14314 switch (mData->mMachineState)
14315 {
14316 case MachineState_Starting:
14317 case MachineState_Restoring:
14318 case MachineState_TeleportingIn:
14319 case MachineState_Paused:
14320 case MachineState_Running:
14321 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14322 * elsewhere... */
14323 alock.release();
14324 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14325 default: break;
14326 }
14327#else
14328 NOREF(aDevice);
14329 NOREF(aMaskedIfs);
14330#endif
14331 return false;
14332}
14333
14334/**
14335 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14336 */
14337HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14338 IVirtualBoxErrorInfo *aError,
14339 ULONG aMaskedIfs,
14340 const com::Utf8Str &aCaptureFilename)
14341{
14342 LogFlowThisFunc(("\n"));
14343
14344 AutoCaller autoCaller(this);
14345
14346 /* This notification may happen after the machine object has been
14347 * uninitialized (the session was closed), so don't assert. */
14348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14349
14350 ComPtr<IInternalSessionControl> directControl;
14351 {
14352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14353 if (mData->mSession.mLockType == LockType_VM)
14354 directControl = mData->mSession.mDirectControl;
14355 }
14356
14357 /* fail on notifications sent after #OnSessionEnd() is called, it is
14358 * expected by the caller */
14359 if (!directControl)
14360 return E_FAIL;
14361
14362 /* No locks should be held at this point. */
14363 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14364 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14365
14366 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14367}
14368
14369/**
14370 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14371 */
14372HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14373 IVirtualBoxErrorInfo *aError)
14374{
14375 LogFlowThisFunc(("\n"));
14376
14377 AutoCaller autoCaller(this);
14378
14379 /* This notification may happen after the machine object has been
14380 * uninitialized (the session was closed), so don't assert. */
14381 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14382
14383 ComPtr<IInternalSessionControl> directControl;
14384 {
14385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14386 if (mData->mSession.mLockType == LockType_VM)
14387 directControl = mData->mSession.mDirectControl;
14388 }
14389
14390 /* fail on notifications sent after #OnSessionEnd() is called, it is
14391 * expected by the caller */
14392 if (!directControl)
14393 return E_FAIL;
14394
14395 /* No locks should be held at this point. */
14396 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14397 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14398
14399 return directControl->OnUSBDeviceDetach(aId, aError);
14400}
14401
14402// protected methods
14403/////////////////////////////////////////////////////////////////////////////
14404
14405/**
14406 * Deletes the given file if it is no longer in use by either the current machine state
14407 * (if the machine is "saved") or any of the machine's snapshots.
14408 *
14409 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14410 * but is different for each SnapshotMachine. When calling this, the order of calling this
14411 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14412 * is therefore critical. I know, it's all rather messy.
14413 *
14414 * @param strStateFile
14415 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14416 * the test for whether the saved state file is in use.
14417 */
14418void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14419 Snapshot *pSnapshotToIgnore)
14420{
14421 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14422 if ( (strStateFile.isNotEmpty())
14423 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14424 )
14425 // ... and it must also not be shared with other snapshots
14426 if ( !mData->mFirstSnapshot
14427 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14428 // this checks the SnapshotMachine's state file paths
14429 )
14430 RTFileDelete(strStateFile.c_str());
14431}
14432
14433/**
14434 * Locks the attached media.
14435 *
14436 * All attached hard disks are locked for writing and DVD/floppy are locked for
14437 * reading. Parents of attached hard disks (if any) are locked for reading.
14438 *
14439 * This method also performs accessibility check of all media it locks: if some
14440 * media is inaccessible, the method will return a failure and a bunch of
14441 * extended error info objects per each inaccessible medium.
14442 *
14443 * Note that this method is atomic: if it returns a success, all media are
14444 * locked as described above; on failure no media is locked at all (all
14445 * succeeded individual locks will be undone).
14446 *
14447 * The caller is responsible for doing the necessary state sanity checks.
14448 *
14449 * The locks made by this method must be undone by calling #unlockMedia() when
14450 * no more needed.
14451 */
14452HRESULT SessionMachine::i_lockMedia()
14453{
14454 AutoCaller autoCaller(this);
14455 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14456
14457 AutoMultiWriteLock2 alock(this->lockHandle(),
14458 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14459
14460 /* bail out if trying to lock things with already set up locking */
14461 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14462
14463 MultiResult mrc(S_OK);
14464
14465 /* Collect locking information for all medium objects attached to the VM. */
14466 for (MediumAttachmentList::const_iterator
14467 it = mMediumAttachments->begin();
14468 it != mMediumAttachments->end();
14469 ++it)
14470 {
14471 MediumAttachment *pAtt = *it;
14472 DeviceType_T devType = pAtt->i_getType();
14473 Medium *pMedium = pAtt->i_getMedium();
14474
14475 MediumLockList *pMediumLockList(new MediumLockList());
14476 // There can be attachments without a medium (floppy/dvd), and thus
14477 // it's impossible to create a medium lock list. It still makes sense
14478 // to have the empty medium lock list in the map in case a medium is
14479 // attached later.
14480 if (pMedium != NULL)
14481 {
14482 MediumType_T mediumType = pMedium->i_getType();
14483 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14484 || mediumType == MediumType_Shareable;
14485 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14486
14487 alock.release();
14488 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14489 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14490 false /* fMediumLockWriteAll */,
14491 NULL,
14492 *pMediumLockList);
14493 alock.acquire();
14494 if (FAILED(mrc))
14495 {
14496 delete pMediumLockList;
14497 mData->mSession.mLockedMedia.Clear();
14498 break;
14499 }
14500 }
14501
14502 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14503 if (FAILED(rc))
14504 {
14505 mData->mSession.mLockedMedia.Clear();
14506 mrc = setError(rc,
14507 tr("Collecting locking information for all attached media failed"));
14508 break;
14509 }
14510 }
14511
14512 if (SUCCEEDED(mrc))
14513 {
14514 /* Now lock all media. If this fails, nothing is locked. */
14515 alock.release();
14516 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14517 alock.acquire();
14518 if (FAILED(rc))
14519 {
14520 mrc = setError(rc,
14521 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14522 }
14523 }
14524
14525 return mrc;
14526}
14527
14528/**
14529 * Undoes the locks made by by #lockMedia().
14530 */
14531HRESULT SessionMachine::i_unlockMedia()
14532{
14533 AutoCaller autoCaller(this);
14534 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14535
14536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14537
14538 /* we may be holding important error info on the current thread;
14539 * preserve it */
14540 ErrorInfoKeeper eik;
14541
14542 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14543 AssertComRC(rc);
14544 return rc;
14545}
14546
14547/**
14548 * Helper to change the machine state (reimplementation).
14549 *
14550 * @note Locks this object for writing.
14551 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14552 * it can cause crashes in random places due to unexpectedly committing
14553 * the current settings. The caller is responsible for that. The call
14554 * to saveStateSettings is fine, because this method does not commit.
14555 */
14556HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14557{
14558 LogFlowThisFuncEnter();
14559 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14560
14561 AutoCaller autoCaller(this);
14562 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14563
14564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14565
14566 MachineState_T oldMachineState = mData->mMachineState;
14567
14568 AssertMsgReturn(oldMachineState != aMachineState,
14569 ("oldMachineState=%s, aMachineState=%s\n",
14570 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14571 E_FAIL);
14572
14573 HRESULT rc = S_OK;
14574
14575 int stsFlags = 0;
14576 bool deleteSavedState = false;
14577
14578 /* detect some state transitions */
14579
14580 if ( ( oldMachineState == MachineState_Saved
14581 && aMachineState == MachineState_Restoring)
14582 || ( ( oldMachineState == MachineState_PoweredOff
14583 || oldMachineState == MachineState_Teleported
14584 || oldMachineState == MachineState_Aborted
14585 )
14586 && ( aMachineState == MachineState_TeleportingIn
14587 || aMachineState == MachineState_Starting
14588 )
14589 )
14590 )
14591 {
14592 /* The EMT thread is about to start */
14593
14594 /* Nothing to do here for now... */
14595
14596 /// @todo NEWMEDIA don't let mDVDDrive and other children
14597 /// change anything when in the Starting/Restoring state
14598 }
14599 else if ( ( oldMachineState == MachineState_Running
14600 || oldMachineState == MachineState_Paused
14601 || oldMachineState == MachineState_Teleporting
14602 || oldMachineState == MachineState_OnlineSnapshotting
14603 || oldMachineState == MachineState_LiveSnapshotting
14604 || oldMachineState == MachineState_Stuck
14605 || oldMachineState == MachineState_Starting
14606 || oldMachineState == MachineState_Stopping
14607 || oldMachineState == MachineState_Saving
14608 || oldMachineState == MachineState_Restoring
14609 || oldMachineState == MachineState_TeleportingPausedVM
14610 || oldMachineState == MachineState_TeleportingIn
14611 )
14612 && ( aMachineState == MachineState_PoweredOff
14613 || aMachineState == MachineState_Saved
14614 || aMachineState == MachineState_Teleported
14615 || aMachineState == MachineState_Aborted
14616 )
14617 )
14618 {
14619 /* The EMT thread has just stopped, unlock attached media. Note that as
14620 * opposed to locking that is done from Console, we do unlocking here
14621 * because the VM process may have aborted before having a chance to
14622 * properly unlock all media it locked. */
14623
14624 unlockMedia();
14625 }
14626
14627 if (oldMachineState == MachineState_Restoring)
14628 {
14629 if (aMachineState != MachineState_Saved)
14630 {
14631 /*
14632 * delete the saved state file once the machine has finished
14633 * restoring from it (note that Console sets the state from
14634 * Restoring to Saved if the VM couldn't restore successfully,
14635 * to give the user an ability to fix an error and retry --
14636 * we keep the saved state file in this case)
14637 */
14638 deleteSavedState = true;
14639 }
14640 }
14641 else if ( oldMachineState == MachineState_Saved
14642 && ( aMachineState == MachineState_PoweredOff
14643 || aMachineState == MachineState_Aborted
14644 || aMachineState == MachineState_Teleported
14645 )
14646 )
14647 {
14648 /*
14649 * delete the saved state after SessionMachine::ForgetSavedState() is called
14650 * or if the VM process (owning a direct VM session) crashed while the
14651 * VM was Saved
14652 */
14653
14654 /// @todo (dmik)
14655 // Not sure that deleting the saved state file just because of the
14656 // client death before it attempted to restore the VM is a good
14657 // thing. But when it crashes we need to go to the Aborted state
14658 // which cannot have the saved state file associated... The only
14659 // way to fix this is to make the Aborted condition not a VM state
14660 // but a bool flag: i.e., when a crash occurs, set it to true and
14661 // change the state to PoweredOff or Saved depending on the
14662 // saved state presence.
14663
14664 deleteSavedState = true;
14665 mData->mCurrentStateModified = TRUE;
14666 stsFlags |= SaveSTS_CurStateModified;
14667 }
14668
14669 if ( aMachineState == MachineState_Starting
14670 || aMachineState == MachineState_Restoring
14671 || aMachineState == MachineState_TeleportingIn
14672 )
14673 {
14674 /* set the current state modified flag to indicate that the current
14675 * state is no more identical to the state in the
14676 * current snapshot */
14677 if (!mData->mCurrentSnapshot.isNull())
14678 {
14679 mData->mCurrentStateModified = TRUE;
14680 stsFlags |= SaveSTS_CurStateModified;
14681 }
14682 }
14683
14684 if (deleteSavedState)
14685 {
14686 if (mRemoveSavedState)
14687 {
14688 Assert(!mSSData->strStateFilePath.isEmpty());
14689
14690 // it is safe to delete the saved state file if ...
14691 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14692 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14693 // ... none of the snapshots share the saved state file
14694 )
14695 RTFileDelete(mSSData->strStateFilePath.c_str());
14696 }
14697
14698 mSSData->strStateFilePath.setNull();
14699 stsFlags |= SaveSTS_StateFilePath;
14700 }
14701
14702 /* redirect to the underlying peer machine */
14703 mPeer->i_setMachineState(aMachineState);
14704
14705 if ( oldMachineState != MachineState_RestoringSnapshot
14706 && ( aMachineState == MachineState_PoweredOff
14707 || aMachineState == MachineState_Teleported
14708 || aMachineState == MachineState_Aborted
14709 || aMachineState == MachineState_Saved))
14710 {
14711 /* the machine has stopped execution
14712 * (or the saved state file was adopted) */
14713 stsFlags |= SaveSTS_StateTimeStamp;
14714 }
14715
14716 if ( ( oldMachineState == MachineState_PoweredOff
14717 || oldMachineState == MachineState_Aborted
14718 || oldMachineState == MachineState_Teleported
14719 )
14720 && aMachineState == MachineState_Saved)
14721 {
14722 /* the saved state file was adopted */
14723 Assert(!mSSData->strStateFilePath.isEmpty());
14724 stsFlags |= SaveSTS_StateFilePath;
14725 }
14726
14727#ifdef VBOX_WITH_GUEST_PROPS
14728 if ( aMachineState == MachineState_PoweredOff
14729 || aMachineState == MachineState_Aborted
14730 || aMachineState == MachineState_Teleported)
14731 {
14732 /* Make sure any transient guest properties get removed from the
14733 * property store on shutdown. */
14734 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14735
14736 /* remove it from the settings representation */
14737 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14738 for (settings::GuestPropertiesList::iterator
14739 it = llGuestProperties.begin();
14740 it != llGuestProperties.end();
14741 /*nothing*/)
14742 {
14743 const settings::GuestProperty &prop = *it;
14744 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14745 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14746 {
14747 it = llGuestProperties.erase(it);
14748 fNeedsSaving = true;
14749 }
14750 else
14751 {
14752 ++it;
14753 }
14754 }
14755
14756 /* Additionally remove it from the HWData representation. Required to
14757 * keep everything in sync, as this is what the API keeps using. */
14758 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14759 for (HWData::GuestPropertyMap::iterator
14760 it = llHWGuestProperties.begin();
14761 it != llHWGuestProperties.end();
14762 /*nothing*/)
14763 {
14764 uint32_t fFlags = it->second.mFlags;
14765 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14766 {
14767 /* iterator where we need to continue after the erase call
14768 * (C++03 is a fact still, and it doesn't return the iterator
14769 * which would allow continuing) */
14770 HWData::GuestPropertyMap::iterator it2 = it;
14771 ++it2;
14772 llHWGuestProperties.erase(it);
14773 it = it2;
14774 fNeedsSaving = true;
14775 }
14776 else
14777 {
14778 ++it;
14779 }
14780 }
14781
14782 if (fNeedsSaving)
14783 {
14784 mData->mCurrentStateModified = TRUE;
14785 stsFlags |= SaveSTS_CurStateModified;
14786 }
14787 }
14788#endif /* VBOX_WITH_GUEST_PROPS */
14789
14790 rc = i_saveStateSettings(stsFlags);
14791
14792 if ( ( oldMachineState != MachineState_PoweredOff
14793 && oldMachineState != MachineState_Aborted
14794 && oldMachineState != MachineState_Teleported
14795 )
14796 && ( aMachineState == MachineState_PoweredOff
14797 || aMachineState == MachineState_Aborted
14798 || aMachineState == MachineState_Teleported
14799 )
14800 )
14801 {
14802 /* we've been shut down for any reason */
14803 /* no special action so far */
14804 }
14805
14806 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14807 LogFlowThisFuncLeave();
14808 return rc;
14809}
14810
14811/**
14812 * Sends the current machine state value to the VM process.
14813 *
14814 * @note Locks this object for reading, then calls a client process.
14815 */
14816HRESULT SessionMachine::i_updateMachineStateOnClient()
14817{
14818 AutoCaller autoCaller(this);
14819 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14820
14821 ComPtr<IInternalSessionControl> directControl;
14822 {
14823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14824 AssertReturn(!!mData, E_FAIL);
14825 if (mData->mSession.mLockType == LockType_VM)
14826 directControl = mData->mSession.mDirectControl;
14827
14828 /* directControl may be already set to NULL here in #OnSessionEnd()
14829 * called too early by the direct session process while there is still
14830 * some operation (like deleting the snapshot) in progress. The client
14831 * process in this case is waiting inside Session::close() for the
14832 * "end session" process object to complete, while #uninit() called by
14833 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14834 * operation to complete. For now, we accept this inconsistent behavior
14835 * and simply do nothing here. */
14836
14837 if (mData->mSession.mState == SessionState_Unlocking)
14838 return S_OK;
14839 }
14840
14841 /* ignore notifications sent after #OnSessionEnd() is called */
14842 if (!directControl)
14843 return S_OK;
14844
14845 return directControl->UpdateMachineState(mData->mMachineState);
14846}
14847
14848
14849/*static*/
14850HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14851{
14852 va_list args;
14853 va_start(args, pcszMsg);
14854 HRESULT rc = setErrorInternal(aResultCode,
14855 getStaticClassIID(),
14856 getStaticComponentName(),
14857 Utf8Str(pcszMsg, args),
14858 false /* aWarning */,
14859 true /* aLogIt */);
14860 va_end(args);
14861 return rc;
14862}
14863
14864
14865HRESULT Machine::updateState(MachineState_T aState)
14866{
14867 NOREF(aState);
14868 ReturnComNotImplemented();
14869}
14870
14871HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14872{
14873 NOREF(aProgress);
14874 ReturnComNotImplemented();
14875}
14876
14877HRESULT Machine::endPowerUp(LONG aResult)
14878{
14879 NOREF(aResult);
14880 ReturnComNotImplemented();
14881}
14882
14883HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14884{
14885 NOREF(aProgress);
14886 ReturnComNotImplemented();
14887}
14888
14889HRESULT Machine::endPoweringDown(LONG aResult,
14890 const com::Utf8Str &aErrMsg)
14891{
14892 NOREF(aResult);
14893 NOREF(aErrMsg);
14894 ReturnComNotImplemented();
14895}
14896
14897HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14898 BOOL *aMatched,
14899 ULONG *aMaskedInterfaces)
14900{
14901 NOREF(aDevice);
14902 NOREF(aMatched);
14903 NOREF(aMaskedInterfaces);
14904 ReturnComNotImplemented();
14905
14906}
14907
14908HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14909{
14910 NOREF(aId); NOREF(aCaptureFilename);
14911 ReturnComNotImplemented();
14912}
14913
14914HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14915 BOOL aDone)
14916{
14917 NOREF(aId);
14918 NOREF(aDone);
14919 ReturnComNotImplemented();
14920}
14921
14922HRESULT Machine::autoCaptureUSBDevices()
14923{
14924 ReturnComNotImplemented();
14925}
14926
14927HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14928{
14929 NOREF(aDone);
14930 ReturnComNotImplemented();
14931}
14932
14933HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14934 ComPtr<IProgress> &aProgress)
14935{
14936 NOREF(aSession);
14937 NOREF(aProgress);
14938 ReturnComNotImplemented();
14939}
14940
14941HRESULT Machine::finishOnlineMergeMedium()
14942{
14943 ReturnComNotImplemented();
14944}
14945
14946HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14947{
14948 RT_NOREF(aParms, aID);
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14953{
14954 RT_NOREF(aID);
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::clipboardAreaAttach(ULONG aID)
14959{
14960 RT_NOREF(aID);
14961 ReturnComNotImplemented();
14962}
14963HRESULT Machine::clipboardAreaDetach(ULONG aID)
14964{
14965 RT_NOREF(aID);
14966 ReturnComNotImplemented();
14967}
14968
14969HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
14970{
14971 RT_NOREF(aID);
14972 ReturnComNotImplemented();
14973}
14974
14975HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
14976{
14977 RT_NOREF(aID, aRefCount);
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14982 std::vector<com::Utf8Str> &aValues,
14983 std::vector<LONG64> &aTimestamps,
14984 std::vector<com::Utf8Str> &aFlags)
14985{
14986 NOREF(aNames);
14987 NOREF(aValues);
14988 NOREF(aTimestamps);
14989 NOREF(aFlags);
14990 ReturnComNotImplemented();
14991}
14992
14993HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14994 const com::Utf8Str &aValue,
14995 LONG64 aTimestamp,
14996 const com::Utf8Str &aFlags)
14997{
14998 NOREF(aName);
14999 NOREF(aValue);
15000 NOREF(aTimestamp);
15001 NOREF(aFlags);
15002 ReturnComNotImplemented();
15003}
15004
15005HRESULT Machine::lockMedia()
15006{
15007 ReturnComNotImplemented();
15008}
15009
15010HRESULT Machine::unlockMedia()
15011{
15012 ReturnComNotImplemented();
15013}
15014
15015HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15016 ComPtr<IMediumAttachment> &aNewAttachment)
15017{
15018 NOREF(aAttachment);
15019 NOREF(aNewAttachment);
15020 ReturnComNotImplemented();
15021}
15022
15023HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15024 ULONG aCpuUser,
15025 ULONG aCpuKernel,
15026 ULONG aCpuIdle,
15027 ULONG aMemTotal,
15028 ULONG aMemFree,
15029 ULONG aMemBalloon,
15030 ULONG aMemShared,
15031 ULONG aMemCache,
15032 ULONG aPagedTotal,
15033 ULONG aMemAllocTotal,
15034 ULONG aMemFreeTotal,
15035 ULONG aMemBalloonTotal,
15036 ULONG aMemSharedTotal,
15037 ULONG aVmNetRx,
15038 ULONG aVmNetTx)
15039{
15040 NOREF(aValidStats);
15041 NOREF(aCpuUser);
15042 NOREF(aCpuKernel);
15043 NOREF(aCpuIdle);
15044 NOREF(aMemTotal);
15045 NOREF(aMemFree);
15046 NOREF(aMemBalloon);
15047 NOREF(aMemShared);
15048 NOREF(aMemCache);
15049 NOREF(aPagedTotal);
15050 NOREF(aMemAllocTotal);
15051 NOREF(aMemFreeTotal);
15052 NOREF(aMemBalloonTotal);
15053 NOREF(aMemSharedTotal);
15054 NOREF(aVmNetRx);
15055 NOREF(aVmNetTx);
15056 ReturnComNotImplemented();
15057}
15058
15059HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15060 com::Utf8Str &aResult)
15061{
15062 NOREF(aAuthParams);
15063 NOREF(aResult);
15064 ReturnComNotImplemented();
15065}
15066
15067com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15068{
15069 com::Utf8Str strControllerName = "Unknown";
15070 switch (aBusType)
15071 {
15072 case StorageBus_IDE:
15073 {
15074 strControllerName = "IDE";
15075 break;
15076 }
15077 case StorageBus_SATA:
15078 {
15079 strControllerName = "SATA";
15080 break;
15081 }
15082 case StorageBus_SCSI:
15083 {
15084 strControllerName = "SCSI";
15085 break;
15086 }
15087 case StorageBus_Floppy:
15088 {
15089 strControllerName = "Floppy";
15090 break;
15091 }
15092 case StorageBus_SAS:
15093 {
15094 strControllerName = "SAS";
15095 break;
15096 }
15097 case StorageBus_USB:
15098 {
15099 strControllerName = "USB";
15100 break;
15101 }
15102 default:
15103 break;
15104 }
15105 return strControllerName;
15106}
15107
15108HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15109{
15110 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15111
15112 AutoCaller autoCaller(this);
15113 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15114
15115 HRESULT rc = S_OK;
15116
15117 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15118 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15119 rc = getUSBDeviceFilters(usbDeviceFilters);
15120 if (FAILED(rc)) return rc;
15121
15122 NOREF(aFlags);
15123 com::Utf8Str osTypeId;
15124 ComObjPtr<GuestOSType> osType = NULL;
15125
15126 /* Get the guest os type as a string from the VB. */
15127 rc = getOSTypeId(osTypeId);
15128 if (FAILED(rc)) return rc;
15129
15130 /* Get the os type obj that coresponds, can be used to get
15131 * the defaults for this guest OS. */
15132 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15133 if (FAILED(rc)) return rc;
15134
15135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15136
15137 /* Let the OS type select 64-bit ness. */
15138 mHWData->mLongMode = osType->i_is64Bit()
15139 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15140
15141 /* Let the OS type enable the X2APIC */
15142 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15143
15144 /* This one covers IOAPICEnabled. */
15145 mBIOSSettings->i_applyDefaults(osType);
15146
15147 /* Initialize default record settings. */
15148 mRecordingSettings->i_applyDefaults();
15149
15150 /* Initialize default BIOS settings here */
15151 /* Hardware virtualization must be ON by default */
15152 mHWData->mAPIC = true;
15153 mHWData->mHWVirtExEnabled = true;
15154
15155 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15156 if (FAILED(rc)) return rc;
15157
15158 /* Graphics stuff. */
15159 GraphicsControllerType_T graphicsController;
15160 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15161 if (FAILED(rc)) return rc;
15162
15163 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15164 if (FAILED(rc)) return rc;
15165
15166 ULONG vramSize;
15167 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15168 if (FAILED(rc)) return rc;
15169
15170 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15171 if (FAILED(rc)) return rc;
15172
15173 BOOL fAccelerate2DVideoEnabled;
15174 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15175 if (FAILED(rc)) return rc;
15176
15177 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15178 if (FAILED(rc)) return rc;
15179
15180 BOOL fAccelerate3DEnabled;
15181 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15182 if (FAILED(rc)) return rc;
15183
15184 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15185 if (FAILED(rc)) return rc;
15186
15187 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15188 if (FAILED(rc)) return rc;
15189
15190 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15191 if (FAILED(rc)) return rc;
15192
15193 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15194 if (FAILED(rc)) return rc;
15195
15196 BOOL mRTCUseUTC;
15197 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15198 if (FAILED(rc)) return rc;
15199
15200 setRTCUseUTC(mRTCUseUTC);
15201 if (FAILED(rc)) return rc;
15202
15203 /* the setter does more than just the assignment, so use it */
15204 ChipsetType_T enmChipsetType;
15205 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15206 if (FAILED(rc)) return rc;
15207
15208 rc = COMSETTER(ChipsetType)(enmChipsetType);
15209 if (FAILED(rc)) return rc;
15210
15211 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15212 if (FAILED(rc)) return rc;
15213
15214 /* Apply network adapters defaults */
15215 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15216 mNetworkAdapters[slot]->i_applyDefaults(osType);
15217
15218 /* Apply serial port defaults */
15219 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15220 mSerialPorts[slot]->i_applyDefaults(osType);
15221
15222 /* Apply parallel port defaults - not OS dependent*/
15223 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15224 mParallelPorts[slot]->i_applyDefaults();
15225
15226 /* Audio stuff. */
15227 AudioControllerType_T audioController;
15228 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15229 if (FAILED(rc)) return rc;
15230
15231 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15232 if (FAILED(rc)) return rc;
15233
15234 AudioCodecType_T audioCodec;
15235 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15236 if (FAILED(rc)) return rc;
15237
15238 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15239 if (FAILED(rc)) return rc;
15240
15241 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15242 if (FAILED(rc)) return rc;
15243
15244 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15245 if (FAILED(rc)) return rc;
15246
15247 /* Storage Controllers */
15248 StorageControllerType_T hdStorageControllerType;
15249 StorageBus_T hdStorageBusType;
15250 StorageControllerType_T dvdStorageControllerType;
15251 StorageBus_T dvdStorageBusType;
15252 BOOL recommendedFloppy;
15253 ComPtr<IStorageController> floppyController;
15254 ComPtr<IStorageController> hdController;
15255 ComPtr<IStorageController> dvdController;
15256 Utf8Str strFloppyName, strDVDName, strHDName;
15257
15258 /* GUI auto generates controller names using bus type. Do the same*/
15259 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15260
15261 /* Floppy recommended? add one. */
15262 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15263 if (FAILED(rc)) return rc;
15264 if (recommendedFloppy)
15265 {
15266 rc = addStorageController(strFloppyName,
15267 StorageBus_Floppy,
15268 floppyController);
15269 if (FAILED(rc)) return rc;
15270 }
15271
15272 /* Setup one DVD storage controller. */
15273 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15274 if (FAILED(rc)) return rc;
15275
15276 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15277 if (FAILED(rc)) return rc;
15278
15279 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15280
15281 rc = addStorageController(strDVDName,
15282 dvdStorageBusType,
15283 dvdController);
15284 if (FAILED(rc)) return rc;
15285
15286 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15287 if (FAILED(rc)) return rc;
15288
15289 /* Setup one HDD storage controller. */
15290 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15291 if (FAILED(rc)) return rc;
15292
15293 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15294 if (FAILED(rc)) return rc;
15295
15296 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15297
15298 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15299 {
15300 rc = addStorageController(strHDName,
15301 hdStorageBusType,
15302 hdController);
15303 if (FAILED(rc)) return rc;
15304
15305 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15306 if (FAILED(rc)) return rc;
15307 }
15308 else
15309 {
15310 /* The HD controller is the same as DVD: */
15311 hdController = dvdController;
15312 }
15313
15314 /* Limit the AHCI port count if it's used because windows has trouble with
15315 * too many ports and other guest (OS X in particular) may take extra long
15316 * boot: */
15317
15318 // pParent = static_cast<Medium*>(aP)
15319 IStorageController *temp = hdController;
15320 ComObjPtr<StorageController> storageController;
15321 storageController = static_cast<StorageController *>(temp);
15322
15323 // tempHDController = aHDController;
15324 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15325 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15326 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15327 storageController->COMSETTER(PortCount)(1);
15328
15329 /* USB stuff */
15330
15331 bool ohciEnabled = false;
15332
15333 ComPtr<IUSBController> usbController;
15334 BOOL recommendedUSB3;
15335 BOOL recommendedUSB;
15336 BOOL usbProxyAvailable;
15337
15338 getUSBProxyAvailable(&usbProxyAvailable);
15339 if (FAILED(rc)) return rc;
15340
15341 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15342 if (FAILED(rc)) return rc;
15343 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15344 if (FAILED(rc)) return rc;
15345
15346 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15347 {
15348#ifdef VBOX_WITH_EXTPACK
15349 /* USB 3.0 is only available if the proper ExtPack is installed. */
15350 ExtPackManager *aManager = mParent->i_getExtPackManager();
15351 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15352 {
15353 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15354 if (FAILED(rc)) return rc;
15355
15356 /* xHci includes OHCI */
15357 ohciEnabled = true;
15358 }
15359#endif
15360 }
15361 if ( !ohciEnabled
15362 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15363 {
15364 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15365 if (FAILED(rc)) return rc;
15366 ohciEnabled = true;
15367
15368#ifdef VBOX_WITH_EXTPACK
15369 /* USB 2.0 is only available if the proper ExtPack is installed.
15370 * Note. Configuring EHCI here and providing messages about
15371 * the missing extpack isn't exactly clean, but it is a
15372 * necessary evil to patch over legacy compatability issues
15373 * introduced by the new distribution model. */
15374 ExtPackManager *manager = mParent->i_getExtPackManager();
15375 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15376 {
15377 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15378 if (FAILED(rc)) return rc;
15379 }
15380#endif
15381 }
15382
15383 /* Set recommended human interface device types: */
15384 BOOL recommendedUSBHID;
15385 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15386 if (FAILED(rc)) return rc;
15387
15388 if (recommendedUSBHID)
15389 {
15390 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15391 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15392 if (!ohciEnabled && !usbDeviceFilters.isNull())
15393 {
15394 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15395 if (FAILED(rc)) return rc;
15396 }
15397 }
15398
15399 BOOL recommendedUSBTablet;
15400 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15401 if (FAILED(rc)) return rc;
15402
15403 if (recommendedUSBTablet)
15404 {
15405 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15406 if (!ohciEnabled && !usbDeviceFilters.isNull())
15407 {
15408 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15409 if (FAILED(rc)) return rc;
15410 }
15411 }
15412 return S_OK;
15413}
15414
15415/* This isn't handled entirely by the wrapper generator yet. */
15416#ifdef VBOX_WITH_XPCOM
15417NS_DECL_CLASSINFO(SessionMachine)
15418NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15419
15420NS_DECL_CLASSINFO(SnapshotMachine)
15421NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15422#endif
Note: See TracBrowser for help on using the repository browser.

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