VirtualBox

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

Last change on this file since 67954 was 67738, checked in by vboxsync, 7 years ago

Machine.h: Avoid dragging in UnattendedImpl.h as it causes most of VBoxSVC to be recompiled each time its modified.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 519.2 KB
Line 
1/* $Id: MachineImpl.cpp 67738 2017-06-30 14:49:40Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2017 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/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#ifdef VBOX_WITH_UNATTENDED
47# include "UnattendedImpl.h"
48#endif
49#include "SystemPropertiesImpl.h"
50
51// generated header
52#include "VBoxEvents.h"
53
54#ifdef VBOX_WITH_USB
55# include "USBProxyService.h"
56#endif
57
58#include "AutoCaller.h"
59#include "HashedPw.h"
60#include "Performance.h"
61
62#include <iprt/asm.h>
63#include <iprt/path.h>
64#include <iprt/dir.h>
65#include <iprt/env.h>
66#include <iprt/lockvalidator.h>
67#include <iprt/process.h>
68#include <iprt/cpp/utils.h>
69#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
70#include <iprt/sha.h>
71#include <iprt/string.h>
72
73#include <VBox/com/array.h>
74#include <VBox/com/list.h>
75
76#include <VBox/err.h>
77#include <VBox/param.h>
78#include <VBox/settings.h>
79#include <VBox/vmm/ssm.h>
80
81#ifdef VBOX_WITH_GUEST_PROPS
82# include <VBox/HostServices/GuestPropertySvc.h>
83# include <VBox/com/array.h>
84#endif
85
86#include "VBox/com/MultiResult.h"
87
88#include <algorithm>
89
90#ifdef VBOX_WITH_DTRACE_R3_MAIN
91# include "dtrace/VBoxAPI.h"
92#endif
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mLockType = LockType_Null;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureRate = 512;
173 mVideoCaptureFPS = 25;
174 mVideoCaptureMaxTime = 0;
175 mVideoCaptureMaxFileSize = 0;
176 mVideoCaptureEnabled = false;
177 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
178 maVideoCaptureScreens[i] = true;
179
180 mHWVirtExEnabled = true;
181 mHWVirtExNestedPagingEnabled = true;
182#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
183 mHWVirtExLargePagesEnabled = true;
184#else
185 /* Not supported on 32 bits hosts. */
186 mHWVirtExLargePagesEnabled = false;
187#endif
188 mHWVirtExVPIDEnabled = true;
189 mHWVirtExUXEnabled = true;
190 mHWVirtExForceEnabled = false;
191#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
192 mPAEEnabled = true;
193#else
194 mPAEEnabled = false;
195#endif
196 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
197 mTripleFaultReset = false;
198 mAPIC = true;
199 mX2APIC = false;
200 mHPETEnabled = false;
201 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
202 mCpuIdPortabilityLevel = 0;
203 mCpuProfile = "host";
204
205 /* default boot order: floppy - DVD - HDD */
206 mBootOrder[0] = DeviceType_Floppy;
207 mBootOrder[1] = DeviceType_DVD;
208 mBootOrder[2] = DeviceType_HardDisk;
209 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
210 mBootOrder[i] = DeviceType_Null;
211
212 mClipboardMode = ClipboardMode_Disabled;
213 mDnDMode = DnDMode_Disabled;
214
215 mFirmwareType = FirmwareType_BIOS;
216 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
217 mPointingHIDType = PointingHIDType_PS2Mouse;
218 mChipsetType = ChipsetType_PIIX3;
219 mParavirtProvider = ParavirtProvider_Default;
220 mEmulatedUSBCardReaderEnabled = FALSE;
221
222 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
223 mCPUAttached[i] = false;
224
225 mIOCacheEnabled = true;
226 mIOCacheSize = 5; /* 5MB */
227}
228
229Machine::HWData::~HWData()
230{
231}
232
233/////////////////////////////////////////////////////////////////////////////
234// Machine class
235/////////////////////////////////////////////////////////////////////////////
236
237// constructor / destructor
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::Machine() :
241#ifdef VBOX_WITH_RESOURCE_USAGE_API
242 mCollectorGuest(NULL),
243#endif
244 mPeer(NULL),
245 mParent(NULL),
246 mSerialPorts(),
247 mParallelPorts(),
248 uRegistryNeedsSaving(0)
249{}
250
251Machine::~Machine()
252{}
253
254HRESULT Machine::FinalConstruct()
255{
256 LogFlowThisFunc(("\n"));
257 return BaseFinalConstruct();
258}
259
260void Machine::FinalRelease()
261{
262 LogFlowThisFunc(("\n"));
263 uninit();
264 BaseFinalRelease();
265}
266
267/**
268 * Initializes a new machine instance; this init() variant creates a new, empty machine.
269 * This gets called from VirtualBox::CreateMachine().
270 *
271 * @param aParent Associated parent object
272 * @param strConfigFile Local file system path to the VM settings file (can
273 * be relative to the VirtualBox config directory).
274 * @param strName name for the machine
275 * @param llGroups list of groups for the machine
276 * @param aOsType OS Type of this machine or NULL.
277 * @param aId UUID for the new machine.
278 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
279 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
280 * scheme (includes the UUID).
281 *
282 * @return Success indicator. if not S_OK, the machine object is invalid
283 */
284HRESULT Machine::init(VirtualBox *aParent,
285 const Utf8Str &strConfigFile,
286 const Utf8Str &strName,
287 const StringsList &llGroups,
288 GuestOSType *aOsType,
289 const Guid &aId,
290 bool fForceOverwrite,
291 bool fDirectoryIncludesUUID)
292{
293 LogFlowThisFuncEnter();
294 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
295
296 /* Enclose the state transition NotReady->InInit->Ready */
297 AutoInitSpan autoInitSpan(this);
298 AssertReturn(autoInitSpan.isOk(), E_FAIL);
299
300 HRESULT rc = initImpl(aParent, strConfigFile);
301 if (FAILED(rc)) return rc;
302
303 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
304 if (FAILED(rc)) return rc;
305
306 if (SUCCEEDED(rc))
307 {
308 // create an empty machine config
309 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
310
311 rc = initDataAndChildObjects();
312 }
313
314 if (SUCCEEDED(rc))
315 {
316 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
317 mData->mAccessible = TRUE;
318
319 unconst(mData->mUuid) = aId;
320
321 mUserData->s.strName = strName;
322
323 mUserData->s.llGroups = llGroups;
324
325 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
326 // the "name sync" flag determines whether the machine directory gets renamed along
327 // with the machine file; say so if the settings file name is the same as the
328 // settings file parent directory (machine directory)
329 mUserData->s.fNameSync = i_isInOwnDir();
330
331 // initialize the default snapshots folder
332 rc = COMSETTER(SnapshotFolder)(NULL);
333 AssertComRC(rc);
334
335 if (aOsType)
336 {
337 /* Store OS type */
338 mUserData->s.strOsType = aOsType->i_id();
339
340 /* Apply BIOS defaults */
341 mBIOSSettings->i_applyDefaults(aOsType);
342
343 /* Let the OS type select 64-bit ness. */
344 mHWData->mLongMode = aOsType->i_is64Bit()
345 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
346
347 /* Let the OS type enable the X2APIC */
348 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
349 }
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Apply parallel port defaults */
360 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
361 mParallelPorts[slot]->i_applyDefaults();
362
363 /* At this point the changing of the current state modification
364 * flag is allowed. */
365 i_allowStateModification();
366
367 /* commit all changes made during the initialization */
368 i_commit();
369 }
370
371 /* Confirm a successful initialization when it's the case */
372 if (SUCCEEDED(rc))
373 {
374 if (mData->mAccessible)
375 autoInitSpan.setSucceeded();
376 else
377 autoInitSpan.setLimited();
378 }
379
380 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
381 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
382 mData->mRegistered,
383 mData->mAccessible,
384 rc));
385
386 LogFlowThisFuncLeave();
387
388 return rc;
389}
390
391/**
392 * Initializes a new instance with data from machine XML (formerly Init_Registered).
393 * Gets called in two modes:
394 *
395 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
396 * UUID is specified and we mark the machine as "registered";
397 *
398 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
399 * and the machine remains unregistered until RegisterMachine() is called.
400 *
401 * @param aParent Associated parent object
402 * @param strConfigFile Local file system path to the VM settings file (can
403 * be relative to the VirtualBox config directory).
404 * @param aId UUID of the machine or NULL (see above).
405 *
406 * @return Success indicator. if not S_OK, the machine object is invalid
407 */
408HRESULT Machine::initFromSettings(VirtualBox *aParent,
409 const Utf8Str &strConfigFile,
410 const Guid *aId)
411{
412 LogFlowThisFuncEnter();
413 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
414
415 /* Enclose the state transition NotReady->InInit->Ready */
416 AutoInitSpan autoInitSpan(this);
417 AssertReturn(autoInitSpan.isOk(), E_FAIL);
418
419 HRESULT rc = initImpl(aParent, strConfigFile);
420 if (FAILED(rc)) return rc;
421
422 if (aId)
423 {
424 // loading a registered VM:
425 unconst(mData->mUuid) = *aId;
426 mData->mRegistered = TRUE;
427 // now load the settings from XML:
428 rc = i_registeredInit();
429 // this calls initDataAndChildObjects() and loadSettings()
430 }
431 else
432 {
433 // opening an unregistered VM (VirtualBox::OpenMachine()):
434 rc = initDataAndChildObjects();
435
436 if (SUCCEEDED(rc))
437 {
438 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
439 mData->mAccessible = TRUE;
440
441 try
442 {
443 // load and parse machine XML; this will throw on XML or logic errors
444 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
445
446 // reject VM UUID duplicates, they can happen if someone
447 // tries to register an already known VM config again
448 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
449 true /* fPermitInaccessible */,
450 false /* aDoSetError */,
451 NULL) != VBOX_E_OBJECT_NOT_FOUND)
452 {
453 throw setError(E_FAIL,
454 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
455 mData->m_strConfigFile.c_str());
456 }
457
458 // use UUID from machine config
459 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
460
461 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
462 NULL /* puuidRegistry */);
463 if (FAILED(rc)) throw rc;
464
465 /* At this point the changing of the current state modification
466 * flag is allowed. */
467 i_allowStateModification();
468
469 i_commit();
470 }
471 catch (HRESULT err)
472 {
473 /* we assume that error info is set by the thrower */
474 rc = err;
475 }
476 catch (...)
477 {
478 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
479 }
480 }
481 }
482
483 /* Confirm a successful initialization when it's the case */
484 if (SUCCEEDED(rc))
485 {
486 if (mData->mAccessible)
487 autoInitSpan.setSucceeded();
488 else
489 {
490 autoInitSpan.setLimited();
491
492 // uninit media from this machine's media registry, or else
493 // reloading the settings will fail
494 mParent->i_unregisterMachineMedia(i_getId());
495 }
496 }
497
498 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
499 "rc=%08X\n",
500 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
501 mData->mRegistered, mData->mAccessible, rc));
502
503 LogFlowThisFuncLeave();
504
505 return rc;
506}
507
508/**
509 * Initializes a new instance from a machine config that is already in memory
510 * (import OVF case). Since we are importing, the UUID in the machine
511 * config is ignored and we always generate a fresh one.
512 *
513 * @param aParent Associated parent object.
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent The VirtualBox object.
607 * @param strConfigFile Settings file.
608 * @return
609 */
610HRESULT Machine::initImpl(VirtualBox *aParent,
611 const Utf8Str &strConfigFile)
612{
613 LogFlowThisFuncEnter();
614
615 AssertReturn(aParent, E_INVALIDARG);
616 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
617
618 HRESULT rc = S_OK;
619
620 /* share the parent weakly */
621 unconst(mParent) = aParent;
622
623 /* allocate the essential machine data structure (the rest will be
624 * allocated later by initDataAndChildObjects() */
625 mData.allocate();
626
627 /* memorize the config file name (as provided) */
628 mData->m_strConfigFile = strConfigFile;
629
630 /* get the full file name */
631 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
632 if (RT_FAILURE(vrc1))
633 return setError(VBOX_E_FILE_ERROR,
634 tr("Invalid machine settings file name '%s' (%Rrc)"),
635 strConfigFile.c_str(),
636 vrc1);
637
638 LogFlowThisFuncLeave();
639
640 return rc;
641}
642
643/**
644 * Tries to create a machine settings file in the path stored in the machine
645 * instance data. Used when a new machine is created to fail gracefully if
646 * the settings file could not be written (e.g. because machine dir is read-only).
647 * @return
648 */
649HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
650{
651 HRESULT rc = S_OK;
652
653 // when we create a new machine, we must be able to create the settings file
654 RTFILE f = NIL_RTFILE;
655 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
656 if ( RT_SUCCESS(vrc)
657 || vrc == VERR_SHARING_VIOLATION
658 )
659 {
660 if (RT_SUCCESS(vrc))
661 RTFileClose(f);
662 if (!fForceOverwrite)
663 rc = setError(VBOX_E_FILE_ERROR,
664 tr("Machine settings file '%s' already exists"),
665 mData->m_strConfigFileFull.c_str());
666 else
667 {
668 /* try to delete the config file, as otherwise the creation
669 * of a new settings file will fail. */
670 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
671 if (RT_FAILURE(vrc2))
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Could not delete the existing settings file '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(), vrc2);
675 }
676 }
677 else if ( vrc != VERR_FILE_NOT_FOUND
678 && vrc != VERR_PATH_NOT_FOUND
679 )
680 rc = setError(VBOX_E_FILE_ERROR,
681 tr("Invalid machine settings file name '%s' (%Rrc)"),
682 mData->m_strConfigFileFull.c_str(),
683 vrc);
684 return rc;
685}
686
687/**
688 * Initializes the registered machine by loading the settings file.
689 * This method is separated from #init() in order to make it possible to
690 * retry the operation after VirtualBox startup instead of refusing to
691 * startup the whole VirtualBox server in case if the settings file of some
692 * registered VM is invalid or inaccessible.
693 *
694 * @note Must be always called from this object's write lock
695 * (unless called from #init() that doesn't need any locking).
696 * @note Locks the mUSBController method for writing.
697 * @note Subclasses must not call this method.
698 */
699HRESULT Machine::i_registeredInit()
700{
701 AssertReturn(!i_isSessionMachine(), E_FAIL);
702 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
703 AssertReturn(mData->mUuid.isValid(), E_FAIL);
704 AssertReturn(!mData->mAccessible, E_FAIL);
705
706 HRESULT rc = initDataAndChildObjects();
707
708 if (SUCCEEDED(rc))
709 {
710 /* Temporarily reset the registered flag in order to let setters
711 * potentially called from loadSettings() succeed (isMutable() used in
712 * all setters will return FALSE for a Machine instance if mRegistered
713 * is TRUE). */
714 mData->mRegistered = FALSE;
715
716 try
717 {
718 // load and parse machine XML; this will throw on XML or logic errors
719 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
720
721 if (mData->mUuid != mData->pMachineConfigFile->uuid)
722 throw setError(E_FAIL,
723 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
724 mData->pMachineConfigFile->uuid.raw(),
725 mData->m_strConfigFileFull.c_str(),
726 mData->mUuid.toString().c_str(),
727 mParent->i_settingsFilePath().c_str());
728
729 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
730 NULL /* const Guid *puuidRegistry */);
731 if (FAILED(rc)) throw rc;
732 }
733 catch (HRESULT err)
734 {
735 /* we assume that error info is set by the thrower */
736 rc = err;
737 }
738 catch (...)
739 {
740 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
741 }
742
743 /* Restore the registered flag (even on failure) */
744 mData->mRegistered = TRUE;
745 }
746
747 if (SUCCEEDED(rc))
748 {
749 /* Set mAccessible to TRUE only if we successfully locked and loaded
750 * the settings file */
751 mData->mAccessible = TRUE;
752
753 /* commit all changes made during loading the settings file */
754 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
755 /// @todo r=klaus for some reason the settings loading logic backs up
756 // the settings, and therefore a commit is needed. Should probably be changed.
757 }
758 else
759 {
760 /* If the machine is registered, then, instead of returning a
761 * failure, we mark it as inaccessible and set the result to
762 * success to give it a try later */
763
764 /* fetch the current error info */
765 mData->mAccessError = com::ErrorInfo();
766 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
767
768 /* rollback all changes */
769 i_rollback(false /* aNotify */);
770
771 // uninit media from this machine's media registry, or else
772 // reloading the settings will fail
773 mParent->i_unregisterMachineMedia(i_getId());
774
775 /* uninitialize the common part to make sure all data is reset to
776 * default (null) values */
777 uninitDataAndChildObjects();
778
779 rc = S_OK;
780 }
781
782 return rc;
783}
784
785/**
786 * Uninitializes the instance.
787 * Called either from FinalRelease() or by the parent when it gets destroyed.
788 *
789 * @note The caller of this method must make sure that this object
790 * a) doesn't have active callers on the current thread and b) is not locked
791 * by the current thread; otherwise uninit() will hang either a) due to
792 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
793 * a dead-lock caused by this thread waiting for all callers on the other
794 * threads are done but preventing them from doing so by holding a lock.
795 */
796void Machine::uninit()
797{
798 LogFlowThisFuncEnter();
799
800 Assert(!isWriteLockOnCurrentThread());
801
802 Assert(!uRegistryNeedsSaving);
803 if (uRegistryNeedsSaving)
804 {
805 AutoCaller autoCaller(this);
806 if (SUCCEEDED(autoCaller.rc()))
807 {
808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
809 i_saveSettings(NULL, Machine::SaveS_Force);
810 }
811 }
812
813 /* Enclose the state transition Ready->InUninit->NotReady */
814 AutoUninitSpan autoUninitSpan(this);
815 if (autoUninitSpan.uninitDone())
816 return;
817
818 Assert(!i_isSnapshotMachine());
819 Assert(!i_isSessionMachine());
820 Assert(!!mData);
821
822 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
823 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
824
825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
826
827 if (!mData->mSession.mMachine.isNull())
828 {
829 /* Theoretically, this can only happen if the VirtualBox server has been
830 * terminated while there were clients running that owned open direct
831 * sessions. Since in this case we are definitely called by
832 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
833 * won't happen on the client watcher thread (because it has a
834 * VirtualBox caller for the duration of the
835 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
836 * cannot happen until the VirtualBox caller is released). This is
837 * important, because SessionMachine::uninit() cannot correctly operate
838 * after we return from this method (it expects the Machine instance is
839 * still valid). We'll call it ourselves below.
840 */
841 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
842 (SessionMachine*)mData->mSession.mMachine));
843
844 if (Global::IsOnlineOrTransient(mData->mMachineState))
845 {
846 Log1WarningThisFunc(("Setting state to Aborted!\n"));
847 /* set machine state using SessionMachine reimplementation */
848 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
849 }
850
851 /*
852 * Uninitialize SessionMachine using public uninit() to indicate
853 * an unexpected uninitialization.
854 */
855 mData->mSession.mMachine->uninit();
856 /* SessionMachine::uninit() must set mSession.mMachine to null */
857 Assert(mData->mSession.mMachine.isNull());
858 }
859
860 // uninit media from this machine's media registry, if they're still there
861 Guid uuidMachine(i_getId());
862
863 /* the lock is no more necessary (SessionMachine is uninitialized) */
864 alock.release();
865
866 /* XXX This will fail with
867 * "cannot be closed because it is still attached to 1 virtual machines"
868 * because at this point we did not call uninitDataAndChildObjects() yet
869 * and therefore also removeBackReference() for all these mediums was not called! */
870
871 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
872 mParent->i_unregisterMachineMedia(uuidMachine);
873
874 // has machine been modified?
875 if (mData->flModifications)
876 {
877 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
878 i_rollback(false /* aNotify */);
879 }
880
881 if (mData->mAccessible)
882 uninitDataAndChildObjects();
883
884 /* free the essential data structure last */
885 mData.free();
886
887 LogFlowThisFuncLeave();
888}
889
890// Wrapped IMachine properties
891/////////////////////////////////////////////////////////////////////////////
892HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
893{
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 aParent = pVirtualBox;
897
898 return S_OK;
899}
900
901
902HRESULT Machine::getAccessible(BOOL *aAccessible)
903{
904 /* In some cases (medium registry related), it is necessary to be able to
905 * go through the list of all machines. Happens when an inaccessible VM
906 * has a sensible medium registry. */
907 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
909
910 HRESULT rc = S_OK;
911
912 if (!mData->mAccessible)
913 {
914 /* try to initialize the VM once more if not accessible */
915
916 AutoReinitSpan autoReinitSpan(this);
917 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
918
919#ifdef DEBUG
920 LogFlowThisFunc(("Dumping media backreferences\n"));
921 mParent->i_dumpAllBackRefs();
922#endif
923
924 if (mData->pMachineConfigFile)
925 {
926 // reset the XML file to force loadSettings() (called from i_registeredInit())
927 // to parse it again; the file might have changed
928 delete mData->pMachineConfigFile;
929 mData->pMachineConfigFile = NULL;
930 }
931
932 rc = i_registeredInit();
933
934 if (SUCCEEDED(rc) && mData->mAccessible)
935 {
936 autoReinitSpan.setSucceeded();
937
938 /* make sure interesting parties will notice the accessibility
939 * state change */
940 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
941 mParent->i_onMachineDataChange(mData->mUuid);
942 }
943 }
944
945 if (SUCCEEDED(rc))
946 *aAccessible = mData->mAccessible;
947
948 LogFlowThisFuncLeave();
949
950 return rc;
951}
952
953HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
954{
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
958 {
959 /* return shortly */
960 aAccessError = NULL;
961 return S_OK;
962 }
963
964 HRESULT rc = S_OK;
965
966 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
967 rc = errorInfo.createObject();
968 if (SUCCEEDED(rc))
969 {
970 errorInfo->init(mData->mAccessError.getResultCode(),
971 mData->mAccessError.getInterfaceID().ref(),
972 Utf8Str(mData->mAccessError.getComponent()).c_str(),
973 Utf8Str(mData->mAccessError.getText()));
974 aAccessError = errorInfo;
975 }
976
977 return rc;
978}
979
980HRESULT Machine::getName(com::Utf8Str &aName)
981{
982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
983
984 aName = mUserData->s.strName;
985
986 return S_OK;
987}
988
989HRESULT Machine::setName(const com::Utf8Str &aName)
990{
991 // prohibit setting a UUID only as the machine name, or else it can
992 // never be found by findMachine()
993 Guid test(aName);
994
995 if (test.isValid())
996 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
997
998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 HRESULT rc = i_checkStateDependency(MutableStateDep);
1001 if (FAILED(rc)) return rc;
1002
1003 i_setModified(IsModified_MachineData);
1004 mUserData.backup();
1005 mUserData->s.strName = aName;
1006
1007 return S_OK;
1008}
1009
1010HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1011{
1012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 aDescription = mUserData->s.strDescription;
1015
1016 return S_OK;
1017}
1018
1019HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1020{
1021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 // this can be done in principle in any state as it doesn't affect the VM
1024 // significantly, but play safe by not messing around while complex
1025 // activities are going on
1026 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1027 if (FAILED(rc)) return rc;
1028
1029 i_setModified(IsModified_MachineData);
1030 mUserData.backup();
1031 mUserData->s.strDescription = aDescription;
1032
1033 return S_OK;
1034}
1035
1036HRESULT Machine::getId(com::Guid &aId)
1037{
1038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 aId = mData->mUuid;
1041
1042 return S_OK;
1043}
1044
1045HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1046{
1047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1048 aGroups.resize(mUserData->s.llGroups.size());
1049 size_t i = 0;
1050 for (StringsList::const_iterator
1051 it = mUserData->s.llGroups.begin();
1052 it != mUserData->s.llGroups.end();
1053 ++it, ++i)
1054 aGroups[i] = (*it);
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1060{
1061 StringsList llGroups;
1062 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1063 if (FAILED(rc))
1064 return rc;
1065
1066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1067
1068 rc = i_checkStateDependency(MutableOrSavedStateDep);
1069 if (FAILED(rc)) return rc;
1070
1071 i_setModified(IsModified_MachineData);
1072 mUserData.backup();
1073 mUserData->s.llGroups = llGroups;
1074
1075 return S_OK;
1076}
1077
1078HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1079{
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 aOSTypeId = mUserData->s.strOsType;
1083
1084 return S_OK;
1085}
1086
1087HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1088{
1089 /* look up the object by Id to check it is valid */
1090 ComObjPtr<GuestOSType> pGuestOSType;
1091 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1092 pGuestOSType);
1093 if (FAILED(rc)) return rc;
1094
1095 /* when setting, always use the "etalon" value for consistency -- lookup
1096 * by ID is case-insensitive and the input value may have different case */
1097 Utf8Str osTypeId = pGuestOSType->i_id();
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 rc = i_checkStateDependency(MutableStateDep);
1102 if (FAILED(rc)) return rc;
1103
1104 i_setModified(IsModified_MachineData);
1105 mUserData.backup();
1106 mUserData->s.strOsType = osTypeId;
1107
1108 return S_OK;
1109}
1110
1111HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1112{
1113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 *aFirmwareType = mHWData->mFirmwareType;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1121{
1122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 HRESULT rc = i_checkStateDependency(MutableStateDep);
1125 if (FAILED(rc)) return rc;
1126
1127 i_setModified(IsModified_MachineData);
1128 mHWData.backup();
1129 mHWData->mFirmwareType = aFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1135{
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1144{
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 HRESULT rc = i_checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 i_setModified(IsModified_MachineData);
1151 mHWData.backup();
1152 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aPointingHIDType = mHWData->mPointingHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mPointingHIDType = aPointingHIDType;
1176
1177 return S_OK;
1178}
1179
1180HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1181{
1182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 *aChipsetType = mHWData->mChipsetType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1190{
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = i_checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 if (aChipsetType != mHWData->mChipsetType)
1197 {
1198 i_setModified(IsModified_MachineData);
1199 mHWData.backup();
1200 mHWData->mChipsetType = aChipsetType;
1201
1202 // Resize network adapter array, to be finalized on commit/rollback.
1203 // We must not throw away entries yet, otherwise settings are lost
1204 // without a way to roll back.
1205 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1206 size_t oldCount = mNetworkAdapters.size();
1207 if (newCount > oldCount)
1208 {
1209 mNetworkAdapters.resize(newCount);
1210 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1211 {
1212 unconst(mNetworkAdapters[slot]).createObject();
1213 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1214 }
1215 }
1216 }
1217
1218 return S_OK;
1219}
1220
1221HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1222{
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 aParavirtDebug = mHWData->mParavirtDebug;
1226 return S_OK;
1227}
1228
1229HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1230{
1231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 HRESULT rc = i_checkStateDependency(MutableStateDep);
1234 if (FAILED(rc)) return rc;
1235
1236 /** @todo Parse/validate options? */
1237 if (aParavirtDebug != mHWData->mParavirtDebug)
1238 {
1239 i_setModified(IsModified_MachineData);
1240 mHWData.backup();
1241 mHWData->mParavirtDebug = aParavirtDebug;
1242 }
1243
1244 return S_OK;
1245}
1246
1247HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1248{
1249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 *aParavirtProvider = mHWData->mParavirtProvider;
1252
1253 return S_OK;
1254}
1255
1256HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1257{
1258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1259
1260 HRESULT rc = i_checkStateDependency(MutableStateDep);
1261 if (FAILED(rc)) return rc;
1262
1263 if (aParavirtProvider != mHWData->mParavirtProvider)
1264 {
1265 i_setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mParavirtProvider = aParavirtProvider;
1268 }
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1274{
1275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 *aParavirtProvider = mHWData->mParavirtProvider;
1278 switch (mHWData->mParavirtProvider)
1279 {
1280 case ParavirtProvider_None:
1281 case ParavirtProvider_HyperV:
1282 case ParavirtProvider_KVM:
1283 case ParavirtProvider_Minimal:
1284 break;
1285
1286 /* Resolve dynamic provider types to the effective types. */
1287 default:
1288 {
1289 ComObjPtr<GuestOSType> pGuestOSType;
1290 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1291 pGuestOSType);
1292 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1293
1294 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1295 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1296
1297 switch (mHWData->mParavirtProvider)
1298 {
1299 case ParavirtProvider_Legacy:
1300 {
1301 if (fOsXGuest)
1302 *aParavirtProvider = ParavirtProvider_Minimal;
1303 else
1304 *aParavirtProvider = ParavirtProvider_None;
1305 break;
1306 }
1307
1308 case ParavirtProvider_Default:
1309 {
1310 if (fOsXGuest)
1311 *aParavirtProvider = ParavirtProvider_Minimal;
1312 else if ( mUserData->s.strOsType == "Windows10"
1313 || mUserData->s.strOsType == "Windows10_64"
1314 || mUserData->s.strOsType == "Windows81"
1315 || mUserData->s.strOsType == "Windows81_64"
1316 || mUserData->s.strOsType == "Windows8"
1317 || mUserData->s.strOsType == "Windows8_64"
1318 || mUserData->s.strOsType == "Windows7"
1319 || mUserData->s.strOsType == "Windows7_64"
1320 || mUserData->s.strOsType == "WindowsVista"
1321 || mUserData->s.strOsType == "WindowsVista_64"
1322 || mUserData->s.strOsType == "Windows2012"
1323 || mUserData->s.strOsType == "Windows2012_64"
1324 || mUserData->s.strOsType == "Windows2008"
1325 || mUserData->s.strOsType == "Windows2008_64")
1326 {
1327 *aParavirtProvider = ParavirtProvider_HyperV;
1328 }
1329 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1330 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1331 || mUserData->s.strOsType == "Linux"
1332 || mUserData->s.strOsType == "Linux_64"
1333 || mUserData->s.strOsType == "ArchLinux"
1334 || mUserData->s.strOsType == "ArchLinux_64"
1335 || mUserData->s.strOsType == "Debian"
1336 || mUserData->s.strOsType == "Debian_64"
1337 || mUserData->s.strOsType == "Fedora"
1338 || mUserData->s.strOsType == "Fedora_64"
1339 || mUserData->s.strOsType == "Gentoo"
1340 || mUserData->s.strOsType == "Gentoo_64"
1341 || mUserData->s.strOsType == "Mandriva"
1342 || mUserData->s.strOsType == "Mandriva_64"
1343 || mUserData->s.strOsType == "OpenSUSE"
1344 || mUserData->s.strOsType == "OpenSUSE_64"
1345 || mUserData->s.strOsType == "Oracle"
1346 || mUserData->s.strOsType == "Oracle_64"
1347 || mUserData->s.strOsType == "RedHat"
1348 || mUserData->s.strOsType == "RedHat_64"
1349 || mUserData->s.strOsType == "Turbolinux"
1350 || mUserData->s.strOsType == "Turbolinux_64"
1351 || mUserData->s.strOsType == "Ubuntu"
1352 || mUserData->s.strOsType == "Ubuntu_64"
1353 || mUserData->s.strOsType == "Xandros"
1354 || mUserData->s.strOsType == "Xandros_64")
1355 {
1356 *aParavirtProvider = ParavirtProvider_KVM;
1357 }
1358 else
1359 *aParavirtProvider = ParavirtProvider_None;
1360 break;
1361 }
1362
1363 default: AssertFailedBreak(); /* Shut up MSC. */
1364 }
1365 break;
1366 }
1367 }
1368
1369 Assert( *aParavirtProvider == ParavirtProvider_None
1370 || *aParavirtProvider == ParavirtProvider_Minimal
1371 || *aParavirtProvider == ParavirtProvider_HyperV
1372 || *aParavirtProvider == ParavirtProvider_KVM);
1373 return S_OK;
1374}
1375
1376HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1377{
1378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1379
1380 aHardwareVersion = mHWData->mHWVersion;
1381
1382 return S_OK;
1383}
1384
1385HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1386{
1387 /* check known version */
1388 Utf8Str hwVersion = aHardwareVersion;
1389 if ( hwVersion.compare("1") != 0
1390 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1391 return setError(E_INVALIDARG,
1392 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mHWVersion = aHardwareVersion;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 if (!mHWData->mHardwareUUID.isZero())
1411 aHardwareUUID = mHWData->mHardwareUUID;
1412 else
1413 aHardwareUUID = mData->mUuid;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1419{
1420 if (!aHardwareUUID.isValid())
1421 return E_INVALIDARG;
1422
1423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 HRESULT rc = i_checkStateDependency(MutableStateDep);
1426 if (FAILED(rc)) return rc;
1427
1428 i_setModified(IsModified_MachineData);
1429 mHWData.backup();
1430 if (aHardwareUUID == mData->mUuid)
1431 mHWData->mHardwareUUID.clear();
1432 else
1433 mHWData->mHardwareUUID = aHardwareUUID;
1434
1435 return S_OK;
1436}
1437
1438HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1439{
1440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1441
1442 *aMemorySize = mHWData->mMemorySize;
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::setMemorySize(ULONG aMemorySize)
1448{
1449 /* check RAM limits */
1450 if ( aMemorySize < MM_RAM_MIN_IN_MB
1451 || aMemorySize > MM_RAM_MAX_IN_MB
1452 )
1453 return setError(E_INVALIDARG,
1454 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1455 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1456
1457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1458
1459 HRESULT rc = i_checkStateDependency(MutableStateDep);
1460 if (FAILED(rc)) return rc;
1461
1462 i_setModified(IsModified_MachineData);
1463 mHWData.backup();
1464 mHWData->mMemorySize = aMemorySize;
1465
1466 return S_OK;
1467}
1468
1469HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1470{
1471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 *aCPUCount = mHWData->mCPUCount;
1474
1475 return S_OK;
1476}
1477
1478HRESULT Machine::setCPUCount(ULONG aCPUCount)
1479{
1480 /* check CPU limits */
1481 if ( aCPUCount < SchemaDefs::MinCPUCount
1482 || aCPUCount > SchemaDefs::MaxCPUCount
1483 )
1484 return setError(E_INVALIDARG,
1485 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1486 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1487
1488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1491 if (mHWData->mCPUHotPlugEnabled)
1492 {
1493 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1494 {
1495 if (mHWData->mCPUAttached[idx])
1496 return setError(E_INVALIDARG,
1497 tr("There is still a CPU attached to socket %lu."
1498 "Detach the CPU before removing the socket"),
1499 aCPUCount, idx+1);
1500 }
1501 }
1502
1503 HRESULT rc = i_checkStateDependency(MutableStateDep);
1504 if (FAILED(rc)) return rc;
1505
1506 i_setModified(IsModified_MachineData);
1507 mHWData.backup();
1508 mHWData->mCPUCount = aCPUCount;
1509
1510 return S_OK;
1511}
1512
1513HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1514{
1515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1516
1517 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1523{
1524 HRESULT rc = S_OK;
1525
1526 /* check throttle limits */
1527 if ( aCPUExecutionCap < 1
1528 || aCPUExecutionCap > 100
1529 )
1530 return setError(E_INVALIDARG,
1531 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1532 aCPUExecutionCap, 1, 100);
1533
1534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 alock.release();
1537 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1538 alock.acquire();
1539 if (FAILED(rc)) return rc;
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1544
1545 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1546 if (Global::IsOnline(mData->mMachineState))
1547 i_saveSettings(NULL);
1548
1549 return S_OK;
1550}
1551
1552HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1553{
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1557
1558 return S_OK;
1559}
1560
1561HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1562{
1563 HRESULT rc = S_OK;
1564
1565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1566
1567 rc = i_checkStateDependency(MutableStateDep);
1568 if (FAILED(rc)) return rc;
1569
1570 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1571 {
1572 if (aCPUHotPlugEnabled)
1573 {
1574 i_setModified(IsModified_MachineData);
1575 mHWData.backup();
1576
1577 /* Add the amount of CPUs currently attached */
1578 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1579 mHWData->mCPUAttached[i] = true;
1580 }
1581 else
1582 {
1583 /*
1584 * We can disable hotplug only if the amount of maximum CPUs is equal
1585 * to the amount of attached CPUs
1586 */
1587 unsigned cCpusAttached = 0;
1588 unsigned iHighestId = 0;
1589
1590 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1591 {
1592 if (mHWData->mCPUAttached[i])
1593 {
1594 cCpusAttached++;
1595 iHighestId = i;
1596 }
1597 }
1598
1599 if ( (cCpusAttached != mHWData->mCPUCount)
1600 || (iHighestId >= mHWData->mCPUCount))
1601 return setError(E_INVALIDARG,
1602 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1603
1604 i_setModified(IsModified_MachineData);
1605 mHWData.backup();
1606 }
1607 }
1608
1609 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1610
1611 return rc;
1612}
1613
1614HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1615{
1616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1619
1620 return S_OK;
1621}
1622
1623HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1624{
1625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1628 if (SUCCEEDED(hrc))
1629 {
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1633 }
1634 return hrc;
1635}
1636
1637HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1638{
1639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1640 aCPUProfile = mHWData->mCpuProfile;
1641 return S_OK;
1642}
1643
1644HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1645{
1646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1648 if (SUCCEEDED(hrc))
1649 {
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 /* Empty equals 'host'. */
1653 if (aCPUProfile.isNotEmpty())
1654 mHWData->mCpuProfile = aCPUProfile;
1655 else
1656 mHWData->mCpuProfile = "host";
1657 }
1658 return hrc;
1659}
1660
1661HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1662{
1663#ifdef VBOX_WITH_USB_CARDREADER
1664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1667
1668 return S_OK;
1669#else
1670 NOREF(aEmulatedUSBCardReaderEnabled);
1671 return E_NOTIMPL;
1672#endif
1673}
1674
1675HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1676{
1677#ifdef VBOX_WITH_USB_CARDREADER
1678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1681 if (FAILED(rc)) return rc;
1682
1683 i_setModified(IsModified_MachineData);
1684 mHWData.backup();
1685 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1686
1687 return S_OK;
1688#else
1689 NOREF(aEmulatedUSBCardReaderEnabled);
1690 return E_NOTIMPL;
1691#endif
1692}
1693
1694HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1695{
1696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1697
1698 *aHPETEnabled = mHWData->mHPETEnabled;
1699
1700 return S_OK;
1701}
1702
1703HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1704{
1705 HRESULT rc = S_OK;
1706
1707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1708
1709 rc = i_checkStateDependency(MutableStateDep);
1710 if (FAILED(rc)) return rc;
1711
1712 i_setModified(IsModified_MachineData);
1713 mHWData.backup();
1714
1715 mHWData->mHPETEnabled = aHPETEnabled;
1716
1717 return rc;
1718}
1719
1720HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1721{
1722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1725 return S_OK;
1726}
1727
1728HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1729{
1730 HRESULT rc = S_OK;
1731
1732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 i_setModified(IsModified_MachineData);
1735 mHWData.backup();
1736 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1737
1738 alock.release();
1739 rc = i_onVideoCaptureChange();
1740 alock.acquire();
1741 if (FAILED(rc))
1742 {
1743 /*
1744 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1745 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1746 * determine if it should start or stop capturing. Therefore we need to manually
1747 * undo change.
1748 */
1749 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1750 return rc;
1751 }
1752
1753 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1754 if (Global::IsOnline(mData->mMachineState))
1755 i_saveSettings(NULL);
1756
1757 return rc;
1758}
1759
1760HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1761{
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1764 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1765 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1766 return S_OK;
1767}
1768
1769HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1770{
1771 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1772 bool fChanged = false;
1773
1774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1775
1776 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1777 {
1778 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1779 {
1780 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1781 fChanged = true;
1782 }
1783 }
1784 if (fChanged)
1785 {
1786 alock.release();
1787 HRESULT rc = i_onVideoCaptureChange();
1788 alock.acquire();
1789 if (FAILED(rc)) return rc;
1790 i_setModified(IsModified_MachineData);
1791
1792 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1793 if (Global::IsOnline(mData->mMachineState))
1794 i_saveSettings(NULL);
1795 }
1796
1797 return S_OK;
1798}
1799
1800HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1801{
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803 if (mHWData->mVideoCaptureFile.isEmpty())
1804 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1805 else
1806 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1807 return S_OK;
1808}
1809
1810HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1811{
1812 Utf8Str strFile(aVideoCaptureFile);
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 if ( Global::IsOnline(mData->mMachineState)
1816 && mHWData->mVideoCaptureEnabled)
1817 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1818
1819 if (!RTPathStartsWithRoot(strFile.c_str()))
1820 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1821
1822 if (!strFile.isEmpty())
1823 {
1824 Utf8Str defaultFile;
1825 i_getDefaultVideoCaptureFile(defaultFile);
1826 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1827 strFile.setNull();
1828 }
1829
1830 i_setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureFile = strFile;
1833
1834 return S_OK;
1835}
1836
1837HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1838{
1839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1840 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1845{
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 if ( Global::IsOnline(mData->mMachineState)
1849 && mHWData->mVideoCaptureEnabled)
1850 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1851
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1855
1856 return S_OK;
1857}
1858
1859HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1860{
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1863 return S_OK;
1864}
1865
1866HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1867{
1868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1869
1870 if ( Global::IsOnline(mData->mMachineState)
1871 && mHWData->mVideoCaptureEnabled)
1872 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1873
1874 i_setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1877
1878 return S_OK;
1879}
1880
1881HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1882{
1883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1884 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1885 return S_OK;
1886}
1887
1888HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1889{
1890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 if ( Global::IsOnline(mData->mMachineState)
1893 && mHWData->mVideoCaptureEnabled)
1894 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1895
1896 i_setModified(IsModified_MachineData);
1897 mHWData.backup();
1898 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1899
1900 return S_OK;
1901}
1902
1903HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1904{
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1907 return S_OK;
1908}
1909
1910HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1911{
1912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 if ( Global::IsOnline(mData->mMachineState)
1915 && mHWData->mVideoCaptureEnabled)
1916 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1917
1918 i_setModified(IsModified_MachineData);
1919 mHWData.backup();
1920 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1921
1922 return S_OK;
1923}
1924
1925HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1926{
1927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1928 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1929 return S_OK;
1930}
1931
1932HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1933{
1934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1935
1936 if ( Global::IsOnline(mData->mMachineState)
1937 && mHWData->mVideoCaptureEnabled)
1938 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1939
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1948{
1949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1950 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1951 return S_OK;
1952}
1953
1954HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1955{
1956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 if ( Global::IsOnline(mData->mMachineState)
1959 && mHWData->mVideoCaptureEnabled)
1960 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1961
1962 i_setModified(IsModified_MachineData);
1963 mHWData.backup();
1964 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1965
1966 return S_OK;
1967}
1968
1969HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1970{
1971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1974 return S_OK;
1975}
1976
1977HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1978{
1979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1980
1981 if ( Global::IsOnline(mData->mMachineState)
1982 && mHWData->mVideoCaptureEnabled)
1983 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1984
1985 i_setModified(IsModified_MachineData);
1986 mHWData.backup();
1987 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1988
1989 return S_OK;
1990}
1991
1992HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1993{
1994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
1996 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1997
1998 return S_OK;
1999}
2000
2001HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2002{
2003 switch (aGraphicsControllerType)
2004 {
2005 case GraphicsControllerType_Null:
2006 case GraphicsControllerType_VBoxVGA:
2007#ifdef VBOX_WITH_VMSVGA
2008 case GraphicsControllerType_VMSVGA:
2009#endif
2010 break;
2011 default:
2012 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2013 }
2014
2015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2016
2017 HRESULT rc = i_checkStateDependency(MutableStateDep);
2018 if (FAILED(rc)) return rc;
2019
2020 i_setModified(IsModified_MachineData);
2021 mHWData.backup();
2022 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2023
2024 return S_OK;
2025}
2026
2027HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2028{
2029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 *aVRAMSize = mHWData->mVRAMSize;
2032
2033 return S_OK;
2034}
2035
2036HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2037{
2038 /* check VRAM limits */
2039 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2040 return setError(E_INVALIDARG,
2041 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2042 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2043
2044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 HRESULT rc = i_checkStateDependency(MutableStateDep);
2047 if (FAILED(rc)) return rc;
2048
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mVRAMSize = aVRAMSize;
2052
2053 return S_OK;
2054}
2055
2056/** @todo this method should not be public */
2057HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2058{
2059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2060
2061 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2062
2063 return S_OK;
2064}
2065
2066/**
2067 * Set the memory balloon size.
2068 *
2069 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2070 * we have to make sure that we never call IGuest from here.
2071 */
2072HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2073{
2074 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2075#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2076 /* check limits */
2077 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2078 return setError(E_INVALIDARG,
2079 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2080 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2081
2082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2083
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2087
2088 return S_OK;
2089#else
2090 NOREF(aMemoryBalloonSize);
2091 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2092#endif
2093}
2094
2095HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2096{
2097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2098
2099 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2100 return S_OK;
2101}
2102
2103HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2104{
2105#ifdef VBOX_WITH_PAGE_SHARING
2106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2107
2108 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2109 i_setModified(IsModified_MachineData);
2110 mHWData.backup();
2111 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2112 return S_OK;
2113#else
2114 NOREF(aPageFusionEnabled);
2115 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2116#endif
2117}
2118
2119HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2120{
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2129{
2130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 HRESULT rc = i_checkStateDependency(MutableStateDep);
2133 if (FAILED(rc)) return rc;
2134
2135 /** @todo check validity! */
2136
2137 i_setModified(IsModified_MachineData);
2138 mHWData.backup();
2139 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2140
2141 return S_OK;
2142}
2143
2144
2145HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2146{
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2155{
2156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 HRESULT rc = i_checkStateDependency(MutableStateDep);
2159 if (FAILED(rc)) return rc;
2160
2161 /** @todo check validity! */
2162 i_setModified(IsModified_MachineData);
2163 mHWData.backup();
2164 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2165
2166 return S_OK;
2167}
2168
2169HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2170{
2171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 *aMonitorCount = mHWData->mMonitorCount;
2174
2175 return S_OK;
2176}
2177
2178HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2179{
2180 /* make sure monitor count is a sensible number */
2181 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2182 return setError(E_INVALIDARG,
2183 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2184 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2185
2186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 HRESULT rc = i_checkStateDependency(MutableStateDep);
2189 if (FAILED(rc)) return rc;
2190
2191 i_setModified(IsModified_MachineData);
2192 mHWData.backup();
2193 mHWData->mMonitorCount = aMonitorCount;
2194
2195 return S_OK;
2196}
2197
2198HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2199{
2200 /* mBIOSSettings is constant during life time, no need to lock */
2201 aBIOSSettings = mBIOSSettings;
2202
2203 return S_OK;
2204}
2205
2206HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2207{
2208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2209
2210 switch (aProperty)
2211 {
2212 case CPUPropertyType_PAE:
2213 *aValue = mHWData->mPAEEnabled;
2214 break;
2215
2216 case CPUPropertyType_LongMode:
2217 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2218 *aValue = TRUE;
2219 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2220 *aValue = FALSE;
2221#if HC_ARCH_BITS == 64
2222 else
2223 *aValue = TRUE;
2224#else
2225 else
2226 {
2227 *aValue = FALSE;
2228
2229 ComObjPtr<GuestOSType> pGuestOSType;
2230 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2231 pGuestOSType);
2232 if (SUCCEEDED(hrc2))
2233 {
2234 if (pGuestOSType->i_is64Bit())
2235 {
2236 ComObjPtr<Host> pHost = mParent->i_host();
2237 alock.release();
2238
2239 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2240 if (FAILED(hrc2))
2241 *aValue = FALSE;
2242 }
2243 }
2244 }
2245#endif
2246 break;
2247
2248 case CPUPropertyType_TripleFaultReset:
2249 *aValue = mHWData->mTripleFaultReset;
2250 break;
2251
2252 case CPUPropertyType_APIC:
2253 *aValue = mHWData->mAPIC;
2254 break;
2255
2256 case CPUPropertyType_X2APIC:
2257 *aValue = mHWData->mX2APIC;
2258 break;
2259
2260 default:
2261 return E_INVALIDARG;
2262 }
2263 return S_OK;
2264}
2265
2266HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2267{
2268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2269
2270 HRESULT rc = i_checkStateDependency(MutableStateDep);
2271 if (FAILED(rc)) return rc;
2272
2273 switch (aProperty)
2274 {
2275 case CPUPropertyType_PAE:
2276 i_setModified(IsModified_MachineData);
2277 mHWData.backup();
2278 mHWData->mPAEEnabled = !!aValue;
2279 break;
2280
2281 case CPUPropertyType_LongMode:
2282 i_setModified(IsModified_MachineData);
2283 mHWData.backup();
2284 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2285 break;
2286
2287 case CPUPropertyType_TripleFaultReset:
2288 i_setModified(IsModified_MachineData);
2289 mHWData.backup();
2290 mHWData->mTripleFaultReset = !!aValue;
2291 break;
2292
2293 case CPUPropertyType_APIC:
2294 if (mHWData->mX2APIC)
2295 aValue = TRUE;
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mAPIC = !!aValue;
2299 break;
2300
2301 case CPUPropertyType_X2APIC:
2302 i_setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mX2APIC = !!aValue;
2305 if (aValue)
2306 mHWData->mAPIC = !!aValue;
2307 break;
2308
2309 default:
2310 return E_INVALIDARG;
2311 }
2312 return S_OK;
2313}
2314
2315HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2316{
2317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2318
2319 switch(aId)
2320 {
2321 case 0x0:
2322 case 0x1:
2323 case 0x2:
2324 case 0x3:
2325 case 0x4:
2326 case 0x5:
2327 case 0x6:
2328 case 0x7:
2329 case 0x8:
2330 case 0x9:
2331 case 0xA:
2332 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2333 return E_INVALIDARG;
2334
2335 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2336 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2337 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2338 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2339 break;
2340
2341 case 0x80000000:
2342 case 0x80000001:
2343 case 0x80000002:
2344 case 0x80000003:
2345 case 0x80000004:
2346 case 0x80000005:
2347 case 0x80000006:
2348 case 0x80000007:
2349 case 0x80000008:
2350 case 0x80000009:
2351 case 0x8000000A:
2352 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2353 return E_INVALIDARG;
2354
2355 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2356 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2357 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2358 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2359 break;
2360
2361 default:
2362 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2363 }
2364 return S_OK;
2365}
2366
2367
2368HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2369{
2370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2371
2372 HRESULT rc = i_checkStateDependency(MutableStateDep);
2373 if (FAILED(rc)) return rc;
2374
2375 switch(aId)
2376 {
2377 case 0x0:
2378 case 0x1:
2379 case 0x2:
2380 case 0x3:
2381 case 0x4:
2382 case 0x5:
2383 case 0x6:
2384 case 0x7:
2385 case 0x8:
2386 case 0x9:
2387 case 0xA:
2388 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2389 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2390 i_setModified(IsModified_MachineData);
2391 mHWData.backup();
2392 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2393 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2394 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2395 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2396 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2397 break;
2398
2399 case 0x80000000:
2400 case 0x80000001:
2401 case 0x80000002:
2402 case 0x80000003:
2403 case 0x80000004:
2404 case 0x80000005:
2405 case 0x80000006:
2406 case 0x80000007:
2407 case 0x80000008:
2408 case 0x80000009:
2409 case 0x8000000A:
2410 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2411 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2412 i_setModified(IsModified_MachineData);
2413 mHWData.backup();
2414 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2415 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2416 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2417 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2418 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2419 break;
2420
2421 default:
2422 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2423 }
2424 return S_OK;
2425}
2426
2427HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2428{
2429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2430
2431 HRESULT rc = i_checkStateDependency(MutableStateDep);
2432 if (FAILED(rc)) return rc;
2433
2434 switch(aId)
2435 {
2436 case 0x0:
2437 case 0x1:
2438 case 0x2:
2439 case 0x3:
2440 case 0x4:
2441 case 0x5:
2442 case 0x6:
2443 case 0x7:
2444 case 0x8:
2445 case 0x9:
2446 case 0xA:
2447 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2448 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2449 i_setModified(IsModified_MachineData);
2450 mHWData.backup();
2451 /* Invalidate leaf. */
2452 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2453 break;
2454
2455 case 0x80000000:
2456 case 0x80000001:
2457 case 0x80000002:
2458 case 0x80000003:
2459 case 0x80000004:
2460 case 0x80000005:
2461 case 0x80000006:
2462 case 0x80000007:
2463 case 0x80000008:
2464 case 0x80000009:
2465 case 0x8000000A:
2466 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2467 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2468 i_setModified(IsModified_MachineData);
2469 mHWData.backup();
2470 /* Invalidate leaf. */
2471 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2472 break;
2473
2474 default:
2475 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2476 }
2477 return S_OK;
2478}
2479
2480HRESULT Machine::removeAllCPUIDLeaves()
2481{
2482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 HRESULT rc = i_checkStateDependency(MutableStateDep);
2485 if (FAILED(rc)) return rc;
2486
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489
2490 /* Invalidate all standard leafs. */
2491 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2492 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2493
2494 /* Invalidate all extended leafs. */
2495 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2496 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2497
2498 return S_OK;
2499}
2500HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2501{
2502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2503
2504 switch(aProperty)
2505 {
2506 case HWVirtExPropertyType_Enabled:
2507 *aValue = mHWData->mHWVirtExEnabled;
2508 break;
2509
2510 case HWVirtExPropertyType_VPID:
2511 *aValue = mHWData->mHWVirtExVPIDEnabled;
2512 break;
2513
2514 case HWVirtExPropertyType_NestedPaging:
2515 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2516 break;
2517
2518 case HWVirtExPropertyType_UnrestrictedExecution:
2519 *aValue = mHWData->mHWVirtExUXEnabled;
2520 break;
2521
2522 case HWVirtExPropertyType_LargePages:
2523 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2524#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2525 *aValue = FALSE;
2526#endif
2527 break;
2528
2529 case HWVirtExPropertyType_Force:
2530 *aValue = mHWData->mHWVirtExForceEnabled;
2531 break;
2532
2533 default:
2534 return E_INVALIDARG;
2535 }
2536 return S_OK;
2537}
2538
2539HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2540{
2541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 HRESULT rc = i_checkStateDependency(MutableStateDep);
2544 if (FAILED(rc)) return rc;
2545
2546 switch(aProperty)
2547 {
2548 case HWVirtExPropertyType_Enabled:
2549 i_setModified(IsModified_MachineData);
2550 mHWData.backup();
2551 mHWData->mHWVirtExEnabled = !!aValue;
2552 break;
2553
2554 case HWVirtExPropertyType_VPID:
2555 i_setModified(IsModified_MachineData);
2556 mHWData.backup();
2557 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2558 break;
2559
2560 case HWVirtExPropertyType_NestedPaging:
2561 i_setModified(IsModified_MachineData);
2562 mHWData.backup();
2563 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2564 break;
2565
2566 case HWVirtExPropertyType_UnrestrictedExecution:
2567 i_setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExUXEnabled = !!aValue;
2570 break;
2571
2572 case HWVirtExPropertyType_LargePages:
2573 i_setModified(IsModified_MachineData);
2574 mHWData.backup();
2575 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2576 break;
2577
2578 case HWVirtExPropertyType_Force:
2579 i_setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExForceEnabled = !!aValue;
2582 break;
2583
2584 default:
2585 return E_INVALIDARG;
2586 }
2587
2588 return S_OK;
2589}
2590
2591HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2592{
2593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2594
2595 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2596
2597 return S_OK;
2598}
2599
2600HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2601{
2602 /** @todo (r=dmik):
2603 * 1. Allow to change the name of the snapshot folder containing snapshots
2604 * 2. Rename the folder on disk instead of just changing the property
2605 * value (to be smart and not to leave garbage). Note that it cannot be
2606 * done here because the change may be rolled back. Thus, the right
2607 * place is #saveSettings().
2608 */
2609
2610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 HRESULT rc = i_checkStateDependency(MutableStateDep);
2613 if (FAILED(rc)) return rc;
2614
2615 if (!mData->mCurrentSnapshot.isNull())
2616 return setError(E_FAIL,
2617 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2618
2619 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2620
2621 if (strSnapshotFolder.isEmpty())
2622 strSnapshotFolder = "Snapshots";
2623 int vrc = i_calculateFullPath(strSnapshotFolder,
2624 strSnapshotFolder);
2625 if (RT_FAILURE(vrc))
2626 return setError(E_FAIL,
2627 tr("Invalid snapshot folder '%s' (%Rrc)"),
2628 strSnapshotFolder.c_str(), vrc);
2629
2630 i_setModified(IsModified_MachineData);
2631 mUserData.backup();
2632
2633 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2634
2635 return S_OK;
2636}
2637
2638HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 aMediumAttachments.resize(mMediumAttachments->size());
2643 size_t i = 0;
2644 for (MediumAttachmentList::const_iterator
2645 it = mMediumAttachments->begin();
2646 it != mMediumAttachments->end();
2647 ++it, ++i)
2648 aMediumAttachments[i] = *it;
2649
2650 return S_OK;
2651}
2652
2653HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2654{
2655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2656
2657 Assert(!!mVRDEServer);
2658
2659 aVRDEServer = mVRDEServer;
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 aAudioAdapter = mAudioAdapter;
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2674{
2675#ifdef VBOX_WITH_VUSB
2676 clearError();
2677 MultiResult rc(S_OK);
2678
2679# ifdef VBOX_WITH_USB
2680 rc = mParent->i_host()->i_checkUSBProxyService();
2681 if (FAILED(rc)) return rc;
2682# endif
2683
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 aUSBControllers.resize(mUSBControllers->size());
2687 size_t i = 0;
2688 for (USBControllerList::const_iterator
2689 it = mUSBControllers->begin();
2690 it != mUSBControllers->end();
2691 ++it, ++i)
2692 aUSBControllers[i] = *it;
2693
2694 return S_OK;
2695#else
2696 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2697 * extended error info to indicate that USB is simply not available
2698 * (w/o treating it as a failure), for example, as in OSE */
2699 NOREF(aUSBControllers);
2700 ReturnComNotImplemented();
2701#endif /* VBOX_WITH_VUSB */
2702}
2703
2704HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2705{
2706#ifdef VBOX_WITH_VUSB
2707 clearError();
2708 MultiResult rc(S_OK);
2709
2710# ifdef VBOX_WITH_USB
2711 rc = mParent->i_host()->i_checkUSBProxyService();
2712 if (FAILED(rc)) return rc;
2713# endif
2714
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 aUSBDeviceFilters = mUSBDeviceFilters;
2718 return rc;
2719#else
2720 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2721 * extended error info to indicate that USB is simply not available
2722 * (w/o treating it as a failure), for example, as in OSE */
2723 NOREF(aUSBDeviceFilters);
2724 ReturnComNotImplemented();
2725#endif /* VBOX_WITH_VUSB */
2726}
2727
2728HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aSettingsFilePath = mData->m_strConfigFileFull;
2733
2734 return S_OK;
2735}
2736
2737HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2738{
2739 RT_NOREF(aSettingsFilePath);
2740 ReturnComNotImplemented();
2741}
2742
2743HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2744{
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2748 if (FAILED(rc)) return rc;
2749
2750 if (!mData->pMachineConfigFile->fileExists())
2751 // this is a new machine, and no config file exists yet:
2752 *aSettingsModified = TRUE;
2753 else
2754 *aSettingsModified = (mData->flModifications != 0);
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 *aSessionState = mData->mSession.mState;
2764
2765 return S_OK;
2766}
2767
2768HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771
2772 aSessionName = mData->mSession.mName;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2778{
2779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 *aSessionPID = mData->mSession.mPID;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::getState(MachineState_T *aState)
2787{
2788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 *aState = mData->mMachineState;
2791 Assert(mData->mMachineState != MachineState_Null);
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2797{
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2801
2802 return S_OK;
2803}
2804
2805HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2806{
2807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2808
2809 aStateFilePath = mSSData->strStateFilePath;
2810
2811 return S_OK;
2812}
2813
2814HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2815{
2816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2817
2818 i_getLogFolder(aLogFolder);
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2824{
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 aCurrentSnapshot = mData->mCurrentSnapshot;
2828
2829 return S_OK;
2830}
2831
2832HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2833{
2834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2835
2836 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2837 ? 0
2838 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2839
2840 return S_OK;
2841}
2842
2843HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2844{
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 /* Note: for machines with no snapshots, we always return FALSE
2848 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2849 * reasons :) */
2850
2851 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2852 ? FALSE
2853 : mData->mCurrentStateModified;
2854
2855 return S_OK;
2856}
2857
2858HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2859{
2860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 aSharedFolders.resize(mHWData->mSharedFolders.size());
2863 size_t i = 0;
2864 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2865 it = mHWData->mSharedFolders.begin();
2866 it != mHWData->mSharedFolders.end();
2867 ++it, ++i)
2868 aSharedFolders[i] = *it;
2869
2870 return S_OK;
2871}
2872
2873HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2874{
2875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 *aClipboardMode = mHWData->mClipboardMode;
2878
2879 return S_OK;
2880}
2881
2882HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2883{
2884 HRESULT rc = S_OK;
2885
2886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 alock.release();
2889 rc = i_onClipboardModeChange(aClipboardMode);
2890 alock.acquire();
2891 if (FAILED(rc)) return rc;
2892
2893 i_setModified(IsModified_MachineData);
2894 mHWData.backup();
2895 mHWData->mClipboardMode = aClipboardMode;
2896
2897 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2898 if (Global::IsOnline(mData->mMachineState))
2899 i_saveSettings(NULL);
2900
2901 return S_OK;
2902}
2903
2904HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2905{
2906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 *aDnDMode = mHWData->mDnDMode;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2914{
2915 HRESULT rc = S_OK;
2916
2917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 alock.release();
2920 rc = i_onDnDModeChange(aDnDMode);
2921
2922 alock.acquire();
2923 if (FAILED(rc)) return rc;
2924
2925 i_setModified(IsModified_MachineData);
2926 mHWData.backup();
2927 mHWData->mDnDMode = aDnDMode;
2928
2929 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2930 if (Global::IsOnline(mData->mMachineState))
2931 i_saveSettings(NULL);
2932
2933 return S_OK;
2934}
2935
2936HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2937{
2938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 aStorageControllers.resize(mStorageControllers->size());
2941 size_t i = 0;
2942 for (StorageControllerList::const_iterator
2943 it = mStorageControllers->begin();
2944 it != mStorageControllers->end();
2945 ++it, ++i)
2946 aStorageControllers[i] = *it;
2947
2948 return S_OK;
2949}
2950
2951HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2952{
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 *aEnabled = mUserData->s.fTeleporterEnabled;
2956
2957 return S_OK;
2958}
2959
2960HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2961{
2962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2963
2964 /* Only allow it to be set to true when PoweredOff or Aborted.
2965 (Clearing it is always permitted.) */
2966 if ( aTeleporterEnabled
2967 && mData->mRegistered
2968 && ( !i_isSessionMachine()
2969 || ( mData->mMachineState != MachineState_PoweredOff
2970 && mData->mMachineState != MachineState_Teleported
2971 && mData->mMachineState != MachineState_Aborted
2972 )
2973 )
2974 )
2975 return setError(VBOX_E_INVALID_VM_STATE,
2976 tr("The machine is not powered off (state is %s)"),
2977 Global::stringifyMachineState(mData->mMachineState));
2978
2979 i_setModified(IsModified_MachineData);
2980 mUserData.backup();
2981 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2982
2983 return S_OK;
2984}
2985
2986HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2987{
2988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2991
2992 return S_OK;
2993}
2994
2995HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2996{
2997 if (aTeleporterPort >= _64K)
2998 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2999
3000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3001
3002 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3003 if (FAILED(rc)) return rc;
3004
3005 i_setModified(IsModified_MachineData);
3006 mUserData.backup();
3007 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3008
3009 return S_OK;
3010}
3011
3012HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3013{
3014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3015
3016 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3017
3018 return S_OK;
3019}
3020
3021HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3022{
3023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3026 if (FAILED(rc)) return rc;
3027
3028 i_setModified(IsModified_MachineData);
3029 mUserData.backup();
3030 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3031
3032 return S_OK;
3033}
3034
3035HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3036{
3037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3038 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3039
3040 return S_OK;
3041}
3042
3043HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3044{
3045 /*
3046 * Hash the password first.
3047 */
3048 com::Utf8Str aT = aTeleporterPassword;
3049
3050 if (!aT.isEmpty())
3051 {
3052 if (VBoxIsPasswordHashed(&aT))
3053 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3054 VBoxHashPassword(&aT);
3055 }
3056
3057 /*
3058 * Do the update.
3059 */
3060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3061 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3062 if (SUCCEEDED(hrc))
3063 {
3064 i_setModified(IsModified_MachineData);
3065 mUserData.backup();
3066 mUserData->s.strTeleporterPassword = aT;
3067 }
3068
3069 return hrc;
3070}
3071
3072HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3073{
3074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3075
3076 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3077 return S_OK;
3078}
3079
3080HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3081{
3082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3083
3084 /** @todo deal with running state change. */
3085 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3086 if (FAILED(rc)) return rc;
3087
3088 i_setModified(IsModified_MachineData);
3089 mUserData.backup();
3090 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3091 return S_OK;
3092}
3093
3094HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3095{
3096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3097
3098 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3099 return S_OK;
3100}
3101
3102HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3103{
3104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3105
3106 /** @todo deal with running state change. */
3107 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3108 if (FAILED(rc)) return rc;
3109
3110 i_setModified(IsModified_MachineData);
3111 mUserData.backup();
3112 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3113 return S_OK;
3114}
3115
3116HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3117{
3118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3119
3120 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3121 return S_OK;
3122}
3123
3124HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3125{
3126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3127
3128 /** @todo deal with running state change. */
3129 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3130 if (FAILED(rc)) return rc;
3131
3132 i_setModified(IsModified_MachineData);
3133 mUserData.backup();
3134 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3135 return S_OK;
3136}
3137
3138HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3139{
3140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3141
3142 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3143
3144 return S_OK;
3145}
3146
3147HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3148{
3149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 /** @todo deal with running state change. */
3152 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3153 if (FAILED(rc)) return rc;
3154
3155 i_setModified(IsModified_MachineData);
3156 mUserData.backup();
3157 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3158
3159 return S_OK;
3160}
3161
3162HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3163{
3164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3165
3166 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3167 return S_OK;
3168}
3169
3170HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3171{
3172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3173
3174 /** @todo deal with running state change. */
3175 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3176 if (FAILED(rc)) return rc;
3177
3178 i_setModified(IsModified_MachineData);
3179 mUserData.backup();
3180 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3181 return S_OK;
3182}
3183
3184HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3185{
3186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3187
3188 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3189
3190 return S_OK;
3191}
3192
3193HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3194{
3195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3196
3197 /* Only allow it to be set to true when PoweredOff or Aborted.
3198 (Clearing it is always permitted.) */
3199 if ( aRTCUseUTC
3200 && mData->mRegistered
3201 && ( !i_isSessionMachine()
3202 || ( mData->mMachineState != MachineState_PoweredOff
3203 && mData->mMachineState != MachineState_Teleported
3204 && mData->mMachineState != MachineState_Aborted
3205 )
3206 )
3207 )
3208 return setError(VBOX_E_INVALID_VM_STATE,
3209 tr("The machine is not powered off (state is %s)"),
3210 Global::stringifyMachineState(mData->mMachineState));
3211
3212 i_setModified(IsModified_MachineData);
3213 mUserData.backup();
3214 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3215
3216 return S_OK;
3217}
3218
3219HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3220{
3221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3222
3223 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3224
3225 return S_OK;
3226}
3227
3228HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3229{
3230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3231
3232 HRESULT rc = i_checkStateDependency(MutableStateDep);
3233 if (FAILED(rc)) return rc;
3234
3235 i_setModified(IsModified_MachineData);
3236 mHWData.backup();
3237 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3238
3239 return S_OK;
3240}
3241
3242HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3243{
3244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3245
3246 *aIOCacheSize = mHWData->mIOCacheSize;
3247
3248 return S_OK;
3249}
3250
3251HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3252{
3253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3254
3255 HRESULT rc = i_checkStateDependency(MutableStateDep);
3256 if (FAILED(rc)) return rc;
3257
3258 i_setModified(IsModified_MachineData);
3259 mHWData.backup();
3260 mHWData->mIOCacheSize = aIOCacheSize;
3261
3262 return S_OK;
3263}
3264
3265
3266/**
3267 * @note Locks objects!
3268 */
3269HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3270 LockType_T aLockType)
3271{
3272 /* check the session state */
3273 SessionState_T state;
3274 HRESULT rc = aSession->COMGETTER(State)(&state);
3275 if (FAILED(rc)) return rc;
3276
3277 if (state != SessionState_Unlocked)
3278 return setError(VBOX_E_INVALID_OBJECT_STATE,
3279 tr("The given session is busy"));
3280
3281 // get the client's IInternalSessionControl interface
3282 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3283 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3284 E_INVALIDARG);
3285
3286 // session name (only used in some code paths)
3287 Utf8Str strSessionName;
3288
3289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3290
3291 if (!mData->mRegistered)
3292 return setError(E_UNEXPECTED,
3293 tr("The machine '%s' is not registered"),
3294 mUserData->s.strName.c_str());
3295
3296 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3297
3298 SessionState_T oldState = mData->mSession.mState;
3299 /* Hack: in case the session is closing and there is a progress object
3300 * which allows waiting for the session to be closed, take the opportunity
3301 * and do a limited wait (max. 1 second). This helps a lot when the system
3302 * is busy and thus session closing can take a little while. */
3303 if ( mData->mSession.mState == SessionState_Unlocking
3304 && mData->mSession.mProgress)
3305 {
3306 alock.release();
3307 mData->mSession.mProgress->WaitForCompletion(1000);
3308 alock.acquire();
3309 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3310 }
3311
3312 // try again now
3313 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3314 // (i.e. session machine exists)
3315 && (aLockType == LockType_Shared) // caller wants a shared link to the
3316 // existing session that holds the write lock:
3317 )
3318 {
3319 // OK, share the session... we are now dealing with three processes:
3320 // 1) VBoxSVC (where this code runs);
3321 // 2) process C: the caller's client process (who wants a shared session);
3322 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3323
3324 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3325 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3326 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3327 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3328 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3329
3330 /*
3331 * Release the lock before calling the client process. It's safe here
3332 * since the only thing to do after we get the lock again is to add
3333 * the remote control to the list (which doesn't directly influence
3334 * anything).
3335 */
3336 alock.release();
3337
3338 // get the console of the session holding the write lock (this is a remote call)
3339 ComPtr<IConsole> pConsoleW;
3340 if (mData->mSession.mLockType == LockType_VM)
3341 {
3342 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3343 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3344 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3345 if (FAILED(rc))
3346 // the failure may occur w/o any error info (from RPC), so provide one
3347 return setError(VBOX_E_VM_ERROR,
3348 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3349 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3350 }
3351
3352 // share the session machine and W's console with the caller's session
3353 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3354 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3355 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3356
3357 if (FAILED(rc))
3358 // the failure may occur w/o any error info (from RPC), so provide one
3359 return setError(VBOX_E_VM_ERROR,
3360 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3361 alock.acquire();
3362
3363 // need to revalidate the state after acquiring the lock again
3364 if (mData->mSession.mState != SessionState_Locked)
3365 {
3366 pSessionControl->Uninitialize();
3367 return setError(VBOX_E_INVALID_SESSION_STATE,
3368 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3369 mUserData->s.strName.c_str());
3370 }
3371
3372 // add the caller's session to the list
3373 mData->mSession.mRemoteControls.push_back(pSessionControl);
3374 }
3375 else if ( mData->mSession.mState == SessionState_Locked
3376 || mData->mSession.mState == SessionState_Unlocking
3377 )
3378 {
3379 // sharing not permitted, or machine still unlocking:
3380 return setError(VBOX_E_INVALID_OBJECT_STATE,
3381 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3382 mUserData->s.strName.c_str());
3383 }
3384 else
3385 {
3386 // machine is not locked: then write-lock the machine (create the session machine)
3387
3388 // must not be busy
3389 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3390
3391 // get the caller's session PID
3392 RTPROCESS pid = NIL_RTPROCESS;
3393 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3394 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3395 Assert(pid != NIL_RTPROCESS);
3396
3397 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3398
3399 if (fLaunchingVMProcess)
3400 {
3401 if (mData->mSession.mPID == NIL_RTPROCESS)
3402 {
3403 // two or more clients racing for a lock, the one which set the
3404 // session state to Spawning will win, the others will get an
3405 // error as we can't decide here if waiting a little would help
3406 // (only for shared locks this would avoid an error)
3407 return setError(VBOX_E_INVALID_OBJECT_STATE,
3408 tr("The machine '%s' already has a lock request pending"),
3409 mUserData->s.strName.c_str());
3410 }
3411
3412 // this machine is awaiting for a spawning session to be opened:
3413 // then the calling process must be the one that got started by
3414 // LaunchVMProcess()
3415
3416 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3417 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3418
3419#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3420 /* Hardened windows builds spawns three processes when a VM is
3421 launched, the 3rd one is the one that will end up here. */
3422 RTPROCESS ppid;
3423 int rc = RTProcQueryParent(pid, &ppid);
3424 if (RT_SUCCESS(rc))
3425 rc = RTProcQueryParent(ppid, &ppid);
3426 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3427 || rc == VERR_ACCESS_DENIED)
3428 {
3429 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3430 mData->mSession.mPID = pid;
3431 }
3432#endif
3433
3434 if (mData->mSession.mPID != pid)
3435 return setError(E_ACCESSDENIED,
3436 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3437 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3438 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3439 }
3440
3441 // create the mutable SessionMachine from the current machine
3442 ComObjPtr<SessionMachine> sessionMachine;
3443 sessionMachine.createObject();
3444 rc = sessionMachine->init(this);
3445 AssertComRC(rc);
3446
3447 /* NOTE: doing return from this function after this point but
3448 * before the end is forbidden since it may call SessionMachine::uninit()
3449 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3450 * lock while still holding the Machine lock in alock so that a deadlock
3451 * is possible due to the wrong lock order. */
3452
3453 if (SUCCEEDED(rc))
3454 {
3455 /*
3456 * Set the session state to Spawning to protect against subsequent
3457 * attempts to open a session and to unregister the machine after
3458 * we release the lock.
3459 */
3460 SessionState_T origState = mData->mSession.mState;
3461 mData->mSession.mState = SessionState_Spawning;
3462
3463#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3464 /* Get the client token ID to be passed to the client process */
3465 Utf8Str strTokenId;
3466 sessionMachine->i_getTokenId(strTokenId);
3467 Assert(!strTokenId.isEmpty());
3468#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3469 /* Get the client token to be passed to the client process */
3470 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3471 /* The token is now "owned" by pToken, fix refcount */
3472 if (!pToken.isNull())
3473 pToken->Release();
3474#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3475
3476 /*
3477 * Release the lock before calling the client process -- it will call
3478 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3479 * because the state is Spawning, so that LaunchVMProcess() and
3480 * LockMachine() calls will fail. This method, called before we
3481 * acquire the lock again, will fail because of the wrong PID.
3482 *
3483 * Note that mData->mSession.mRemoteControls accessed outside
3484 * the lock may not be modified when state is Spawning, so it's safe.
3485 */
3486 alock.release();
3487
3488 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3489#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3490 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3491#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3492 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3493 /* Now the token is owned by the client process. */
3494 pToken.setNull();
3495#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3496 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3497
3498 /* The failure may occur w/o any error info (from RPC), so provide one */
3499 if (FAILED(rc))
3500 setError(VBOX_E_VM_ERROR,
3501 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3502
3503 // get session name, either to remember or to compare against
3504 // the already known session name.
3505 {
3506 Bstr bstrSessionName;
3507 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3508 if (SUCCEEDED(rc2))
3509 strSessionName = bstrSessionName;
3510 }
3511
3512 if ( SUCCEEDED(rc)
3513 && fLaunchingVMProcess
3514 )
3515 {
3516 /* complete the remote session initialization */
3517
3518 /* get the console from the direct session */
3519 ComPtr<IConsole> console;
3520 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3521 ComAssertComRC(rc);
3522
3523 if (SUCCEEDED(rc) && !console)
3524 {
3525 ComAssert(!!console);
3526 rc = E_FAIL;
3527 }
3528
3529 /* assign machine & console to the remote session */
3530 if (SUCCEEDED(rc))
3531 {
3532 /*
3533 * after LaunchVMProcess(), the first and the only
3534 * entry in remoteControls is that remote session
3535 */
3536 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3537 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3538 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3539
3540 /* The failure may occur w/o any error info (from RPC), so provide one */
3541 if (FAILED(rc))
3542 setError(VBOX_E_VM_ERROR,
3543 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3544 }
3545
3546 if (FAILED(rc))
3547 pSessionControl->Uninitialize();
3548 }
3549
3550 /* acquire the lock again */
3551 alock.acquire();
3552
3553 /* Restore the session state */
3554 mData->mSession.mState = origState;
3555 }
3556
3557 // finalize spawning anyway (this is why we don't return on errors above)
3558 if (fLaunchingVMProcess)
3559 {
3560 Assert(mData->mSession.mName == strSessionName);
3561 /* Note that the progress object is finalized later */
3562 /** @todo Consider checking mData->mSession.mProgress for cancellation
3563 * around here. */
3564
3565 /* We don't reset mSession.mPID here because it is necessary for
3566 * SessionMachine::uninit() to reap the child process later. */
3567
3568 if (FAILED(rc))
3569 {
3570 /* Close the remote session, remove the remote control from the list
3571 * and reset session state to Closed (@note keep the code in sync
3572 * with the relevant part in checkForSpawnFailure()). */
3573
3574 Assert(mData->mSession.mRemoteControls.size() == 1);
3575 if (mData->mSession.mRemoteControls.size() == 1)
3576 {
3577 ErrorInfoKeeper eik;
3578 mData->mSession.mRemoteControls.front()->Uninitialize();
3579 }
3580
3581 mData->mSession.mRemoteControls.clear();
3582 mData->mSession.mState = SessionState_Unlocked;
3583 }
3584 }
3585 else
3586 {
3587 /* memorize PID of the directly opened session */
3588 if (SUCCEEDED(rc))
3589 mData->mSession.mPID = pid;
3590 }
3591
3592 if (SUCCEEDED(rc))
3593 {
3594 mData->mSession.mLockType = aLockType;
3595 /* memorize the direct session control and cache IUnknown for it */
3596 mData->mSession.mDirectControl = pSessionControl;
3597 mData->mSession.mState = SessionState_Locked;
3598 if (!fLaunchingVMProcess)
3599 mData->mSession.mName = strSessionName;
3600 /* associate the SessionMachine with this Machine */
3601 mData->mSession.mMachine = sessionMachine;
3602
3603 /* request an IUnknown pointer early from the remote party for later
3604 * identity checks (it will be internally cached within mDirectControl
3605 * at least on XPCOM) */
3606 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3607 NOREF(unk);
3608 }
3609
3610 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3611 * would break the lock order */
3612 alock.release();
3613
3614 /* uninitialize the created session machine on failure */
3615 if (FAILED(rc))
3616 sessionMachine->uninit();
3617 }
3618
3619 if (SUCCEEDED(rc))
3620 {
3621 /*
3622 * tell the client watcher thread to update the set of
3623 * machines that have open sessions
3624 */
3625 mParent->i_updateClientWatcher();
3626
3627 if (oldState != SessionState_Locked)
3628 /* fire an event */
3629 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3630 }
3631
3632 return rc;
3633}
3634
3635/**
3636 * @note Locks objects!
3637 */
3638HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3639 const com::Utf8Str &aName,
3640 const com::Utf8Str &aEnvironment,
3641 ComPtr<IProgress> &aProgress)
3642{
3643 Utf8Str strFrontend(aName);
3644 /* "emergencystop" doesn't need the session, so skip the checks/interface
3645 * retrieval. This code doesn't quite fit in here, but introducing a
3646 * special API method would be even more effort, and would require explicit
3647 * support by every API client. It's better to hide the feature a bit. */
3648 if (strFrontend != "emergencystop")
3649 CheckComArgNotNull(aSession);
3650
3651 HRESULT rc = S_OK;
3652 if (strFrontend.isEmpty())
3653 {
3654 Bstr bstrFrontend;
3655 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3656 if (FAILED(rc))
3657 return rc;
3658 strFrontend = bstrFrontend;
3659 if (strFrontend.isEmpty())
3660 {
3661 ComPtr<ISystemProperties> systemProperties;
3662 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3663 if (FAILED(rc))
3664 return rc;
3665 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3666 if (FAILED(rc))
3667 return rc;
3668 strFrontend = bstrFrontend;
3669 }
3670 /* paranoia - emergencystop is not a valid default */
3671 if (strFrontend == "emergencystop")
3672 strFrontend = Utf8Str::Empty;
3673 }
3674 /* default frontend: Qt GUI */
3675 if (strFrontend.isEmpty())
3676 strFrontend = "GUI/Qt";
3677
3678 if (strFrontend != "emergencystop")
3679 {
3680 /* check the session state */
3681 SessionState_T state;
3682 rc = aSession->COMGETTER(State)(&state);
3683 if (FAILED(rc))
3684 return rc;
3685
3686 if (state != SessionState_Unlocked)
3687 return setError(VBOX_E_INVALID_OBJECT_STATE,
3688 tr("The given session is busy"));
3689
3690 /* get the IInternalSessionControl interface */
3691 ComPtr<IInternalSessionControl> control(aSession);
3692 ComAssertMsgRet(!control.isNull(),
3693 ("No IInternalSessionControl interface"),
3694 E_INVALIDARG);
3695
3696 /* get the teleporter enable state for the progress object init. */
3697 BOOL fTeleporterEnabled;
3698 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3699 if (FAILED(rc))
3700 return rc;
3701
3702 /* create a progress object */
3703 ComObjPtr<ProgressProxy> progress;
3704 progress.createObject();
3705 rc = progress->init(mParent,
3706 static_cast<IMachine*>(this),
3707 Bstr(tr("Starting VM")).raw(),
3708 TRUE /* aCancelable */,
3709 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3710 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3711 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3712 2 /* uFirstOperationWeight */,
3713 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3714
3715 if (SUCCEEDED(rc))
3716 {
3717 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3718 if (SUCCEEDED(rc))
3719 {
3720 aProgress = progress;
3721
3722 /* signal the client watcher thread */
3723 mParent->i_updateClientWatcher();
3724
3725 /* fire an event */
3726 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3727 }
3728 }
3729 }
3730 else
3731 {
3732 /* no progress object - either instant success or failure */
3733 aProgress = NULL;
3734
3735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3736
3737 if (mData->mSession.mState != SessionState_Locked)
3738 return setError(VBOX_E_INVALID_OBJECT_STATE,
3739 tr("The machine '%s' is not locked by a session"),
3740 mUserData->s.strName.c_str());
3741
3742 /* must have a VM process associated - do not kill normal API clients
3743 * with an open session */
3744 if (!Global::IsOnline(mData->mMachineState))
3745 return setError(VBOX_E_INVALID_OBJECT_STATE,
3746 tr("The machine '%s' does not have a VM process"),
3747 mUserData->s.strName.c_str());
3748
3749 /* forcibly terminate the VM process */
3750 if (mData->mSession.mPID != NIL_RTPROCESS)
3751 RTProcTerminate(mData->mSession.mPID);
3752
3753 /* signal the client watcher thread, as most likely the client has
3754 * been terminated */
3755 mParent->i_updateClientWatcher();
3756 }
3757
3758 return rc;
3759}
3760
3761HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3762{
3763 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3764 return setError(E_INVALIDARG,
3765 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3766 aPosition, SchemaDefs::MaxBootPosition);
3767
3768 if (aDevice == DeviceType_USB)
3769 return setError(E_NOTIMPL,
3770 tr("Booting from USB device is currently not supported"));
3771
3772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3773
3774 HRESULT rc = i_checkStateDependency(MutableStateDep);
3775 if (FAILED(rc)) return rc;
3776
3777 i_setModified(IsModified_MachineData);
3778 mHWData.backup();
3779 mHWData->mBootOrder[aPosition - 1] = aDevice;
3780
3781 return S_OK;
3782}
3783
3784HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3785{
3786 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3787 return setError(E_INVALIDARG,
3788 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3789 aPosition, SchemaDefs::MaxBootPosition);
3790
3791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3792
3793 *aDevice = mHWData->mBootOrder[aPosition - 1];
3794
3795 return S_OK;
3796}
3797
3798HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3799 LONG aControllerPort,
3800 LONG aDevice,
3801 DeviceType_T aType,
3802 const ComPtr<IMedium> &aMedium)
3803{
3804 IMedium *aM = aMedium;
3805 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3806 aName.c_str(), aControllerPort, aDevice, aType, aM));
3807
3808 // request the host lock first, since might be calling Host methods for getting host drives;
3809 // next, protect the media tree all the while we're in here, as well as our member variables
3810 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3811 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3812
3813 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3814 if (FAILED(rc)) return rc;
3815
3816 /// @todo NEWMEDIA implicit machine registration
3817 if (!mData->mRegistered)
3818 return setError(VBOX_E_INVALID_OBJECT_STATE,
3819 tr("Cannot attach storage devices to an unregistered machine"));
3820
3821 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3822
3823 /* Check for an existing controller. */
3824 ComObjPtr<StorageController> ctl;
3825 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3826 if (FAILED(rc)) return rc;
3827
3828 StorageControllerType_T ctrlType;
3829 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3830 if (FAILED(rc))
3831 return setError(E_FAIL,
3832 tr("Could not get type of controller '%s'"),
3833 aName.c_str());
3834
3835 bool fSilent = false;
3836 Utf8Str strReconfig;
3837
3838 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3839 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3840 if ( mData->mMachineState == MachineState_Paused
3841 && strReconfig == "1")
3842 fSilent = true;
3843
3844 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3845 bool fHotplug = false;
3846 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3847 fHotplug = true;
3848
3849 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3850 return setError(VBOX_E_INVALID_VM_STATE,
3851 tr("Controller '%s' does not support hotplugging"),
3852 aName.c_str());
3853
3854 // check that the port and device are not out of range
3855 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3856 if (FAILED(rc)) return rc;
3857
3858 /* check if the device slot is already busy */
3859 MediumAttachment *pAttachTemp;
3860 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3861 aName,
3862 aControllerPort,
3863 aDevice)))
3864 {
3865 Medium *pMedium = pAttachTemp->i_getMedium();
3866 if (pMedium)
3867 {
3868 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3869 return setError(VBOX_E_OBJECT_IN_USE,
3870 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3871 pMedium->i_getLocationFull().c_str(),
3872 aControllerPort,
3873 aDevice,
3874 aName.c_str());
3875 }
3876 else
3877 return setError(VBOX_E_OBJECT_IN_USE,
3878 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3879 aControllerPort, aDevice, aName.c_str());
3880 }
3881
3882 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3883 if (aMedium && medium.isNull())
3884 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3885
3886 AutoCaller mediumCaller(medium);
3887 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3888
3889 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3890
3891 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3892 && !medium.isNull()
3893 )
3894 return setError(VBOX_E_OBJECT_IN_USE,
3895 tr("Medium '%s' is already attached to this virtual machine"),
3896 medium->i_getLocationFull().c_str());
3897
3898 if (!medium.isNull())
3899 {
3900 MediumType_T mtype = medium->i_getType();
3901 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3902 // For DVDs it's not written to the config file, so needs no global config
3903 // version bump. For floppies it's a new attribute "type", which is ignored
3904 // by older VirtualBox version, so needs no global config version bump either.
3905 // For hard disks this type is not accepted.
3906 if (mtype == MediumType_MultiAttach)
3907 {
3908 // This type is new with VirtualBox 4.0 and therefore requires settings
3909 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3910 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3911 // two reasons: The medium type is a property of the media registry tree, which
3912 // can reside in the global config file (for pre-4.0 media); we would therefore
3913 // possibly need to bump the global config version. We don't want to do that though
3914 // because that might make downgrading to pre-4.0 impossible.
3915 // As a result, we can only use these two new types if the medium is NOT in the
3916 // global registry:
3917 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3918 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3919 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3920 )
3921 return setError(VBOX_E_INVALID_OBJECT_STATE,
3922 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3923 "to machines that were created with VirtualBox 4.0 or later"),
3924 medium->i_getLocationFull().c_str());
3925 }
3926 }
3927
3928 bool fIndirect = false;
3929 if (!medium.isNull())
3930 fIndirect = medium->i_isReadOnly();
3931 bool associate = true;
3932
3933 do
3934 {
3935 if ( aType == DeviceType_HardDisk
3936 && mMediumAttachments.isBackedUp())
3937 {
3938 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3939
3940 /* check if the medium was attached to the VM before we started
3941 * changing attachments in which case the attachment just needs to
3942 * be restored */
3943 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3944 {
3945 AssertReturn(!fIndirect, E_FAIL);
3946
3947 /* see if it's the same bus/channel/device */
3948 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3949 {
3950 /* the simplest case: restore the whole attachment
3951 * and return, nothing else to do */
3952 mMediumAttachments->push_back(pAttachTemp);
3953
3954 /* Reattach the medium to the VM. */
3955 if (fHotplug || fSilent)
3956 {
3957 mediumLock.release();
3958 treeLock.release();
3959 alock.release();
3960
3961 MediumLockList *pMediumLockList(new MediumLockList());
3962
3963 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3964 medium /* pToLockWrite */,
3965 false /* fMediumLockWriteAll */,
3966 NULL,
3967 *pMediumLockList);
3968 alock.acquire();
3969 if (FAILED(rc))
3970 delete pMediumLockList;
3971 else
3972 {
3973 mData->mSession.mLockedMedia.Unlock();
3974 alock.release();
3975 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3976 mData->mSession.mLockedMedia.Lock();
3977 alock.acquire();
3978 }
3979 alock.release();
3980
3981 if (SUCCEEDED(rc))
3982 {
3983 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3984 /* Remove lock list in case of error. */
3985 if (FAILED(rc))
3986 {
3987 mData->mSession.mLockedMedia.Unlock();
3988 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3989 mData->mSession.mLockedMedia.Lock();
3990 }
3991 }
3992 }
3993
3994 return S_OK;
3995 }
3996
3997 /* bus/channel/device differ; we need a new attachment object,
3998 * but don't try to associate it again */
3999 associate = false;
4000 break;
4001 }
4002 }
4003
4004 /* go further only if the attachment is to be indirect */
4005 if (!fIndirect)
4006 break;
4007
4008 /* perform the so called smart attachment logic for indirect
4009 * attachments. Note that smart attachment is only applicable to base
4010 * hard disks. */
4011
4012 if (medium->i_getParent().isNull())
4013 {
4014 /* first, investigate the backup copy of the current hard disk
4015 * attachments to make it possible to re-attach existing diffs to
4016 * another device slot w/o losing their contents */
4017 if (mMediumAttachments.isBackedUp())
4018 {
4019 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4020
4021 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4022 uint32_t foundLevel = 0;
4023
4024 for (MediumAttachmentList::const_iterator
4025 it = oldAtts.begin();
4026 it != oldAtts.end();
4027 ++it)
4028 {
4029 uint32_t level = 0;
4030 MediumAttachment *pAttach = *it;
4031 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4032 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4033 if (pMedium.isNull())
4034 continue;
4035
4036 if (pMedium->i_getBase(&level) == medium)
4037 {
4038 /* skip the hard disk if its currently attached (we
4039 * cannot attach the same hard disk twice) */
4040 if (i_findAttachment(*mMediumAttachments.data(),
4041 pMedium))
4042 continue;
4043
4044 /* matched device, channel and bus (i.e. attached to the
4045 * same place) will win and immediately stop the search;
4046 * otherwise the attachment that has the youngest
4047 * descendant of medium will be used
4048 */
4049 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4050 {
4051 /* the simplest case: restore the whole attachment
4052 * and return, nothing else to do */
4053 mMediumAttachments->push_back(*it);
4054
4055 /* Reattach the medium to the VM. */
4056 if (fHotplug || fSilent)
4057 {
4058 mediumLock.release();
4059 treeLock.release();
4060 alock.release();
4061
4062 MediumLockList *pMediumLockList(new MediumLockList());
4063
4064 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4065 medium /* pToLockWrite */,
4066 false /* fMediumLockWriteAll */,
4067 NULL,
4068 *pMediumLockList);
4069 alock.acquire();
4070 if (FAILED(rc))
4071 delete pMediumLockList;
4072 else
4073 {
4074 mData->mSession.mLockedMedia.Unlock();
4075 alock.release();
4076 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4077 mData->mSession.mLockedMedia.Lock();
4078 alock.acquire();
4079 }
4080 alock.release();
4081
4082 if (SUCCEEDED(rc))
4083 {
4084 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4085 /* Remove lock list in case of error. */
4086 if (FAILED(rc))
4087 {
4088 mData->mSession.mLockedMedia.Unlock();
4089 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4090 mData->mSession.mLockedMedia.Lock();
4091 }
4092 }
4093 }
4094
4095 return S_OK;
4096 }
4097 else if ( foundIt == oldAtts.end()
4098 || level > foundLevel /* prefer younger */
4099 )
4100 {
4101 foundIt = it;
4102 foundLevel = level;
4103 }
4104 }
4105 }
4106
4107 if (foundIt != oldAtts.end())
4108 {
4109 /* use the previously attached hard disk */
4110 medium = (*foundIt)->i_getMedium();
4111 mediumCaller.attach(medium);
4112 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4113 mediumLock.attach(medium);
4114 /* not implicit, doesn't require association with this VM */
4115 fIndirect = false;
4116 associate = false;
4117 /* go right to the MediumAttachment creation */
4118 break;
4119 }
4120 }
4121
4122 /* must give up the medium lock and medium tree lock as below we
4123 * go over snapshots, which needs a lock with higher lock order. */
4124 mediumLock.release();
4125 treeLock.release();
4126
4127 /* then, search through snapshots for the best diff in the given
4128 * hard disk's chain to base the new diff on */
4129
4130 ComObjPtr<Medium> base;
4131 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4132 while (snap)
4133 {
4134 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4135
4136 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4137
4138 MediumAttachment *pAttachFound = NULL;
4139 uint32_t foundLevel = 0;
4140
4141 for (MediumAttachmentList::const_iterator
4142 it = snapAtts.begin();
4143 it != snapAtts.end();
4144 ++it)
4145 {
4146 MediumAttachment *pAttach = *it;
4147 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4148 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4149 if (pMedium.isNull())
4150 continue;
4151
4152 uint32_t level = 0;
4153 if (pMedium->i_getBase(&level) == medium)
4154 {
4155 /* matched device, channel and bus (i.e. attached to the
4156 * same place) will win and immediately stop the search;
4157 * otherwise the attachment that has the youngest
4158 * descendant of medium will be used
4159 */
4160 if ( pAttach->i_getDevice() == aDevice
4161 && pAttach->i_getPort() == aControllerPort
4162 && pAttach->i_getControllerName() == aName
4163 )
4164 {
4165 pAttachFound = pAttach;
4166 break;
4167 }
4168 else if ( !pAttachFound
4169 || level > foundLevel /* prefer younger */
4170 )
4171 {
4172 pAttachFound = pAttach;
4173 foundLevel = level;
4174 }
4175 }
4176 }
4177
4178 if (pAttachFound)
4179 {
4180 base = pAttachFound->i_getMedium();
4181 break;
4182 }
4183
4184 snap = snap->i_getParent();
4185 }
4186
4187 /* re-lock medium tree and the medium, as we need it below */
4188 treeLock.acquire();
4189 mediumLock.acquire();
4190
4191 /* found a suitable diff, use it as a base */
4192 if (!base.isNull())
4193 {
4194 medium = base;
4195 mediumCaller.attach(medium);
4196 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4197 mediumLock.attach(medium);
4198 }
4199 }
4200
4201 Utf8Str strFullSnapshotFolder;
4202 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4203
4204 ComObjPtr<Medium> diff;
4205 diff.createObject();
4206 // store this diff in the same registry as the parent
4207 Guid uuidRegistryParent;
4208 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4209 {
4210 // parent image has no registry: this can happen if we're attaching a new immutable
4211 // image that has not yet been attached (medium then points to the base and we're
4212 // creating the diff image for the immutable, and the parent is not yet registered);
4213 // put the parent in the machine registry then
4214 mediumLock.release();
4215 treeLock.release();
4216 alock.release();
4217 i_addMediumToRegistry(medium);
4218 alock.acquire();
4219 treeLock.acquire();
4220 mediumLock.acquire();
4221 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4222 }
4223 rc = diff->init(mParent,
4224 medium->i_getPreferredDiffFormat(),
4225 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4226 uuidRegistryParent,
4227 DeviceType_HardDisk);
4228 if (FAILED(rc)) return rc;
4229
4230 /* Apply the normal locking logic to the entire chain. */
4231 MediumLockList *pMediumLockList(new MediumLockList());
4232 mediumLock.release();
4233 treeLock.release();
4234 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4235 diff /* pToLockWrite */,
4236 false /* fMediumLockWriteAll */,
4237 medium,
4238 *pMediumLockList);
4239 treeLock.acquire();
4240 mediumLock.acquire();
4241 if (SUCCEEDED(rc))
4242 {
4243 mediumLock.release();
4244 treeLock.release();
4245 rc = pMediumLockList->Lock();
4246 treeLock.acquire();
4247 mediumLock.acquire();
4248 if (FAILED(rc))
4249 setError(rc,
4250 tr("Could not lock medium when creating diff '%s'"),
4251 diff->i_getLocationFull().c_str());
4252 else
4253 {
4254 /* will release the lock before the potentially lengthy
4255 * operation, so protect with the special state */
4256 MachineState_T oldState = mData->mMachineState;
4257 i_setMachineState(MachineState_SettingUp);
4258
4259 mediumLock.release();
4260 treeLock.release();
4261 alock.release();
4262
4263 rc = medium->i_createDiffStorage(diff,
4264 medium->i_getPreferredDiffVariant(),
4265 pMediumLockList,
4266 NULL /* aProgress */,
4267 true /* aWait */);
4268
4269 alock.acquire();
4270 treeLock.acquire();
4271 mediumLock.acquire();
4272
4273 i_setMachineState(oldState);
4274 }
4275 }
4276
4277 /* Unlock the media and free the associated memory. */
4278 delete pMediumLockList;
4279
4280 if (FAILED(rc)) return rc;
4281
4282 /* use the created diff for the actual attachment */
4283 medium = diff;
4284 mediumCaller.attach(medium);
4285 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4286 mediumLock.attach(medium);
4287 }
4288 while (0);
4289
4290 ComObjPtr<MediumAttachment> attachment;
4291 attachment.createObject();
4292 rc = attachment->init(this,
4293 medium,
4294 aName,
4295 aControllerPort,
4296 aDevice,
4297 aType,
4298 fIndirect,
4299 false /* fPassthrough */,
4300 false /* fTempEject */,
4301 false /* fNonRotational */,
4302 false /* fDiscard */,
4303 fHotplug /* fHotPluggable */,
4304 Utf8Str::Empty);
4305 if (FAILED(rc)) return rc;
4306
4307 if (associate && !medium.isNull())
4308 {
4309 // as the last step, associate the medium to the VM
4310 rc = medium->i_addBackReference(mData->mUuid);
4311 // here we can fail because of Deleting, or being in process of creating a Diff
4312 if (FAILED(rc)) return rc;
4313
4314 mediumLock.release();
4315 treeLock.release();
4316 alock.release();
4317 i_addMediumToRegistry(medium);
4318 alock.acquire();
4319 treeLock.acquire();
4320 mediumLock.acquire();
4321 }
4322
4323 /* success: finally remember the attachment */
4324 i_setModified(IsModified_Storage);
4325 mMediumAttachments.backup();
4326 mMediumAttachments->push_back(attachment);
4327
4328 mediumLock.release();
4329 treeLock.release();
4330 alock.release();
4331
4332 if (fHotplug || fSilent)
4333 {
4334 if (!medium.isNull())
4335 {
4336 MediumLockList *pMediumLockList(new MediumLockList());
4337
4338 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4339 medium /* pToLockWrite */,
4340 false /* fMediumLockWriteAll */,
4341 NULL,
4342 *pMediumLockList);
4343 alock.acquire();
4344 if (FAILED(rc))
4345 delete pMediumLockList;
4346 else
4347 {
4348 mData->mSession.mLockedMedia.Unlock();
4349 alock.release();
4350 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4351 mData->mSession.mLockedMedia.Lock();
4352 alock.acquire();
4353 }
4354 alock.release();
4355 }
4356
4357 if (SUCCEEDED(rc))
4358 {
4359 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4360 /* Remove lock list in case of error. */
4361 if (FAILED(rc))
4362 {
4363 mData->mSession.mLockedMedia.Unlock();
4364 mData->mSession.mLockedMedia.Remove(attachment);
4365 mData->mSession.mLockedMedia.Lock();
4366 }
4367 }
4368 }
4369
4370 /* Save modified registries, but skip this machine as it's the caller's
4371 * job to save its settings like all other settings changes. */
4372 mParent->i_unmarkRegistryModified(i_getId());
4373 mParent->i_saveModifiedRegistries();
4374
4375 return rc;
4376}
4377
4378HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4379 LONG aDevice)
4380{
4381 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4382 aName.c_str(), aControllerPort, aDevice));
4383
4384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4385
4386 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4387 if (FAILED(rc)) return rc;
4388
4389 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4390
4391 /* Check for an existing controller. */
4392 ComObjPtr<StorageController> ctl;
4393 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4394 if (FAILED(rc)) return rc;
4395
4396 StorageControllerType_T ctrlType;
4397 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4398 if (FAILED(rc))
4399 return setError(E_FAIL,
4400 tr("Could not get type of controller '%s'"),
4401 aName.c_str());
4402
4403 bool fSilent = false;
4404 Utf8Str strReconfig;
4405
4406 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4407 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4408 if ( mData->mMachineState == MachineState_Paused
4409 && strReconfig == "1")
4410 fSilent = true;
4411
4412 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4413 bool fHotplug = false;
4414 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4415 fHotplug = true;
4416
4417 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4418 return setError(VBOX_E_INVALID_VM_STATE,
4419 tr("Controller '%s' does not support hotplugging"),
4420 aName.c_str());
4421
4422 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4423 aName,
4424 aControllerPort,
4425 aDevice);
4426 if (!pAttach)
4427 return setError(VBOX_E_OBJECT_NOT_FOUND,
4428 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4429 aDevice, aControllerPort, aName.c_str());
4430
4431 if (fHotplug && !pAttach->i_getHotPluggable())
4432 return setError(VBOX_E_NOT_SUPPORTED,
4433 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4434 aDevice, aControllerPort, aName.c_str());
4435
4436 /*
4437 * The VM has to detach the device before we delete any implicit diffs.
4438 * If this fails we can roll back without loosing data.
4439 */
4440 if (fHotplug || fSilent)
4441 {
4442 alock.release();
4443 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4444 alock.acquire();
4445 }
4446 if (FAILED(rc)) return rc;
4447
4448 /* If we are here everything went well and we can delete the implicit now. */
4449 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4450
4451 alock.release();
4452
4453 /* Save modified registries, but skip this machine as it's the caller's
4454 * job to save its settings like all other settings changes. */
4455 mParent->i_unmarkRegistryModified(i_getId());
4456 mParent->i_saveModifiedRegistries();
4457
4458 return rc;
4459}
4460
4461HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4462 LONG aDevice, BOOL aPassthrough)
4463{
4464 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4465 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4466
4467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4468
4469 HRESULT rc = i_checkStateDependency(MutableStateDep);
4470 if (FAILED(rc)) return rc;
4471
4472 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4473
4474 if (Global::IsOnlineOrTransient(mData->mMachineState))
4475 return setError(VBOX_E_INVALID_VM_STATE,
4476 tr("Invalid machine state: %s"),
4477 Global::stringifyMachineState(mData->mMachineState));
4478
4479 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4480 aName,
4481 aControllerPort,
4482 aDevice);
4483 if (!pAttach)
4484 return setError(VBOX_E_OBJECT_NOT_FOUND,
4485 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4486 aDevice, aControllerPort, aName.c_str());
4487
4488
4489 i_setModified(IsModified_Storage);
4490 mMediumAttachments.backup();
4491
4492 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4493
4494 if (pAttach->i_getType() != DeviceType_DVD)
4495 return setError(E_INVALIDARG,
4496 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4497 aDevice, aControllerPort, aName.c_str());
4498 pAttach->i_updatePassthrough(!!aPassthrough);
4499
4500 return S_OK;
4501}
4502
4503HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4504 LONG aDevice, BOOL aTemporaryEject)
4505{
4506
4507 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4508 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4509
4510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4511
4512 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4513 if (FAILED(rc)) return rc;
4514
4515 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4516 aName,
4517 aControllerPort,
4518 aDevice);
4519 if (!pAttach)
4520 return setError(VBOX_E_OBJECT_NOT_FOUND,
4521 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4522 aDevice, aControllerPort, aName.c_str());
4523
4524
4525 i_setModified(IsModified_Storage);
4526 mMediumAttachments.backup();
4527
4528 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4529
4530 if (pAttach->i_getType() != DeviceType_DVD)
4531 return setError(E_INVALIDARG,
4532 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4533 aDevice, aControllerPort, aName.c_str());
4534 pAttach->i_updateTempEject(!!aTemporaryEject);
4535
4536 return S_OK;
4537}
4538
4539HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4540 LONG aDevice, BOOL aNonRotational)
4541{
4542
4543 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4544 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4545
4546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4547
4548 HRESULT rc = i_checkStateDependency(MutableStateDep);
4549 if (FAILED(rc)) return rc;
4550
4551 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4552
4553 if (Global::IsOnlineOrTransient(mData->mMachineState))
4554 return setError(VBOX_E_INVALID_VM_STATE,
4555 tr("Invalid machine state: %s"),
4556 Global::stringifyMachineState(mData->mMachineState));
4557
4558 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4559 aName,
4560 aControllerPort,
4561 aDevice);
4562 if (!pAttach)
4563 return setError(VBOX_E_OBJECT_NOT_FOUND,
4564 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4565 aDevice, aControllerPort, aName.c_str());
4566
4567
4568 i_setModified(IsModified_Storage);
4569 mMediumAttachments.backup();
4570
4571 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4572
4573 if (pAttach->i_getType() != DeviceType_HardDisk)
4574 return setError(E_INVALIDARG,
4575 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"),
4576 aDevice, aControllerPort, aName.c_str());
4577 pAttach->i_updateNonRotational(!!aNonRotational);
4578
4579 return S_OK;
4580}
4581
4582HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4583 LONG aDevice, BOOL aDiscard)
4584{
4585
4586 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4587 aName.c_str(), aControllerPort, aDevice, aDiscard));
4588
4589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4590
4591 HRESULT rc = i_checkStateDependency(MutableStateDep);
4592 if (FAILED(rc)) return rc;
4593
4594 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4595
4596 if (Global::IsOnlineOrTransient(mData->mMachineState))
4597 return setError(VBOX_E_INVALID_VM_STATE,
4598 tr("Invalid machine state: %s"),
4599 Global::stringifyMachineState(mData->mMachineState));
4600
4601 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4602 aName,
4603 aControllerPort,
4604 aDevice);
4605 if (!pAttach)
4606 return setError(VBOX_E_OBJECT_NOT_FOUND,
4607 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4608 aDevice, aControllerPort, aName.c_str());
4609
4610
4611 i_setModified(IsModified_Storage);
4612 mMediumAttachments.backup();
4613
4614 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4615
4616 if (pAttach->i_getType() != DeviceType_HardDisk)
4617 return setError(E_INVALIDARG,
4618 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"),
4619 aDevice, aControllerPort, aName.c_str());
4620 pAttach->i_updateDiscard(!!aDiscard);
4621
4622 return S_OK;
4623}
4624
4625HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4626 LONG aDevice, BOOL aHotPluggable)
4627{
4628 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4629 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4630
4631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 HRESULT rc = i_checkStateDependency(MutableStateDep);
4634 if (FAILED(rc)) return rc;
4635
4636 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4637
4638 if (Global::IsOnlineOrTransient(mData->mMachineState))
4639 return setError(VBOX_E_INVALID_VM_STATE,
4640 tr("Invalid machine state: %s"),
4641 Global::stringifyMachineState(mData->mMachineState));
4642
4643 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4644 aName,
4645 aControllerPort,
4646 aDevice);
4647 if (!pAttach)
4648 return setError(VBOX_E_OBJECT_NOT_FOUND,
4649 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4650 aDevice, aControllerPort, aName.c_str());
4651
4652 /* Check for an existing controller. */
4653 ComObjPtr<StorageController> ctl;
4654 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4655 if (FAILED(rc)) return rc;
4656
4657 StorageControllerType_T ctrlType;
4658 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4659 if (FAILED(rc))
4660 return setError(E_FAIL,
4661 tr("Could not get type of controller '%s'"),
4662 aName.c_str());
4663
4664 if (!i_isControllerHotplugCapable(ctrlType))
4665 return setError(VBOX_E_NOT_SUPPORTED,
4666 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4667 aName.c_str());
4668
4669 i_setModified(IsModified_Storage);
4670 mMediumAttachments.backup();
4671
4672 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4673
4674 if (pAttach->i_getType() == DeviceType_Floppy)
4675 return setError(E_INVALIDARG,
4676 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"),
4677 aDevice, aControllerPort, aName.c_str());
4678 pAttach->i_updateHotPluggable(!!aHotPluggable);
4679
4680 return S_OK;
4681}
4682
4683HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4684 LONG aDevice)
4685{
4686 int rc = S_OK;
4687 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4688 aName.c_str(), aControllerPort, aDevice));
4689
4690 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4691
4692 return rc;
4693}
4694
4695HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4696 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4697{
4698 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4699 aName.c_str(), aControllerPort, aDevice));
4700
4701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4702
4703 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4704 if (FAILED(rc)) return rc;
4705
4706 if (Global::IsOnlineOrTransient(mData->mMachineState))
4707 return setError(VBOX_E_INVALID_VM_STATE,
4708 tr("Invalid machine state: %s"),
4709 Global::stringifyMachineState(mData->mMachineState));
4710
4711 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4712 aName,
4713 aControllerPort,
4714 aDevice);
4715 if (!pAttach)
4716 return setError(VBOX_E_OBJECT_NOT_FOUND,
4717 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4718 aDevice, aControllerPort, aName.c_str());
4719
4720
4721 i_setModified(IsModified_Storage);
4722 mMediumAttachments.backup();
4723
4724 IBandwidthGroup *iB = aBandwidthGroup;
4725 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4726 if (aBandwidthGroup && group.isNull())
4727 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4728
4729 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4730
4731 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4732 if (strBandwidthGroupOld.isNotEmpty())
4733 {
4734 /* Get the bandwidth group object and release it - this must not fail. */
4735 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4736 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4737 Assert(SUCCEEDED(rc));
4738
4739 pBandwidthGroupOld->i_release();
4740 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4741 }
4742
4743 if (!group.isNull())
4744 {
4745 group->i_reference();
4746 pAttach->i_updateBandwidthGroup(group->i_getName());
4747 }
4748
4749 return S_OK;
4750}
4751
4752HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4753 LONG aControllerPort,
4754 LONG aDevice,
4755 DeviceType_T aType)
4756{
4757 HRESULT rc = S_OK;
4758
4759 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4760 aName.c_str(), aControllerPort, aDevice, aType));
4761
4762 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4763
4764 return rc;
4765}
4766
4767
4768HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4769 LONG aControllerPort,
4770 LONG aDevice,
4771 BOOL aForce)
4772{
4773 int rc = S_OK;
4774 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4775 aName.c_str(), aControllerPort, aForce));
4776
4777 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4778
4779 return rc;
4780}
4781
4782HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4783 LONG aControllerPort,
4784 LONG aDevice,
4785 const ComPtr<IMedium> &aMedium,
4786 BOOL aForce)
4787{
4788 int rc = S_OK;
4789 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4790 aName.c_str(), aControllerPort, aDevice, aForce));
4791
4792 // request the host lock first, since might be calling Host methods for getting host drives;
4793 // next, protect the media tree all the while we're in here, as well as our member variables
4794 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4795 this->lockHandle(),
4796 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4797
4798 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4799 aName,
4800 aControllerPort,
4801 aDevice);
4802 if (pAttach.isNull())
4803 return setError(VBOX_E_OBJECT_NOT_FOUND,
4804 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4805 aDevice, aControllerPort, aName.c_str());
4806
4807 /* Remember previously mounted medium. The medium before taking the
4808 * backup is not necessarily the same thing. */
4809 ComObjPtr<Medium> oldmedium;
4810 oldmedium = pAttach->i_getMedium();
4811
4812 IMedium *iM = aMedium;
4813 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4814 if (aMedium && pMedium.isNull())
4815 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4816
4817 AutoCaller mediumCaller(pMedium);
4818 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4819
4820 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4821 if (pMedium)
4822 {
4823 DeviceType_T mediumType = pAttach->i_getType();
4824 switch (mediumType)
4825 {
4826 case DeviceType_DVD:
4827 case DeviceType_Floppy:
4828 break;
4829
4830 default:
4831 return setError(VBOX_E_INVALID_OBJECT_STATE,
4832 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4833 aControllerPort,
4834 aDevice,
4835 aName.c_str());
4836 }
4837 }
4838
4839 i_setModified(IsModified_Storage);
4840 mMediumAttachments.backup();
4841
4842 {
4843 // The backup operation makes the pAttach reference point to the
4844 // old settings. Re-get the correct reference.
4845 pAttach = i_findAttachment(*mMediumAttachments.data(),
4846 aName,
4847 aControllerPort,
4848 aDevice);
4849 if (!oldmedium.isNull())
4850 oldmedium->i_removeBackReference(mData->mUuid);
4851 if (!pMedium.isNull())
4852 {
4853 pMedium->i_addBackReference(mData->mUuid);
4854
4855 mediumLock.release();
4856 multiLock.release();
4857 i_addMediumToRegistry(pMedium);
4858 multiLock.acquire();
4859 mediumLock.acquire();
4860 }
4861
4862 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4863 pAttach->i_updateMedium(pMedium);
4864 }
4865
4866 i_setModified(IsModified_Storage);
4867
4868 mediumLock.release();
4869 multiLock.release();
4870 rc = i_onMediumChange(pAttach, aForce);
4871 multiLock.acquire();
4872 mediumLock.acquire();
4873
4874 /* On error roll back this change only. */
4875 if (FAILED(rc))
4876 {
4877 if (!pMedium.isNull())
4878 pMedium->i_removeBackReference(mData->mUuid);
4879 pAttach = i_findAttachment(*mMediumAttachments.data(),
4880 aName,
4881 aControllerPort,
4882 aDevice);
4883 /* If the attachment is gone in the meantime, bail out. */
4884 if (pAttach.isNull())
4885 return rc;
4886 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4887 if (!oldmedium.isNull())
4888 oldmedium->i_addBackReference(mData->mUuid);
4889 pAttach->i_updateMedium(oldmedium);
4890 }
4891
4892 mediumLock.release();
4893 multiLock.release();
4894
4895 /* Save modified registries, but skip this machine as it's the caller's
4896 * job to save its settings like all other settings changes. */
4897 mParent->i_unmarkRegistryModified(i_getId());
4898 mParent->i_saveModifiedRegistries();
4899
4900 return rc;
4901}
4902HRESULT Machine::getMedium(const com::Utf8Str &aName,
4903 LONG aControllerPort,
4904 LONG aDevice,
4905 ComPtr<IMedium> &aMedium)
4906{
4907 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4908 aName.c_str(), aControllerPort, aDevice));
4909
4910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4911
4912 aMedium = NULL;
4913
4914 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4915 aName,
4916 aControllerPort,
4917 aDevice);
4918 if (pAttach.isNull())
4919 return setError(VBOX_E_OBJECT_NOT_FOUND,
4920 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4921 aDevice, aControllerPort, aName.c_str());
4922
4923 aMedium = pAttach->i_getMedium();
4924
4925 return S_OK;
4926}
4927
4928HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4929{
4930
4931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4932
4933 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4934
4935 return S_OK;
4936}
4937
4938HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4939{
4940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4941
4942 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4943
4944 return S_OK;
4945}
4946
4947HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4948{
4949 /* Do not assert if slot is out of range, just return the advertised
4950 status. testdriver/vbox.py triggers this in logVmInfo. */
4951 if (aSlot >= mNetworkAdapters.size())
4952 return setError(E_INVALIDARG,
4953 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4954 aSlot, mNetworkAdapters.size());
4955
4956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4957
4958 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4959
4960 return S_OK;
4961}
4962
4963HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4964{
4965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4966
4967 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4968 size_t i = 0;
4969 for (settings::StringsMap::const_iterator
4970 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4971 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4972 ++it, ++i)
4973 aKeys[i] = it->first;
4974
4975 return S_OK;
4976}
4977
4978 /**
4979 * @note Locks this object for reading.
4980 */
4981HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4982 com::Utf8Str &aValue)
4983{
4984 /* start with nothing found */
4985 aValue = "";
4986
4987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4988
4989 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4990 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4991 // found:
4992 aValue = it->second; // source is a Utf8Str
4993
4994 /* return the result to caller (may be empty) */
4995 return S_OK;
4996}
4997
4998 /**
4999 * @note Locks mParent for writing + this object for writing.
5000 */
5001HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5002{
5003 Utf8Str strOldValue; // empty
5004
5005 // locking note: we only hold the read lock briefly to look up the old value,
5006 // then release it and call the onExtraCanChange callbacks. There is a small
5007 // chance of a race insofar as the callback might be called twice if two callers
5008 // change the same key at the same time, but that's a much better solution
5009 // than the deadlock we had here before. The actual changing of the extradata
5010 // is then performed under the write lock and race-free.
5011
5012 // look up the old value first; if nothing has changed then we need not do anything
5013 {
5014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5015
5016 // For snapshots don't even think about allowing changes, extradata
5017 // is global for a machine, so there is nothing snapshot specific.
5018 if (i_isSnapshotMachine())
5019 return setError(VBOX_E_INVALID_VM_STATE,
5020 tr("Cannot set extradata for a snapshot"));
5021
5022 // check if the right IMachine instance is used
5023 if (mData->mRegistered && !i_isSessionMachine())
5024 return setError(VBOX_E_INVALID_VM_STATE,
5025 tr("Cannot set extradata for an immutable machine"));
5026
5027 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5028 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5029 strOldValue = it->second;
5030 }
5031
5032 bool fChanged;
5033 if ((fChanged = (strOldValue != aValue)))
5034 {
5035 // ask for permission from all listeners outside the locks;
5036 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5037 // lock to copy the list of callbacks to invoke
5038 Bstr error;
5039 Bstr bstrValue(aValue);
5040
5041 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5042 {
5043 const char *sep = error.isEmpty() ? "" : ": ";
5044 CBSTR err = error.raw();
5045 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5046 return setError(E_ACCESSDENIED,
5047 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5048 aKey.c_str(),
5049 aValue.c_str(),
5050 sep,
5051 err);
5052 }
5053
5054 // data is changing and change not vetoed: then write it out under the lock
5055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5056
5057 if (aValue.isEmpty())
5058 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5059 else
5060 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5061 // creates a new key if needed
5062
5063 bool fNeedsGlobalSaveSettings = false;
5064 // This saving of settings is tricky: there is no "old state" for the
5065 // extradata items at all (unlike all other settings), so the old/new
5066 // settings comparison would give a wrong result!
5067 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5068
5069 if (fNeedsGlobalSaveSettings)
5070 {
5071 // save the global settings; for that we should hold only the VirtualBox lock
5072 alock.release();
5073 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5074 mParent->i_saveSettings();
5075 }
5076 }
5077
5078 // fire notification outside the lock
5079 if (fChanged)
5080 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5081
5082 return S_OK;
5083}
5084
5085HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5086{
5087 aProgress = NULL;
5088 NOREF(aSettingsFilePath);
5089 ReturnComNotImplemented();
5090}
5091
5092HRESULT Machine::saveSettings()
5093{
5094 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5095
5096 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5097 if (FAILED(rc)) return rc;
5098
5099 /* the settings file path may never be null */
5100 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5101
5102 /* save all VM data excluding snapshots */
5103 bool fNeedsGlobalSaveSettings = false;
5104 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5105 mlock.release();
5106
5107 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5108 {
5109 // save the global settings; for that we should hold only the VirtualBox lock
5110 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5111 rc = mParent->i_saveSettings();
5112 }
5113
5114 return rc;
5115}
5116
5117
5118HRESULT Machine::discardSettings()
5119{
5120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5121
5122 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5123 if (FAILED(rc)) return rc;
5124
5125 /*
5126 * during this rollback, the session will be notified if data has
5127 * been actually changed
5128 */
5129 i_rollback(true /* aNotify */);
5130
5131 return S_OK;
5132}
5133
5134/** @note Locks objects! */
5135HRESULT Machine::unregister(AutoCaller &autoCaller,
5136 CleanupMode_T aCleanupMode,
5137 std::vector<ComPtr<IMedium> > &aMedia)
5138{
5139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5140
5141 Guid id(i_getId());
5142
5143 if (mData->mSession.mState != SessionState_Unlocked)
5144 return setError(VBOX_E_INVALID_OBJECT_STATE,
5145 tr("Cannot unregister the machine '%s' while it is locked"),
5146 mUserData->s.strName.c_str());
5147
5148 // wait for state dependents to drop to zero
5149 i_ensureNoStateDependencies();
5150
5151 if (!mData->mAccessible)
5152 {
5153 // inaccessible maschines can only be unregistered; uninitialize ourselves
5154 // here because currently there may be no unregistered that are inaccessible
5155 // (this state combination is not supported). Note releasing the caller and
5156 // leaving the lock before calling uninit()
5157 alock.release();
5158 autoCaller.release();
5159
5160 uninit();
5161
5162 mParent->i_unregisterMachine(this, id);
5163 // calls VirtualBox::i_saveSettings()
5164
5165 return S_OK;
5166 }
5167
5168 HRESULT rc = S_OK;
5169
5170 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5171 // discard saved state
5172 if (mData->mMachineState == MachineState_Saved)
5173 {
5174 // add the saved state file to the list of files the caller should delete
5175 Assert(!mSSData->strStateFilePath.isEmpty());
5176 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5177
5178 mSSData->strStateFilePath.setNull();
5179
5180 // unconditionally set the machine state to powered off, we now
5181 // know no session has locked the machine
5182 mData->mMachineState = MachineState_PoweredOff;
5183 }
5184
5185 size_t cSnapshots = 0;
5186 if (mData->mFirstSnapshot)
5187 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5188 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5189 // fail now before we start detaching media
5190 return setError(VBOX_E_INVALID_OBJECT_STATE,
5191 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5192 mUserData->s.strName.c_str(), cSnapshots);
5193
5194 // This list collects the medium objects from all medium attachments
5195 // which we will detach from the machine and its snapshots, in a specific
5196 // order which allows for closing all media without getting "media in use"
5197 // errors, simply by going through the list from the front to the back:
5198 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5199 // and must be closed before the parent media from the snapshots, or closing the parents
5200 // will fail because they still have children);
5201 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5202 // the root ("first") snapshot of the machine.
5203 MediaList llMedia;
5204
5205 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5206 && mMediumAttachments->size()
5207 )
5208 {
5209 // we have media attachments: detach them all and add the Medium objects to our list
5210 if (aCleanupMode != CleanupMode_UnregisterOnly)
5211 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5212 else
5213 return setError(VBOX_E_INVALID_OBJECT_STATE,
5214 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5215 mUserData->s.strName.c_str(), mMediumAttachments->size());
5216 }
5217
5218 if (cSnapshots)
5219 {
5220 // add the media from the medium attachments of the snapshots to llMedia
5221 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5222 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5223 // into the children first
5224
5225 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5226 MachineState_T oldState = mData->mMachineState;
5227 mData->mMachineState = MachineState_DeletingSnapshot;
5228
5229 // make a copy of the first snapshot so the refcount does not drop to 0
5230 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5231 // because of the AutoCaller voodoo)
5232 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5233
5234 // GO!
5235 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5236
5237 mData->mMachineState = oldState;
5238 }
5239
5240 if (FAILED(rc))
5241 {
5242 i_rollbackMedia();
5243 return rc;
5244 }
5245
5246 // commit all the media changes made above
5247 i_commitMedia();
5248
5249 mData->mRegistered = false;
5250
5251 // machine lock no longer needed
5252 alock.release();
5253
5254 // return media to caller
5255 aMedia.resize(llMedia.size());
5256 size_t i = 0;
5257 for (MediaList::const_iterator
5258 it = llMedia.begin();
5259 it != llMedia.end();
5260 ++it, ++i)
5261 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5262
5263 mParent->i_unregisterMachine(this, id);
5264 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5265
5266 return S_OK;
5267}
5268
5269/**
5270 * Task record for deleting a machine config.
5271 */
5272class Machine::DeleteConfigTask
5273 : public Machine::Task
5274{
5275public:
5276 DeleteConfigTask(Machine *m,
5277 Progress *p,
5278 const Utf8Str &t,
5279 const RTCList<ComPtr<IMedium> > &llMediums,
5280 const StringsList &llFilesToDelete)
5281 : Task(m, p, t),
5282 m_llMediums(llMediums),
5283 m_llFilesToDelete(llFilesToDelete)
5284 {}
5285
5286private:
5287 void handler()
5288 {
5289 try
5290 {
5291 m_pMachine->i_deleteConfigHandler(*this);
5292 }
5293 catch (...)
5294 {
5295 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5296 }
5297 }
5298
5299 RTCList<ComPtr<IMedium> > m_llMediums;
5300 StringsList m_llFilesToDelete;
5301
5302 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5303};
5304
5305/**
5306 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5307 * SessionMachine::taskHandler().
5308 *
5309 * @note Locks this object for writing.
5310 *
5311 * @param task
5312 * @return
5313 */
5314void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5315{
5316 LogFlowThisFuncEnter();
5317
5318 AutoCaller autoCaller(this);
5319 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5320 if (FAILED(autoCaller.rc()))
5321 {
5322 /* we might have been uninitialized because the session was accidentally
5323 * closed by the client, so don't assert */
5324 HRESULT rc = setError(E_FAIL,
5325 tr("The session has been accidentally closed"));
5326 task.m_pProgress->i_notifyComplete(rc);
5327 LogFlowThisFuncLeave();
5328 return;
5329 }
5330
5331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5332
5333 HRESULT rc = S_OK;
5334
5335 try
5336 {
5337 ULONG uLogHistoryCount = 3;
5338 ComPtr<ISystemProperties> systemProperties;
5339 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5340 if (FAILED(rc)) throw rc;
5341
5342 if (!systemProperties.isNull())
5343 {
5344 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5345 if (FAILED(rc)) throw rc;
5346 }
5347
5348 MachineState_T oldState = mData->mMachineState;
5349 i_setMachineState(MachineState_SettingUp);
5350 alock.release();
5351 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5352 {
5353 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5354 {
5355 AutoCaller mac(pMedium);
5356 if (FAILED(mac.rc())) throw mac.rc();
5357 Utf8Str strLocation = pMedium->i_getLocationFull();
5358 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5359 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5360 if (FAILED(rc)) throw rc;
5361 }
5362 if (pMedium->i_isMediumFormatFile())
5363 {
5364 ComPtr<IProgress> pProgress2;
5365 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5366 if (FAILED(rc)) throw rc;
5367 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5368 if (FAILED(rc)) throw rc;
5369 /* Check the result of the asynchronous process. */
5370 LONG iRc;
5371 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5372 if (FAILED(rc)) throw rc;
5373 /* If the thread of the progress object has an error, then
5374 * retrieve the error info from there, or it'll be lost. */
5375 if (FAILED(iRc))
5376 throw setError(ProgressErrorInfo(pProgress2));
5377 }
5378
5379 /* Close the medium, deliberately without checking the return
5380 * code, and without leaving any trace in the error info, as
5381 * a failure here is a very minor issue, which shouldn't happen
5382 * as above we even managed to delete the medium. */
5383 {
5384 ErrorInfoKeeper eik;
5385 pMedium->Close();
5386 }
5387 }
5388 i_setMachineState(oldState);
5389 alock.acquire();
5390
5391 // delete the files pushed on the task list by Machine::Delete()
5392 // (this includes saved states of the machine and snapshots and
5393 // medium storage files from the IMedium list passed in, and the
5394 // machine XML file)
5395 for (StringsList::const_iterator
5396 it = task.m_llFilesToDelete.begin();
5397 it != task.m_llFilesToDelete.end();
5398 ++it)
5399 {
5400 const Utf8Str &strFile = *it;
5401 LogFunc(("Deleting file %s\n", strFile.c_str()));
5402 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5403 if (FAILED(rc)) throw rc;
5404
5405 int vrc = RTFileDelete(strFile.c_str());
5406 if (RT_FAILURE(vrc))
5407 throw setError(VBOX_E_IPRT_ERROR,
5408 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5409 }
5410
5411 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5412 if (FAILED(rc)) throw rc;
5413
5414 /* delete the settings only when the file actually exists */
5415 if (mData->pMachineConfigFile->fileExists())
5416 {
5417 /* Delete any backup or uncommitted XML files. Ignore failures.
5418 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5419 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5420 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5421 RTFileDelete(otherXml.c_str());
5422 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5423 RTFileDelete(otherXml.c_str());
5424
5425 /* delete the Logs folder, nothing important should be left
5426 * there (we don't check for errors because the user might have
5427 * some private files there that we don't want to delete) */
5428 Utf8Str logFolder;
5429 getLogFolder(logFolder);
5430 Assert(logFolder.length());
5431 if (RTDirExists(logFolder.c_str()))
5432 {
5433 /* Delete all VBox.log[.N] files from the Logs folder
5434 * (this must be in sync with the rotation logic in
5435 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5436 * files that may have been created by the GUI. */
5437 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5438 logFolder.c_str(), RTPATH_DELIMITER);
5439 RTFileDelete(log.c_str());
5440 log = Utf8StrFmt("%s%cVBox.png",
5441 logFolder.c_str(), RTPATH_DELIMITER);
5442 RTFileDelete(log.c_str());
5443 for (int i = uLogHistoryCount; i > 0; i--)
5444 {
5445 log = Utf8StrFmt("%s%cVBox.log.%d",
5446 logFolder.c_str(), RTPATH_DELIMITER, i);
5447 RTFileDelete(log.c_str());
5448 log = Utf8StrFmt("%s%cVBox.png.%d",
5449 logFolder.c_str(), RTPATH_DELIMITER, i);
5450 RTFileDelete(log.c_str());
5451 }
5452#if defined(RT_OS_WINDOWS)
5453 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5454 RTFileDelete(log.c_str());
5455 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5456 RTFileDelete(log.c_str());
5457#endif
5458
5459 RTDirRemove(logFolder.c_str());
5460 }
5461
5462 /* delete the Snapshots folder, nothing important should be left
5463 * there (we don't check for errors because the user might have
5464 * some private files there that we don't want to delete) */
5465 Utf8Str strFullSnapshotFolder;
5466 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5467 Assert(!strFullSnapshotFolder.isEmpty());
5468 if (RTDirExists(strFullSnapshotFolder.c_str()))
5469 RTDirRemove(strFullSnapshotFolder.c_str());
5470
5471 // delete the directory that contains the settings file, but only
5472 // if it matches the VM name
5473 Utf8Str settingsDir;
5474 if (i_isInOwnDir(&settingsDir))
5475 RTDirRemove(settingsDir.c_str());
5476 }
5477
5478 alock.release();
5479
5480 mParent->i_saveModifiedRegistries();
5481 }
5482 catch (HRESULT aRC) { rc = aRC; }
5483
5484 task.m_pProgress->i_notifyComplete(rc);
5485
5486 LogFlowThisFuncLeave();
5487}
5488
5489HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5490{
5491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5492
5493 HRESULT rc = i_checkStateDependency(MutableStateDep);
5494 if (FAILED(rc)) return rc;
5495
5496 if (mData->mRegistered)
5497 return setError(VBOX_E_INVALID_VM_STATE,
5498 tr("Cannot delete settings of a registered machine"));
5499
5500 // collect files to delete
5501 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5502 if (mData->pMachineConfigFile->fileExists())
5503 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5504
5505 RTCList<ComPtr<IMedium> > llMediums;
5506 for (size_t i = 0; i < aMedia.size(); ++i)
5507 {
5508 IMedium *pIMedium(aMedia[i]);
5509 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5510 if (pMedium.isNull())
5511 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5512 SafeArray<BSTR> ids;
5513 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5514 if (FAILED(rc)) return rc;
5515 /* At this point the medium should not have any back references
5516 * anymore. If it has it is attached to another VM and *must* not
5517 * deleted. */
5518 if (ids.size() < 1)
5519 llMediums.append(pMedium);
5520 }
5521
5522 ComObjPtr<Progress> pProgress;
5523 pProgress.createObject();
5524 rc = pProgress->init(i_getVirtualBox(),
5525 static_cast<IMachine*>(this) /* aInitiator */,
5526 tr("Deleting files"),
5527 true /* fCancellable */,
5528 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5529 tr("Collecting file inventory"));
5530 if (FAILED(rc))
5531 return rc;
5532
5533 /* create and start the task on a separate thread (note that it will not
5534 * start working until we release alock) */
5535 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5536 rc = pTask->createThread();
5537 if (FAILED(rc))
5538 return rc;
5539
5540 pProgress.queryInterfaceTo(aProgress.asOutParam());
5541
5542 LogFlowFuncLeave();
5543
5544 return S_OK;
5545}
5546
5547HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5548{
5549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5550
5551 ComObjPtr<Snapshot> pSnapshot;
5552 HRESULT rc;
5553
5554 if (aNameOrId.isEmpty())
5555 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5556 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5557 else
5558 {
5559 Guid uuid(aNameOrId);
5560 if (uuid.isValid())
5561 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5562 else
5563 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5564 }
5565 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5566
5567 return rc;
5568}
5569
5570HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5571{
5572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5573
5574 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5575 if (FAILED(rc)) return rc;
5576
5577 ComObjPtr<SharedFolder> sharedFolder;
5578 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5579 if (SUCCEEDED(rc))
5580 return setError(VBOX_E_OBJECT_IN_USE,
5581 tr("Shared folder named '%s' already exists"),
5582 aName.c_str());
5583
5584 sharedFolder.createObject();
5585 rc = sharedFolder->init(i_getMachine(),
5586 aName,
5587 aHostPath,
5588 !!aWritable,
5589 !!aAutomount,
5590 true /* fFailOnError */);
5591 if (FAILED(rc)) return rc;
5592
5593 i_setModified(IsModified_SharedFolders);
5594 mHWData.backup();
5595 mHWData->mSharedFolders.push_back(sharedFolder);
5596
5597 /* inform the direct session if any */
5598 alock.release();
5599 i_onSharedFolderChange();
5600
5601 return S_OK;
5602}
5603
5604HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5605{
5606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5607
5608 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5609 if (FAILED(rc)) return rc;
5610
5611 ComObjPtr<SharedFolder> sharedFolder;
5612 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5613 if (FAILED(rc)) return rc;
5614
5615 i_setModified(IsModified_SharedFolders);
5616 mHWData.backup();
5617 mHWData->mSharedFolders.remove(sharedFolder);
5618
5619 /* inform the direct session if any */
5620 alock.release();
5621 i_onSharedFolderChange();
5622
5623 return S_OK;
5624}
5625
5626HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5627{
5628 /* start with No */
5629 *aCanShow = FALSE;
5630
5631 ComPtr<IInternalSessionControl> directControl;
5632 {
5633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5634
5635 if (mData->mSession.mState != SessionState_Locked)
5636 return setError(VBOX_E_INVALID_VM_STATE,
5637 tr("Machine is not locked for session (session state: %s)"),
5638 Global::stringifySessionState(mData->mSession.mState));
5639
5640 if (mData->mSession.mLockType == LockType_VM)
5641 directControl = mData->mSession.mDirectControl;
5642 }
5643
5644 /* ignore calls made after #OnSessionEnd() is called */
5645 if (!directControl)
5646 return S_OK;
5647
5648 LONG64 dummy;
5649 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5650}
5651
5652HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5653{
5654 ComPtr<IInternalSessionControl> directControl;
5655 {
5656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5657
5658 if (mData->mSession.mState != SessionState_Locked)
5659 return setError(E_FAIL,
5660 tr("Machine is not locked for session (session state: %s)"),
5661 Global::stringifySessionState(mData->mSession.mState));
5662
5663 if (mData->mSession.mLockType == LockType_VM)
5664 directControl = mData->mSession.mDirectControl;
5665 }
5666
5667 /* ignore calls made after #OnSessionEnd() is called */
5668 if (!directControl)
5669 return S_OK;
5670
5671 BOOL dummy;
5672 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5673}
5674
5675#ifdef VBOX_WITH_GUEST_PROPS
5676/**
5677 * Look up a guest property in VBoxSVC's internal structures.
5678 */
5679HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5680 com::Utf8Str &aValue,
5681 LONG64 *aTimestamp,
5682 com::Utf8Str &aFlags) const
5683{
5684 using namespace guestProp;
5685
5686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5687 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5688
5689 if (it != mHWData->mGuestProperties.end())
5690 {
5691 char szFlags[MAX_FLAGS_LEN + 1];
5692 aValue = it->second.strValue;
5693 *aTimestamp = it->second.mTimestamp;
5694 writeFlags(it->second.mFlags, szFlags);
5695