VirtualBox

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

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

Main/Progress+Appliance+Machine: Turn IProgress::waitForAsyncProgressCompletion into an internal method. It is not needed by any client code outside the API, it's simply is too special. Also include the error propagation which it originally skipped (leding to reduntant code in the calling code). Remove a replicated version of it from the Appliance code which seems to be the half-forgotten ancestor of the method in IProgress. Adapt the users of the method to the new internal method, which made the code easier to read.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 538.0 KB
Line 
1/* $Id: MachineImpl.cpp 73743 2018-08-17 17:56:34Z 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#include "SystemPropertiesImpl.h"
47#include "MachineImplMoveVM.h"
48#include "ExtPackManagerImpl.h"
49
50// generated header
51#include "VBoxEvents.h"
52
53#ifdef VBOX_WITH_USB
54# include "USBProxyService.h"
55#endif
56
57#include "AutoCaller.h"
58#include "HashedPw.h"
59#include "Performance.h"
60
61#include <iprt/asm.h>
62#include <iprt/path.h>
63#include <iprt/dir.h>
64#include <iprt/env.h>
65#include <iprt/lockvalidator.h>
66#include <iprt/process.h>
67#include <iprt/cpp/utils.h>
68#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
69#include <iprt/sha.h>
70#include <iprt/string.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#ifdef VBOX_WITH_DTRACE_R3_MAIN
90# include "dtrace/VBoxAPI.h"
91#endif
92
93#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
94# define HOSTSUFF_EXE ".exe"
95#else /* !RT_OS_WINDOWS */
96# define HOSTSUFF_EXE ""
97#endif /* !RT_OS_WINDOWS */
98
99// defines / prototypes
100/////////////////////////////////////////////////////////////////////////////
101
102/////////////////////////////////////////////////////////////////////////////
103// Machine::Data structure
104/////////////////////////////////////////////////////////////////////////////
105
106Machine::Data::Data()
107{
108 mRegistered = FALSE;
109 pMachineConfigFile = NULL;
110 /* Contains hints on what has changed when the user is using the VM (config
111 * changes, running the VM, ...). This is used to decide if a config needs
112 * to be written to disk. */
113 flModifications = 0;
114 /* VM modification usually also trigger setting the current state to
115 * "Modified". Although this is not always the case. An e.g. is the VM
116 * initialization phase or when snapshot related data is changed. The
117 * actually behavior is controlled by the following flag. */
118 m_fAllowStateModification = false;
119 mAccessible = FALSE;
120 /* mUuid is initialized in Machine::init() */
121
122 mMachineState = MachineState_PoweredOff;
123 RTTimeNow(&mLastStateChange);
124
125 mMachineStateDeps = 0;
126 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
127 mMachineStateChangePending = 0;
128
129 mCurrentStateModified = TRUE;
130 mGuestPropertiesModified = FALSE;
131
132 mSession.mPID = NIL_RTPROCESS;
133 mSession.mLockType = LockType_Null;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
165 mVRAMSize = 8;
166 mAccelerate3DEnabled = false;
167 mAccelerate2DVideoEnabled = false;
168 mMonitorCount = 1;
169 mVideoCaptureWidth = 1024;
170 mVideoCaptureHeight = 768;
171 mVideoCaptureRate = 512;
172 mVideoCaptureFPS = 25;
173 mVideoCaptureMaxTime = 0;
174 mVideoCaptureMaxFileSize = 0;
175 mVideoCaptureEnabled = false;
176 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
177 maVideoCaptureScreens[i] = true;
178
179 mHWVirtExEnabled = true;
180 mHWVirtExNestedPagingEnabled = true;
181#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
182 mHWVirtExLargePagesEnabled = true;
183#else
184 /* Not supported on 32 bits hosts. */
185 mHWVirtExLargePagesEnabled = false;
186#endif
187 mHWVirtExVPIDEnabled = true;
188 mHWVirtExUXEnabled = true;
189 mHWVirtExForceEnabled = false;
190 mHWVirtExUseNativeApi = 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 mIBPBOnVMExit = false;
201 mIBPBOnVMEntry = false;
202 mSpecCtrl = false;
203 mSpecCtrlByHost = false;
204 mNestedHWVirt = false;
205 mHPETEnabled = false;
206 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
207 mCpuIdPortabilityLevel = 0;
208 mCpuProfile = "host";
209
210 /* default boot order: floppy - DVD - HDD */
211 mBootOrder[0] = DeviceType_Floppy;
212 mBootOrder[1] = DeviceType_DVD;
213 mBootOrder[2] = DeviceType_HardDisk;
214 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
215 mBootOrder[i] = DeviceType_Null;
216
217 mClipboardMode = ClipboardMode_Disabled;
218 mDnDMode = DnDMode_Disabled;
219
220 mFirmwareType = FirmwareType_BIOS;
221 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
222 mPointingHIDType = PointingHIDType_PS2Mouse;
223 mChipsetType = ChipsetType_PIIX3;
224 mParavirtProvider = ParavirtProvider_Default;
225 mEmulatedUSBCardReaderEnabled = FALSE;
226
227 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
228 mCPUAttached[i] = false;
229
230 mIOCacheEnabled = true;
231 mIOCacheSize = 5; /* 5MB */
232}
233
234Machine::HWData::~HWData()
235{
236}
237
238/////////////////////////////////////////////////////////////////////////////
239// Machine class
240/////////////////////////////////////////////////////////////////////////////
241
242// constructor / destructor
243/////////////////////////////////////////////////////////////////////////////
244
245Machine::Machine() :
246#ifdef VBOX_WITH_RESOURCE_USAGE_API
247 mCollectorGuest(NULL),
248#endif
249 mPeer(NULL),
250 mParent(NULL),
251 mSerialPorts(),
252 mParallelPorts(),
253 uRegistryNeedsSaving(0)
254{}
255
256Machine::~Machine()
257{}
258
259HRESULT Machine::FinalConstruct()
260{
261 LogFlowThisFunc(("\n"));
262 return BaseFinalConstruct();
263}
264
265void Machine::FinalRelease()
266{
267 LogFlowThisFunc(("\n"));
268 uninit();
269 BaseFinalRelease();
270}
271
272/**
273 * Initializes a new machine instance; this init() variant creates a new, empty machine.
274 * This gets called from VirtualBox::CreateMachine().
275 *
276 * @param aParent Associated parent object
277 * @param strConfigFile Local file system path to the VM settings file (can
278 * be relative to the VirtualBox config directory).
279 * @param strName name for the machine
280 * @param llGroups list of groups for the machine
281 * @param strOsType OS Type string (stored as is if aOsType is NULL).
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
286 * scheme (includes the UUID).
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 const Utf8Str &strOsType,
295 GuestOSType *aOsType,
296 const Guid &aId,
297 bool fForceOverwrite,
298 bool fDirectoryIncludesUUID)
299{
300 LogFlowThisFuncEnter();
301 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
302
303 /* Enclose the state transition NotReady->InInit->Ready */
304 AutoInitSpan autoInitSpan(this);
305 AssertReturn(autoInitSpan.isOk(), E_FAIL);
306
307 HRESULT rc = initImpl(aParent, strConfigFile);
308 if (FAILED(rc)) return rc;
309
310 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
311 if (FAILED(rc)) return rc;
312
313 if (SUCCEEDED(rc))
314 {
315 // create an empty machine config
316 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
317
318 rc = initDataAndChildObjects();
319 }
320
321 if (SUCCEEDED(rc))
322 {
323 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
324 mData->mAccessible = TRUE;
325
326 unconst(mData->mUuid) = aId;
327
328 mUserData->s.strName = strName;
329
330 if (llGroups.size())
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Let the OS type select 64-bit ness. */
349 mHWData->mLongMode = aOsType->i_is64Bit()
350 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
351
352 /* Let the OS type enable the X2APIC */
353 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
354 }
355 else if (!strOsType.isEmpty())
356 {
357 /* Store OS type */
358 mUserData->s.strOsType = strOsType;
359
360 /* No guest OS type object. Pick some plausible defaults which the
361 * host can handle. There's no way to know or validate anything. */
362 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363 mHWData->mX2APIC = false;
364 }
365
366 /* Apply BIOS defaults */
367 mBIOSSettings->i_applyDefaults(aOsType);
368
369 /* Apply network adapters defaults */
370 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
371 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
372
373 /* Apply serial port defaults */
374 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
375 mSerialPorts[slot]->i_applyDefaults(aOsType);
376
377 /* Apply parallel port defaults */
378 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
379 mParallelPorts[slot]->i_applyDefaults();
380
381 /* At this point the changing of the current state modification
382 * flag is allowed. */
383 i_allowStateModification();
384
385 /* commit all changes made during the initialization */
386 i_commit();
387 }
388
389 /* Confirm a successful initialization when it's the case */
390 if (SUCCEEDED(rc))
391 {
392 if (mData->mAccessible)
393 autoInitSpan.setSucceeded();
394 else
395 autoInitSpan.setLimited();
396 }
397
398 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
399 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
400 mData->mRegistered,
401 mData->mAccessible,
402 rc));
403
404 LogFlowThisFuncLeave();
405
406 return rc;
407}
408
409/**
410 * Initializes a new instance with data from machine XML (formerly Init_Registered).
411 * Gets called in two modes:
412 *
413 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
414 * UUID is specified and we mark the machine as "registered";
415 *
416 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
417 * and the machine remains unregistered until RegisterMachine() is called.
418 *
419 * @param aParent Associated parent object
420 * @param strConfigFile Local file system path to the VM settings file (can
421 * be relative to the VirtualBox config directory).
422 * @param aId UUID of the machine or NULL (see above).
423 *
424 * @return Success indicator. if not S_OK, the machine object is invalid
425 */
426HRESULT Machine::initFromSettings(VirtualBox *aParent,
427 const Utf8Str &strConfigFile,
428 const Guid *aId)
429{
430 LogFlowThisFuncEnter();
431 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
432
433 /* Enclose the state transition NotReady->InInit->Ready */
434 AutoInitSpan autoInitSpan(this);
435 AssertReturn(autoInitSpan.isOk(), E_FAIL);
436
437 HRESULT rc = initImpl(aParent, strConfigFile);
438 if (FAILED(rc)) return rc;
439
440 if (aId)
441 {
442 // loading a registered VM:
443 unconst(mData->mUuid) = *aId;
444 mData->mRegistered = TRUE;
445 // now load the settings from XML:
446 rc = i_registeredInit();
447 // this calls initDataAndChildObjects() and loadSettings()
448 }
449 else
450 {
451 // opening an unregistered VM (VirtualBox::OpenMachine()):
452 rc = initDataAndChildObjects();
453
454 if (SUCCEEDED(rc))
455 {
456 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
457 mData->mAccessible = TRUE;
458
459 try
460 {
461 // load and parse machine XML; this will throw on XML or logic errors
462 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
463
464 // reject VM UUID duplicates, they can happen if someone
465 // tries to register an already known VM config again
466 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
467 true /* fPermitInaccessible */,
468 false /* aDoSetError */,
469 NULL) != VBOX_E_OBJECT_NOT_FOUND)
470 {
471 throw setError(E_FAIL,
472 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
473 mData->m_strConfigFile.c_str());
474 }
475
476 // use UUID from machine config
477 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
478
479 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
480 NULL /* puuidRegistry */);
481 if (FAILED(rc)) throw rc;
482
483 /* At this point the changing of the current state modification
484 * flag is allowed. */
485 i_allowStateModification();
486
487 i_commit();
488 }
489 catch (HRESULT err)
490 {
491 /* we assume that error info is set by the thrower */
492 rc = err;
493 }
494 catch (...)
495 {
496 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
497 }
498 }
499 }
500
501 /* Confirm a successful initialization when it's the case */
502 if (SUCCEEDED(rc))
503 {
504 if (mData->mAccessible)
505 autoInitSpan.setSucceeded();
506 else
507 {
508 autoInitSpan.setLimited();
509
510 // uninit media from this machine's media registry, or else
511 // reloading the settings will fail
512 mParent->i_unregisterMachineMedia(i_getId());
513 }
514 }
515
516 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
517 "rc=%08X\n",
518 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
519 mData->mRegistered, mData->mAccessible, rc));
520
521 LogFlowThisFuncLeave();
522
523 return rc;
524}
525
526/**
527 * Initializes a new instance from a machine config that is already in memory
528 * (import OVF case). Since we are importing, the UUID in the machine
529 * config is ignored and we always generate a fresh one.
530 *
531 * @param aParent Associated parent object.
532 * @param strName Name for the new machine; this overrides what is specified in config.
533 * @param strSettingsFilename File name of .vbox file.
534 * @param config Machine configuration loaded and parsed from XML.
535 *
536 * @return Success indicator. if not S_OK, the machine object is invalid
537 */
538HRESULT Machine::init(VirtualBox *aParent,
539 const Utf8Str &strName,
540 const Utf8Str &strSettingsFilename,
541 const settings::MachineConfigFile &config)
542{
543 LogFlowThisFuncEnter();
544
545 /* Enclose the state transition NotReady->InInit->Ready */
546 AutoInitSpan autoInitSpan(this);
547 AssertReturn(autoInitSpan.isOk(), E_FAIL);
548
549 HRESULT rc = initImpl(aParent, strSettingsFilename);
550 if (FAILED(rc)) return rc;
551
552 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
553 if (FAILED(rc)) return rc;
554
555 rc = initDataAndChildObjects();
556
557 if (SUCCEEDED(rc))
558 {
559 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
560 mData->mAccessible = TRUE;
561
562 // create empty machine config for instance data
563 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
564
565 // generate fresh UUID, ignore machine config
566 unconst(mData->mUuid).create();
567
568 rc = i_loadMachineDataFromSettings(config,
569 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
570
571 // override VM name as well, it may be different
572 mUserData->s.strName = strName;
573
574 if (SUCCEEDED(rc))
575 {
576 /* At this point the changing of the current state modification
577 * flag is allowed. */
578 i_allowStateModification();
579
580 /* commit all changes made during the initialization */
581 i_commit();
582 }
583 }
584
585 /* Confirm a successful initialization when it's the case */
586 if (SUCCEEDED(rc))
587 {
588 if (mData->mAccessible)
589 autoInitSpan.setSucceeded();
590 else
591 {
592 /* Ignore all errors from unregistering, they would destroy
593- * the more interesting error information we already have,
594- * pinpointing the issue with the VM config. */
595 ErrorInfoKeeper eik;
596
597 autoInitSpan.setLimited();
598
599 // uninit media from this machine's media registry, or else
600 // reloading the settings will fail
601 mParent->i_unregisterMachineMedia(i_getId());
602 }
603 }
604
605 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
606 "rc=%08X\n",
607 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
608 mData->mRegistered, mData->mAccessible, rc));
609
610 LogFlowThisFuncLeave();
611
612 return rc;
613}
614
615/**
616 * Shared code between the various init() implementations.
617 * @param aParent The VirtualBox object.
618 * @param strConfigFile Settings file.
619 * @return
620 */
621HRESULT Machine::initImpl(VirtualBox *aParent,
622 const Utf8Str &strConfigFile)
623{
624 LogFlowThisFuncEnter();
625
626 AssertReturn(aParent, E_INVALIDARG);
627 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
628
629 HRESULT rc = S_OK;
630
631 /* share the parent weakly */
632 unconst(mParent) = aParent;
633
634 /* allocate the essential machine data structure (the rest will be
635 * allocated later by initDataAndChildObjects() */
636 mData.allocate();
637
638 /* memorize the config file name (as provided) */
639 mData->m_strConfigFile = strConfigFile;
640
641 /* get the full file name */
642 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
643 if (RT_FAILURE(vrc1))
644 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
645 tr("Invalid machine settings file name '%s' (%Rrc)"),
646 strConfigFile.c_str(),
647 vrc1);
648
649 LogFlowThisFuncLeave();
650
651 return rc;
652}
653
654/**
655 * Tries to create a machine settings file in the path stored in the machine
656 * instance data. Used when a new machine is created to fail gracefully if
657 * the settings file could not be written (e.g. because machine dir is read-only).
658 * @return
659 */
660HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
661{
662 HRESULT rc = S_OK;
663
664 // when we create a new machine, we must be able to create the settings file
665 RTFILE f = NIL_RTFILE;
666 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
667 if ( RT_SUCCESS(vrc)
668 || vrc == VERR_SHARING_VIOLATION
669 )
670 {
671 if (RT_SUCCESS(vrc))
672 RTFileClose(f);
673 if (!fForceOverwrite)
674 rc = setError(VBOX_E_FILE_ERROR,
675 tr("Machine settings file '%s' already exists"),
676 mData->m_strConfigFileFull.c_str());
677 else
678 {
679 /* try to delete the config file, as otherwise the creation
680 * of a new settings file will fail. */
681 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
682 if (RT_FAILURE(vrc2))
683 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
684 tr("Could not delete the existing settings file '%s' (%Rrc)"),
685 mData->m_strConfigFileFull.c_str(), vrc2);
686 }
687 }
688 else if ( vrc != VERR_FILE_NOT_FOUND
689 && vrc != VERR_PATH_NOT_FOUND
690 )
691 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
692 tr("Invalid machine settings file name '%s' (%Rrc)"),
693 mData->m_strConfigFileFull.c_str(),
694 vrc);
695 return rc;
696}
697
698/**
699 * Initializes the registered machine by loading the settings file.
700 * This method is separated from #init() in order to make it possible to
701 * retry the operation after VirtualBox startup instead of refusing to
702 * startup the whole VirtualBox server in case if the settings file of some
703 * registered VM is invalid or inaccessible.
704 *
705 * @note Must be always called from this object's write lock
706 * (unless called from #init() that doesn't need any locking).
707 * @note Locks the mUSBController method for writing.
708 * @note Subclasses must not call this method.
709 */
710HRESULT Machine::i_registeredInit()
711{
712 AssertReturn(!i_isSessionMachine(), E_FAIL);
713 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
714 AssertReturn(mData->mUuid.isValid(), E_FAIL);
715 AssertReturn(!mData->mAccessible, E_FAIL);
716
717 HRESULT rc = initDataAndChildObjects();
718
719 if (SUCCEEDED(rc))
720 {
721 /* Temporarily reset the registered flag in order to let setters
722 * potentially called from loadSettings() succeed (isMutable() used in
723 * all setters will return FALSE for a Machine instance if mRegistered
724 * is TRUE). */
725 mData->mRegistered = FALSE;
726
727 try
728 {
729 // load and parse machine XML; this will throw on XML or logic errors
730 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
731
732 if (mData->mUuid != mData->pMachineConfigFile->uuid)
733 throw setError(E_FAIL,
734 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
735 mData->pMachineConfigFile->uuid.raw(),
736 mData->m_strConfigFileFull.c_str(),
737 mData->mUuid.toString().c_str(),
738 mParent->i_settingsFilePath().c_str());
739
740 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
741 NULL /* const Guid *puuidRegistry */);
742 if (FAILED(rc)) throw rc;
743 }
744 catch (HRESULT err)
745 {
746 /* we assume that error info is set by the thrower */
747 rc = err;
748 }
749 catch (...)
750 {
751 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
752 }
753
754 /* Restore the registered flag (even on failure) */
755 mData->mRegistered = TRUE;
756 }
757
758 if (SUCCEEDED(rc))
759 {
760 /* Set mAccessible to TRUE only if we successfully locked and loaded
761 * the settings file */
762 mData->mAccessible = TRUE;
763
764 /* commit all changes made during loading the settings file */
765 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
766 /// @todo r=klaus for some reason the settings loading logic backs up
767 // the settings, and therefore a commit is needed. Should probably be changed.
768 }
769 else
770 {
771 /* If the machine is registered, then, instead of returning a
772 * failure, we mark it as inaccessible and set the result to
773 * success to give it a try later */
774
775 /* fetch the current error info */
776 mData->mAccessError = com::ErrorInfo();
777 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
778
779 /* rollback all changes */
780 i_rollback(false /* aNotify */);
781
782 // uninit media from this machine's media registry, or else
783 // reloading the settings will fail
784 mParent->i_unregisterMachineMedia(i_getId());
785
786 /* uninitialize the common part to make sure all data is reset to
787 * default (null) values */
788 uninitDataAndChildObjects();
789
790 rc = S_OK;
791 }
792
793 return rc;
794}
795
796/**
797 * Uninitializes the instance.
798 * Called either from FinalRelease() or by the parent when it gets destroyed.
799 *
800 * @note The caller of this method must make sure that this object
801 * a) doesn't have active callers on the current thread and b) is not locked
802 * by the current thread; otherwise uninit() will hang either a) due to
803 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
804 * a dead-lock caused by this thread waiting for all callers on the other
805 * threads are done but preventing them from doing so by holding a lock.
806 */
807void Machine::uninit()
808{
809 LogFlowThisFuncEnter();
810
811 Assert(!isWriteLockOnCurrentThread());
812
813 Assert(!uRegistryNeedsSaving);
814 if (uRegistryNeedsSaving)
815 {
816 AutoCaller autoCaller(this);
817 if (SUCCEEDED(autoCaller.rc()))
818 {
819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
820 i_saveSettings(NULL, Machine::SaveS_Force);
821 }
822 }
823
824 /* Enclose the state transition Ready->InUninit->NotReady */
825 AutoUninitSpan autoUninitSpan(this);
826 if (autoUninitSpan.uninitDone())
827 return;
828
829 Assert(!i_isSnapshotMachine());
830 Assert(!i_isSessionMachine());
831 Assert(!!mData);
832
833 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
834 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
835
836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
837
838 if (!mData->mSession.mMachine.isNull())
839 {
840 /* Theoretically, this can only happen if the VirtualBox server has been
841 * terminated while there were clients running that owned open direct
842 * sessions. Since in this case we are definitely called by
843 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
844 * won't happen on the client watcher thread (because it has a
845 * VirtualBox caller for the duration of the
846 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
847 * cannot happen until the VirtualBox caller is released). This is
848 * important, because SessionMachine::uninit() cannot correctly operate
849 * after we return from this method (it expects the Machine instance is
850 * still valid). We'll call it ourselves below.
851 */
852 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
853 (SessionMachine*)mData->mSession.mMachine));
854
855 if (Global::IsOnlineOrTransient(mData->mMachineState))
856 {
857 Log1WarningThisFunc(("Setting state to Aborted!\n"));
858 /* set machine state using SessionMachine reimplementation */
859 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
860 }
861
862 /*
863 * Uninitialize SessionMachine using public uninit() to indicate
864 * an unexpected uninitialization.
865 */
866 mData->mSession.mMachine->uninit();
867 /* SessionMachine::uninit() must set mSession.mMachine to null */
868 Assert(mData->mSession.mMachine.isNull());
869 }
870
871 // uninit media from this machine's media registry, if they're still there
872 Guid uuidMachine(i_getId());
873
874 /* the lock is no more necessary (SessionMachine is uninitialized) */
875 alock.release();
876
877 /* XXX This will fail with
878 * "cannot be closed because it is still attached to 1 virtual machines"
879 * because at this point we did not call uninitDataAndChildObjects() yet
880 * and therefore also removeBackReference() for all these mediums was not called! */
881
882 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
883 mParent->i_unregisterMachineMedia(uuidMachine);
884
885 // has machine been modified?
886 if (mData->flModifications)
887 {
888 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
889 i_rollback(false /* aNotify */);
890 }
891
892 if (mData->mAccessible)
893 uninitDataAndChildObjects();
894
895 /* free the essential data structure last */
896 mData.free();
897
898 LogFlowThisFuncLeave();
899}
900
901// Wrapped IMachine properties
902/////////////////////////////////////////////////////////////////////////////
903HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
904{
905 /* mParent is constant during life time, no need to lock */
906 ComObjPtr<VirtualBox> pVirtualBox(mParent);
907 aParent = pVirtualBox;
908
909 return S_OK;
910}
911
912
913HRESULT Machine::getAccessible(BOOL *aAccessible)
914{
915 /* In some cases (medium registry related), it is necessary to be able to
916 * go through the list of all machines. Happens when an inaccessible VM
917 * has a sensible medium registry. */
918 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
920
921 HRESULT rc = S_OK;
922
923 if (!mData->mAccessible)
924 {
925 /* try to initialize the VM once more if not accessible */
926
927 AutoReinitSpan autoReinitSpan(this);
928 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
929
930#ifdef DEBUG
931 LogFlowThisFunc(("Dumping media backreferences\n"));
932 mParent->i_dumpAllBackRefs();
933#endif
934
935 if (mData->pMachineConfigFile)
936 {
937 // reset the XML file to force loadSettings() (called from i_registeredInit())
938 // to parse it again; the file might have changed
939 delete mData->pMachineConfigFile;
940 mData->pMachineConfigFile = NULL;
941 }
942
943 rc = i_registeredInit();
944
945 if (SUCCEEDED(rc) && mData->mAccessible)
946 {
947 autoReinitSpan.setSucceeded();
948
949 /* make sure interesting parties will notice the accessibility
950 * state change */
951 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
952 mParent->i_onMachineDataChange(mData->mUuid);
953 }
954 }
955
956 if (SUCCEEDED(rc))
957 *aAccessible = mData->mAccessible;
958
959 LogFlowThisFuncLeave();
960
961 return rc;
962}
963
964HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
965{
966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
967
968 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
969 {
970 /* return shortly */
971 aAccessError = NULL;
972 return S_OK;
973 }
974
975 HRESULT rc = S_OK;
976
977 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
978 rc = errorInfo.createObject();
979 if (SUCCEEDED(rc))
980 {
981 errorInfo->init(mData->mAccessError.getResultCode(),
982 mData->mAccessError.getInterfaceID().ref(),
983 Utf8Str(mData->mAccessError.getComponent()).c_str(),
984 Utf8Str(mData->mAccessError.getText()));
985 aAccessError = errorInfo;
986 }
987
988 return rc;
989}
990
991HRESULT Machine::getName(com::Utf8Str &aName)
992{
993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 aName = mUserData->s.strName;
996
997 return S_OK;
998}
999
1000HRESULT Machine::setName(const com::Utf8Str &aName)
1001{
1002 // prohibit setting a UUID only as the machine name, or else it can
1003 // never be found by findMachine()
1004 Guid test(aName);
1005
1006 if (test.isValid())
1007 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1008
1009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 HRESULT rc = i_checkStateDependency(MutableStateDep);
1012 if (FAILED(rc)) return rc;
1013
1014 i_setModified(IsModified_MachineData);
1015 mUserData.backup();
1016 mUserData->s.strName = aName;
1017
1018 return S_OK;
1019}
1020
1021HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1022{
1023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1024
1025 aDescription = mUserData->s.strDescription;
1026
1027 return S_OK;
1028}
1029
1030HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1031{
1032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1033
1034 // this can be done in principle in any state as it doesn't affect the VM
1035 // significantly, but play safe by not messing around while complex
1036 // activities are going on
1037 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1038 if (FAILED(rc)) return rc;
1039
1040 i_setModified(IsModified_MachineData);
1041 mUserData.backup();
1042 mUserData->s.strDescription = aDescription;
1043
1044 return S_OK;
1045}
1046
1047HRESULT Machine::getId(com::Guid &aId)
1048{
1049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1050
1051 aId = mData->mUuid;
1052
1053 return S_OK;
1054}
1055
1056HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1057{
1058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1059 aGroups.resize(mUserData->s.llGroups.size());
1060 size_t i = 0;
1061 for (StringsList::const_iterator
1062 it = mUserData->s.llGroups.begin();
1063 it != mUserData->s.llGroups.end();
1064 ++it, ++i)
1065 aGroups[i] = (*it);
1066
1067 return S_OK;
1068}
1069
1070HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1071{
1072 StringsList llGroups;
1073 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1074 if (FAILED(rc))
1075 return rc;
1076
1077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 rc = i_checkStateDependency(MutableOrSavedStateDep);
1080 if (FAILED(rc)) return rc;
1081
1082 i_setModified(IsModified_MachineData);
1083 mUserData.backup();
1084 mUserData->s.llGroups = llGroups;
1085
1086 return S_OK;
1087}
1088
1089HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1090{
1091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1092
1093 aOSTypeId = mUserData->s.strOsType;
1094
1095 return S_OK;
1096}
1097
1098HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1099{
1100 /* look up the object by Id to check it is valid */
1101 ComObjPtr<GuestOSType> pGuestOSType;
1102 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1103
1104 /* when setting, always use the "etalon" value for consistency -- lookup
1105 * by ID is case-insensitive and the input value may have different case */
1106 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1107
1108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1109
1110 HRESULT rc = i_checkStateDependency(MutableStateDep);
1111 if (FAILED(rc)) return rc;
1112
1113 i_setModified(IsModified_MachineData);
1114 mUserData.backup();
1115 mUserData->s.strOsType = osTypeId;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1121{
1122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 *aFirmwareType = mHWData->mFirmwareType;
1125
1126 return S_OK;
1127}
1128
1129HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1130{
1131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 HRESULT rc = i_checkStateDependency(MutableStateDep);
1134 if (FAILED(rc)) return rc;
1135
1136 i_setModified(IsModified_MachineData);
1137 mHWData.backup();
1138 mHWData->mFirmwareType = aFirmwareType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1144{
1145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1153{
1154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 HRESULT rc = i_checkStateDependency(MutableStateDep);
1157 if (FAILED(rc)) return rc;
1158
1159 i_setModified(IsModified_MachineData);
1160 mHWData.backup();
1161 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1167{
1168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 *aPointingHIDType = mHWData->mPointingHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1176{
1177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 HRESULT rc = i_checkStateDependency(MutableStateDep);
1180 if (FAILED(rc)) return rc;
1181
1182 i_setModified(IsModified_MachineData);
1183 mHWData.backup();
1184 mHWData->mPointingHIDType = aPointingHIDType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1190{
1191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 *aChipsetType = mHWData->mChipsetType;
1194
1195 return S_OK;
1196}
1197
1198HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1199{
1200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1201
1202 HRESULT rc = i_checkStateDependency(MutableStateDep);
1203 if (FAILED(rc)) return rc;
1204
1205 if (aChipsetType != mHWData->mChipsetType)
1206 {
1207 i_setModified(IsModified_MachineData);
1208 mHWData.backup();
1209 mHWData->mChipsetType = aChipsetType;
1210
1211 // Resize network adapter array, to be finalized on commit/rollback.
1212 // We must not throw away entries yet, otherwise settings are lost
1213 // without a way to roll back.
1214 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1215 size_t oldCount = mNetworkAdapters.size();
1216 if (newCount > oldCount)
1217 {
1218 mNetworkAdapters.resize(newCount);
1219 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1220 {
1221 unconst(mNetworkAdapters[slot]).createObject();
1222 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1223 }
1224 }
1225 }
1226
1227 return S_OK;
1228}
1229
1230HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1231{
1232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 aParavirtDebug = mHWData->mParavirtDebug;
1235 return S_OK;
1236}
1237
1238HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1239{
1240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1241
1242 HRESULT rc = i_checkStateDependency(MutableStateDep);
1243 if (FAILED(rc)) return rc;
1244
1245 /** @todo Parse/validate options? */
1246 if (aParavirtDebug != mHWData->mParavirtDebug)
1247 {
1248 i_setModified(IsModified_MachineData);
1249 mHWData.backup();
1250 mHWData->mParavirtDebug = aParavirtDebug;
1251 }
1252
1253 return S_OK;
1254}
1255
1256HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1257{
1258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1259
1260 *aParavirtProvider = mHWData->mParavirtProvider;
1261
1262 return S_OK;
1263}
1264
1265HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1266{
1267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1268
1269 HRESULT rc = i_checkStateDependency(MutableStateDep);
1270 if (FAILED(rc)) return rc;
1271
1272 if (aParavirtProvider != mHWData->mParavirtProvider)
1273 {
1274 i_setModified(IsModified_MachineData);
1275 mHWData.backup();
1276 mHWData->mParavirtProvider = aParavirtProvider;
1277 }
1278
1279 return S_OK;
1280}
1281
1282HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1283{
1284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1285
1286 *aParavirtProvider = mHWData->mParavirtProvider;
1287 switch (mHWData->mParavirtProvider)
1288 {
1289 case ParavirtProvider_None:
1290 case ParavirtProvider_HyperV:
1291 case ParavirtProvider_KVM:
1292 case ParavirtProvider_Minimal:
1293 break;
1294
1295 /* Resolve dynamic provider types to the effective types. */
1296 default:
1297 {
1298 ComObjPtr<GuestOSType> pGuestOSType;
1299 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1300 pGuestOSType);
1301 if (FAILED(hrc2) || pGuestOSType.isNull())
1302 {
1303 *aParavirtProvider = ParavirtProvider_None;
1304 break;
1305 }
1306
1307 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1308 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1309
1310 switch (mHWData->mParavirtProvider)
1311 {
1312 case ParavirtProvider_Legacy:
1313 {
1314 if (fOsXGuest)
1315 *aParavirtProvider = ParavirtProvider_Minimal;
1316 else
1317 *aParavirtProvider = ParavirtProvider_None;
1318 break;
1319 }
1320
1321 case ParavirtProvider_Default:
1322 {
1323 if (fOsXGuest)
1324 *aParavirtProvider = ParavirtProvider_Minimal;
1325 else if ( mUserData->s.strOsType == "Windows10"
1326 || mUserData->s.strOsType == "Windows10_64"
1327 || mUserData->s.strOsType == "Windows81"
1328 || mUserData->s.strOsType == "Windows81_64"
1329 || mUserData->s.strOsType == "Windows8"
1330 || mUserData->s.strOsType == "Windows8_64"
1331 || mUserData->s.strOsType == "Windows7"
1332 || mUserData->s.strOsType == "Windows7_64"
1333 || mUserData->s.strOsType == "WindowsVista"
1334 || mUserData->s.strOsType == "WindowsVista_64"
1335 || mUserData->s.strOsType == "Windows2012"
1336 || mUserData->s.strOsType == "Windows2012_64"
1337 || mUserData->s.strOsType == "Windows2008"
1338 || mUserData->s.strOsType == "Windows2008_64")
1339 {
1340 *aParavirtProvider = ParavirtProvider_HyperV;
1341 }
1342 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1343 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1344 || mUserData->s.strOsType == "Linux"
1345 || mUserData->s.strOsType == "Linux_64"
1346 || mUserData->s.strOsType == "ArchLinux"
1347 || mUserData->s.strOsType == "ArchLinux_64"
1348 || mUserData->s.strOsType == "Debian"
1349 || mUserData->s.strOsType == "Debian_64"
1350 || mUserData->s.strOsType == "Fedora"
1351 || mUserData->s.strOsType == "Fedora_64"
1352 || mUserData->s.strOsType == "Gentoo"
1353 || mUserData->s.strOsType == "Gentoo_64"
1354 || mUserData->s.strOsType == "Mandriva"
1355 || mUserData->s.strOsType == "Mandriva_64"
1356 || mUserData->s.strOsType == "OpenSUSE"
1357 || mUserData->s.strOsType == "OpenSUSE_64"
1358 || mUserData->s.strOsType == "Oracle"
1359 || mUserData->s.strOsType == "Oracle_64"
1360 || mUserData->s.strOsType == "RedHat"
1361 || mUserData->s.strOsType == "RedHat_64"
1362 || mUserData->s.strOsType == "Turbolinux"
1363 || mUserData->s.strOsType == "Turbolinux_64"
1364 || mUserData->s.strOsType == "Ubuntu"
1365 || mUserData->s.strOsType == "Ubuntu_64"
1366 || mUserData->s.strOsType == "Xandros"
1367 || mUserData->s.strOsType == "Xandros_64")
1368 {
1369 *aParavirtProvider = ParavirtProvider_KVM;
1370 }
1371 else
1372 *aParavirtProvider = ParavirtProvider_None;
1373 break;
1374 }
1375
1376 default: AssertFailedBreak(); /* Shut up MSC. */
1377 }
1378 break;
1379 }
1380 }
1381
1382 Assert( *aParavirtProvider == ParavirtProvider_None
1383 || *aParavirtProvider == ParavirtProvider_Minimal
1384 || *aParavirtProvider == ParavirtProvider_HyperV
1385 || *aParavirtProvider == ParavirtProvider_KVM);
1386 return S_OK;
1387}
1388
1389HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1390{
1391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 aHardwareVersion = mHWData->mHWVersion;
1394
1395 return S_OK;
1396}
1397
1398HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1399{
1400 /* check known version */
1401 Utf8Str hwVersion = aHardwareVersion;
1402 if ( hwVersion.compare("1") != 0
1403 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1404 return setError(E_INVALIDARG,
1405 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1406
1407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1408
1409 HRESULT rc = i_checkStateDependency(MutableStateDep);
1410 if (FAILED(rc)) return rc;
1411
1412 i_setModified(IsModified_MachineData);
1413 mHWData.backup();
1414 mHWData->mHWVersion = aHardwareVersion;
1415
1416 return S_OK;
1417}
1418
1419HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1420{
1421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1422
1423 if (!mHWData->mHardwareUUID.isZero())
1424 aHardwareUUID = mHWData->mHardwareUUID;
1425 else
1426 aHardwareUUID = mData->mUuid;
1427
1428 return S_OK;
1429}
1430
1431HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1432{
1433 if (!aHardwareUUID.isValid())
1434 return E_INVALIDARG;
1435
1436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1437
1438 HRESULT rc = i_checkStateDependency(MutableStateDep);
1439 if (FAILED(rc)) return rc;
1440
1441 i_setModified(IsModified_MachineData);
1442 mHWData.backup();
1443 if (aHardwareUUID == mData->mUuid)
1444 mHWData->mHardwareUUID.clear();
1445 else
1446 mHWData->mHardwareUUID = aHardwareUUID;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1452{
1453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 *aMemorySize = mHWData->mMemorySize;
1456
1457 return S_OK;
1458}
1459
1460HRESULT Machine::setMemorySize(ULONG aMemorySize)
1461{
1462 /* check RAM limits */
1463 if ( aMemorySize < MM_RAM_MIN_IN_MB
1464 || aMemorySize > MM_RAM_MAX_IN_MB
1465 )
1466 return setError(E_INVALIDARG,
1467 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1468 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1469
1470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1471
1472 HRESULT rc = i_checkStateDependency(MutableStateDep);
1473 if (FAILED(rc)) return rc;
1474
1475 i_setModified(IsModified_MachineData);
1476 mHWData.backup();
1477 mHWData->mMemorySize = aMemorySize;
1478
1479 return S_OK;
1480}
1481
1482HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1483{
1484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1485
1486 *aCPUCount = mHWData->mCPUCount;
1487
1488 return S_OK;
1489}
1490
1491HRESULT Machine::setCPUCount(ULONG aCPUCount)
1492{
1493 /* check CPU limits */
1494 if ( aCPUCount < SchemaDefs::MinCPUCount
1495 || aCPUCount > SchemaDefs::MaxCPUCount
1496 )
1497 return setError(E_INVALIDARG,
1498 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1499 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1500
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1504 if (mHWData->mCPUHotPlugEnabled)
1505 {
1506 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1507 {
1508 if (mHWData->mCPUAttached[idx])
1509 return setError(E_INVALIDARG,
1510 tr("There is still a CPU attached to socket %lu."
1511 "Detach the CPU before removing the socket"),
1512 aCPUCount, idx+1);
1513 }
1514 }
1515
1516 HRESULT rc = i_checkStateDependency(MutableStateDep);
1517 if (FAILED(rc)) return rc;
1518
1519 i_setModified(IsModified_MachineData);
1520 mHWData.backup();
1521 mHWData->mCPUCount = aCPUCount;
1522
1523 return S_OK;
1524}
1525
1526HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1527{
1528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1529
1530 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1531
1532 return S_OK;
1533}
1534
1535HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1536{
1537 HRESULT rc = S_OK;
1538
1539 /* check throttle limits */
1540 if ( aCPUExecutionCap < 1
1541 || aCPUExecutionCap > 100
1542 )
1543 return setError(E_INVALIDARG,
1544 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1545 aCPUExecutionCap, 1, 100);
1546
1547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
1549 alock.release();
1550 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1551 alock.acquire();
1552 if (FAILED(rc)) return rc;
1553
1554 i_setModified(IsModified_MachineData);
1555 mHWData.backup();
1556 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1557
1558 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1559 if (Global::IsOnline(mData->mMachineState))
1560 i_saveSettings(NULL);
1561
1562 return S_OK;
1563}
1564
1565HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1566{
1567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1568
1569 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1570
1571 return S_OK;
1572}
1573
1574HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1575{
1576 HRESULT rc = S_OK;
1577
1578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1579
1580 rc = i_checkStateDependency(MutableStateDep);
1581 if (FAILED(rc)) return rc;
1582
1583 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1584 {
1585 if (aCPUHotPlugEnabled)
1586 {
1587 i_setModified(IsModified_MachineData);
1588 mHWData.backup();
1589
1590 /* Add the amount of CPUs currently attached */
1591 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1592 mHWData->mCPUAttached[i] = true;
1593 }
1594 else
1595 {
1596 /*
1597 * We can disable hotplug only if the amount of maximum CPUs is equal
1598 * to the amount of attached CPUs
1599 */
1600 unsigned cCpusAttached = 0;
1601 unsigned iHighestId = 0;
1602
1603 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1604 {
1605 if (mHWData->mCPUAttached[i])
1606 {
1607 cCpusAttached++;
1608 iHighestId = i;
1609 }
1610 }
1611
1612 if ( (cCpusAttached != mHWData->mCPUCount)
1613 || (iHighestId >= mHWData->mCPUCount))
1614 return setError(E_INVALIDARG,
1615 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1616
1617 i_setModified(IsModified_MachineData);
1618 mHWData.backup();
1619 }
1620 }
1621
1622 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1623
1624 return rc;
1625}
1626
1627HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1628{
1629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1630
1631 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1632
1633 return S_OK;
1634}
1635
1636HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1637{
1638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1639
1640 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1641 if (SUCCEEDED(hrc))
1642 {
1643 i_setModified(IsModified_MachineData);
1644 mHWData.backup();
1645 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1646 }
1647 return hrc;
1648}
1649
1650HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1651{
1652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1653 aCPUProfile = mHWData->mCpuProfile;
1654 return S_OK;
1655}
1656
1657HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1658{
1659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1660 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1661 if (SUCCEEDED(hrc))
1662 {
1663 i_setModified(IsModified_MachineData);
1664 mHWData.backup();
1665 /* Empty equals 'host'. */
1666 if (aCPUProfile.isNotEmpty())
1667 mHWData->mCpuProfile = aCPUProfile;
1668 else
1669 mHWData->mCpuProfile = "host";
1670 }
1671 return hrc;
1672}
1673
1674HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1675{
1676#ifdef VBOX_WITH_USB_CARDREADER
1677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1678
1679 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1680
1681 return S_OK;
1682#else
1683 NOREF(aEmulatedUSBCardReaderEnabled);
1684 return E_NOTIMPL;
1685#endif
1686}
1687
1688HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1689{
1690#ifdef VBOX_WITH_USB_CARDREADER
1691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1692
1693 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1694 if (FAILED(rc)) return rc;
1695
1696 i_setModified(IsModified_MachineData);
1697 mHWData.backup();
1698 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1699
1700 return S_OK;
1701#else
1702 NOREF(aEmulatedUSBCardReaderEnabled);
1703 return E_NOTIMPL;
1704#endif
1705}
1706
1707HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1708{
1709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1710
1711 *aHPETEnabled = mHWData->mHPETEnabled;
1712
1713 return S_OK;
1714}
1715
1716HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1717{
1718 HRESULT rc = S_OK;
1719
1720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1721
1722 rc = i_checkStateDependency(MutableStateDep);
1723 if (FAILED(rc)) return rc;
1724
1725 i_setModified(IsModified_MachineData);
1726 mHWData.backup();
1727
1728 mHWData->mHPETEnabled = aHPETEnabled;
1729
1730 return rc;
1731}
1732
1733HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1734{
1735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1736
1737 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1738 return S_OK;
1739}
1740
1741HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1742{
1743 HRESULT rc = S_OK;
1744
1745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1746
1747 i_setModified(IsModified_MachineData);
1748 mHWData.backup();
1749 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1750
1751 alock.release();
1752 rc = i_onVideoCaptureChange();
1753 alock.acquire();
1754 if (FAILED(rc))
1755 {
1756 /*
1757 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1758 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1759 * determine if it should start or stop capturing. Therefore we need to manually
1760 * undo change.
1761 */
1762 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1763 return rc;
1764 }
1765
1766 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1767 if (Global::IsOnline(mData->mMachineState))
1768 i_saveSettings(NULL);
1769
1770 return rc;
1771}
1772
1773HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1774{
1775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1776 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1777 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1778 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1779 return S_OK;
1780}
1781
1782HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1783{
1784 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1785 bool fChanged = false;
1786
1787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1788
1789 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1790 {
1791 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1792 {
1793 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1794 fChanged = true;
1795 }
1796 }
1797 if (fChanged)
1798 {
1799 alock.release();
1800 HRESULT rc = i_onVideoCaptureChange();
1801 alock.acquire();
1802 if (FAILED(rc)) return rc;
1803 i_setModified(IsModified_MachineData);
1804
1805 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1806 if (Global::IsOnline(mData->mMachineState))
1807 i_saveSettings(NULL);
1808 }
1809
1810 return S_OK;
1811}
1812
1813HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1814{
1815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1816 if (mHWData->mVideoCaptureFile.isEmpty())
1817 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1818 else
1819 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1820 return S_OK;
1821}
1822
1823HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1824{
1825 Utf8Str strFile(aVideoCaptureFile);
1826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 if ( Global::IsOnline(mData->mMachineState)
1829 && mHWData->mVideoCaptureEnabled)
1830 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1831
1832 if (!RTPathStartsWithRoot(strFile.c_str()))
1833 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1834
1835 if (!strFile.isEmpty())
1836 {
1837 Utf8Str defaultFile;
1838 i_getDefaultVideoCaptureFile(defaultFile);
1839 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1840 strFile.setNull();
1841 }
1842
1843 i_setModified(IsModified_MachineData);
1844 mHWData.backup();
1845 mHWData->mVideoCaptureFile = strFile;
1846
1847 return S_OK;
1848}
1849
1850HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1851{
1852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1853 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1854 return S_OK;
1855}
1856
1857HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1858{
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 if ( Global::IsOnline(mData->mMachineState)
1862 && mHWData->mVideoCaptureEnabled)
1863 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1864
1865 i_setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1868
1869 return S_OK;
1870}
1871
1872HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1873{
1874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1875 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1876 return S_OK;
1877}
1878
1879HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1880{
1881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1882
1883 if ( Global::IsOnline(mData->mMachineState)
1884 && mHWData->mVideoCaptureEnabled)
1885 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1886
1887 i_setModified(IsModified_MachineData);
1888 mHWData.backup();
1889 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1890
1891 return S_OK;
1892}
1893
1894HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1895{
1896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1897 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1898 return S_OK;
1899}
1900
1901HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1902{
1903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1904
1905 if ( Global::IsOnline(mData->mMachineState)
1906 && mHWData->mVideoCaptureEnabled)
1907 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1908
1909 i_setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1912
1913 return S_OK;
1914}
1915
1916HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1917{
1918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1919 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1920 return S_OK;
1921}
1922
1923HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1924{
1925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 if ( Global::IsOnline(mData->mMachineState)
1928 && mHWData->mVideoCaptureEnabled)
1929 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1930
1931 i_setModified(IsModified_MachineData);
1932 mHWData.backup();
1933 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1934
1935 return S_OK;
1936}
1937
1938HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1939{
1940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1941 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1942 return S_OK;
1943}
1944
1945HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1946{
1947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1948
1949 if ( Global::IsOnline(mData->mMachineState)
1950 && mHWData->mVideoCaptureEnabled)
1951 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1952
1953 i_setModified(IsModified_MachineData);
1954 mHWData.backup();
1955 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1956
1957 return S_OK;
1958}
1959
1960HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1961{
1962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1963 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1964 return S_OK;
1965}
1966
1967HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1968{
1969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1970
1971 if ( Global::IsOnline(mData->mMachineState)
1972 && mHWData->mVideoCaptureEnabled)
1973 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1974
1975 i_setModified(IsModified_MachineData);
1976 mHWData.backup();
1977 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1978
1979 return S_OK;
1980}
1981
1982HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1983{
1984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1985
1986 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1987 return S_OK;
1988}
1989
1990HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1991{
1992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1993
1994 if ( Global::IsOnline(mData->mMachineState)
1995 && mHWData->mVideoCaptureEnabled)
1996 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1997
1998 i_setModified(IsModified_MachineData);
1999 mHWData.backup();
2000 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
2001
2002 return S_OK;
2003}
2004
2005HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
2006{
2007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2008
2009 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2010
2011 return S_OK;
2012}
2013
2014HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2015{
2016 switch (aGraphicsControllerType)
2017 {
2018 case GraphicsControllerType_Null:
2019 case GraphicsControllerType_VBoxVGA:
2020#ifdef VBOX_WITH_VMSVGA
2021 case GraphicsControllerType_VMSVGA:
2022#endif
2023 break;
2024 default:
2025 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2026 }
2027
2028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 HRESULT rc = i_checkStateDependency(MutableStateDep);
2031 if (FAILED(rc)) return rc;
2032
2033 i_setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2036
2037 return S_OK;
2038}
2039
2040HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2041{
2042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 *aVRAMSize = mHWData->mVRAMSize;
2045
2046 return S_OK;
2047}
2048
2049HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2050{
2051 /* check VRAM limits */
2052 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2053 return setError(E_INVALIDARG,
2054 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2055 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2056
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 HRESULT rc = i_checkStateDependency(MutableStateDep);
2060 if (FAILED(rc)) return rc;
2061
2062 i_setModified(IsModified_MachineData);
2063 mHWData.backup();
2064 mHWData->mVRAMSize = aVRAMSize;
2065
2066 return S_OK;
2067}
2068
2069/** @todo this method should not be public */
2070HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2071{
2072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2073
2074 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2075
2076 return S_OK;
2077}
2078
2079/**
2080 * Set the memory balloon size.
2081 *
2082 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2083 * we have to make sure that we never call IGuest from here.
2084 */
2085HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2086{
2087 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2088#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2089 /* check limits */
2090 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2091 return setError(E_INVALIDARG,
2092 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2093 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2094
2095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 i_setModified(IsModified_MachineData);
2098 mHWData.backup();
2099 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2100
2101 return S_OK;
2102#else
2103 NOREF(aMemoryBalloonSize);
2104 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2105#endif
2106}
2107
2108HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2109{
2110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2111
2112 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2113 return S_OK;
2114}
2115
2116HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2117{
2118#ifdef VBOX_WITH_PAGE_SHARING
2119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2122 i_setModified(IsModified_MachineData);
2123 mHWData.backup();
2124 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2125 return S_OK;
2126#else
2127 NOREF(aPageFusionEnabled);
2128 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2129#endif
2130}
2131
2132HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2133{
2134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2137
2138 return S_OK;
2139}
2140
2141HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2142{
2143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2144
2145 HRESULT rc = i_checkStateDependency(MutableStateDep);
2146 if (FAILED(rc)) return rc;
2147
2148 /** @todo check validity! */
2149
2150 i_setModified(IsModified_MachineData);
2151 mHWData.backup();
2152 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2153
2154 return S_OK;
2155}
2156
2157
2158HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2159{
2160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2161
2162 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2163
2164 return S_OK;
2165}
2166
2167HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2168{
2169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2170
2171 HRESULT rc = i_checkStateDependency(MutableStateDep);
2172 if (FAILED(rc)) return rc;
2173
2174 /** @todo check validity! */
2175 i_setModified(IsModified_MachineData);
2176 mHWData.backup();
2177 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2178
2179 return S_OK;
2180}
2181
2182HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2183{
2184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2185
2186 *aMonitorCount = mHWData->mMonitorCount;
2187
2188 return S_OK;
2189}
2190
2191HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2192{
2193 /* make sure monitor count is a sensible number */
2194 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2195 return setError(E_INVALIDARG,
2196 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2197 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2198
2199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2200
2201 HRESULT rc = i_checkStateDependency(MutableStateDep);
2202 if (FAILED(rc)) return rc;
2203
2204 i_setModified(IsModified_MachineData);
2205 mHWData.backup();
2206 mHWData->mMonitorCount = aMonitorCount;
2207
2208 return S_OK;
2209}
2210
2211HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2212{
2213 /* mBIOSSettings is constant during life time, no need to lock */
2214 aBIOSSettings = mBIOSSettings;
2215
2216 return S_OK;
2217}
2218
2219HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2220{
2221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2222
2223 switch (aProperty)
2224 {
2225 case CPUPropertyType_PAE:
2226 *aValue = mHWData->mPAEEnabled;
2227 break;
2228
2229 case CPUPropertyType_LongMode:
2230 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2231 *aValue = TRUE;
2232 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2233 *aValue = FALSE;
2234#if HC_ARCH_BITS == 64
2235 else
2236 *aValue = TRUE;
2237#else
2238 else
2239 {
2240 *aValue = FALSE;
2241
2242 ComObjPtr<GuestOSType> pGuestOSType;
2243 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2244 pGuestOSType);
2245 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2246 {
2247 if (pGuestOSType->i_is64Bit())
2248 {
2249 ComObjPtr<Host> pHost = mParent->i_host();
2250 alock.release();
2251
2252 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2253 if (FAILED(hrc2))
2254 *aValue = FALSE;
2255 }
2256 }
2257 }
2258#endif
2259 break;
2260
2261 case CPUPropertyType_TripleFaultReset:
2262 *aValue = mHWData->mTripleFaultReset;
2263 break;
2264
2265 case CPUPropertyType_APIC:
2266 *aValue = mHWData->mAPIC;
2267 break;
2268
2269 case CPUPropertyType_X2APIC:
2270 *aValue = mHWData->mX2APIC;
2271 break;
2272
2273 case CPUPropertyType_IBPBOnVMExit:
2274 *aValue = mHWData->mIBPBOnVMExit;
2275 break;
2276
2277 case CPUPropertyType_IBPBOnVMEntry:
2278 *aValue = mHWData->mIBPBOnVMEntry;
2279 break;
2280
2281 case CPUPropertyType_SpecCtrl:
2282 *aValue = mHWData->mSpecCtrl;
2283 break;
2284
2285 case CPUPropertyType_SpecCtrlByHost:
2286 *aValue = mHWData->mSpecCtrlByHost;
2287 break;
2288
2289 case CPUPropertyType_HWVirt:
2290 *aValue = mHWData->mNestedHWVirt;
2291 break;
2292
2293 default:
2294 return E_INVALIDARG;
2295 }
2296 return S_OK;
2297}
2298
2299HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2300{
2301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2302
2303 HRESULT rc = i_checkStateDependency(MutableStateDep);
2304 if (FAILED(rc)) return rc;
2305
2306 switch (aProperty)
2307 {
2308 case CPUPropertyType_PAE:
2309 i_setModified(IsModified_MachineData);
2310 mHWData.backup();
2311 mHWData->mPAEEnabled = !!aValue;
2312 break;
2313
2314 case CPUPropertyType_LongMode:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2318 break;
2319
2320 case CPUPropertyType_TripleFaultReset:
2321 i_setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mTripleFaultReset = !!aValue;
2324 break;
2325
2326 case CPUPropertyType_APIC:
2327 if (mHWData->mX2APIC)
2328 aValue = TRUE;
2329 i_setModified(IsModified_MachineData);
2330 mHWData.backup();
2331 mHWData->mAPIC = !!aValue;
2332 break;
2333
2334 case CPUPropertyType_X2APIC:
2335 i_setModified(IsModified_MachineData);
2336 mHWData.backup();
2337 mHWData->mX2APIC = !!aValue;
2338 if (aValue)
2339 mHWData->mAPIC = !!aValue;
2340 break;
2341
2342 case CPUPropertyType_IBPBOnVMExit:
2343 i_setModified(IsModified_MachineData);
2344 mHWData.backup();
2345 mHWData->mIBPBOnVMExit = !!aValue;
2346 break;
2347
2348 case CPUPropertyType_IBPBOnVMEntry:
2349 i_setModified(IsModified_MachineData);
2350 mHWData.backup();
2351 mHWData->mIBPBOnVMEntry = !!aValue;
2352 break;
2353
2354 case CPUPropertyType_SpecCtrl:
2355 i_setModified(IsModified_MachineData);
2356 mHWData.backup();
2357 mHWData->mSpecCtrl = !!aValue;
2358 break;
2359
2360 case CPUPropertyType_SpecCtrlByHost:
2361 i_setModified(IsModified_MachineData);
2362 mHWData.backup();
2363 mHWData->mSpecCtrlByHost = !!aValue;
2364 break;
2365
2366 case CPUPropertyType_HWVirt:
2367 i_setModified(IsModified_MachineData);
2368 mHWData.backup();
2369 mHWData->mNestedHWVirt = !!aValue;
2370 break;
2371
2372 default:
2373 return E_INVALIDARG;
2374 }
2375 return S_OK;
2376}
2377
2378HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2379 ULONG *aValEcx, ULONG *aValEdx)
2380{
2381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2382 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2383 {
2384 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2385 it != mHWData->mCpuIdLeafList.end();
2386 ++it)
2387 {
2388 if (aOrdinal == 0)
2389 {
2390 const settings::CpuIdLeaf &rLeaf= *it;
2391 *aIdx = rLeaf.idx;
2392 *aSubIdx = rLeaf.idxSub;
2393 *aValEax = rLeaf.uEax;
2394 *aValEbx = rLeaf.uEbx;
2395 *aValEcx = rLeaf.uEcx;
2396 *aValEdx = rLeaf.uEdx;
2397 return S_OK;
2398 }
2399 aOrdinal--;
2400 }
2401 }
2402 return E_INVALIDARG;
2403}
2404
2405HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2406{
2407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 /*
2410 * Search the list.
2411 */
2412 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2413 {
2414 const settings::CpuIdLeaf &rLeaf= *it;
2415 if ( rLeaf.idx == aIdx
2416 && ( aSubIdx == UINT32_MAX
2417 || rLeaf.idxSub == aSubIdx) )
2418 {
2419 *aValEax = rLeaf.uEax;
2420 *aValEbx = rLeaf.uEbx;
2421 *aValEcx = rLeaf.uEcx;
2422 *aValEdx = rLeaf.uEdx;
2423 return S_OK;
2424 }
2425 }
2426
2427 return E_INVALIDARG;
2428}
2429
2430
2431HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2432{
2433 /*
2434 * Validate input before taking locks and checking state.
2435 */
2436 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2437 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2438 if ( aIdx >= UINT32_C(0x20)
2439 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2440 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2441 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2442
2443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2444 HRESULT rc = i_checkStateDependency(MutableStateDep);
2445 if (FAILED(rc)) return rc;
2446
2447 /*
2448 * Impose a maximum number of leaves.
2449 */
2450 if (mHWData->mCpuIdLeafList.size() > 256)
2451 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2452
2453 /*
2454 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2455 */
2456 i_setModified(IsModified_MachineData);
2457 mHWData.backup();
2458
2459 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2460 {
2461 settings::CpuIdLeaf &rLeaf= *it;
2462 if ( rLeaf.idx == aIdx
2463 && ( aSubIdx == UINT32_MAX
2464 || rLeaf.idxSub == aSubIdx) )
2465 it = mHWData->mCpuIdLeafList.erase(it);
2466 else
2467 ++it;
2468 }
2469
2470 settings::CpuIdLeaf NewLeaf;
2471 NewLeaf.idx = aIdx;
2472 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2473 NewLeaf.uEax = aValEax;
2474 NewLeaf.uEbx = aValEbx;
2475 NewLeaf.uEcx = aValEcx;
2476 NewLeaf.uEdx = aValEdx;
2477 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2478 return S_OK;
2479}
2480
2481HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2482{
2483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2484
2485 HRESULT rc = i_checkStateDependency(MutableStateDep);
2486 if (FAILED(rc)) return rc;
2487
2488 /*
2489 * Do the removal.
2490 */
2491 bool fModified = false;
2492 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2493 {
2494 settings::CpuIdLeaf &rLeaf= *it;
2495 if ( rLeaf.idx == aIdx
2496 && ( aSubIdx == UINT32_MAX
2497 || rLeaf.idxSub == aSubIdx) )
2498 {
2499 if (!fModified)
2500 {
2501 fModified = true;
2502 i_setModified(IsModified_MachineData);
2503 mHWData.backup();
2504 }
2505 it = mHWData->mCpuIdLeafList.erase(it);
2506 }
2507 else
2508 ++it;
2509 }
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::removeAllCPUIDLeaves()
2515{
2516 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 HRESULT rc = i_checkStateDependency(MutableStateDep);
2519 if (FAILED(rc)) return rc;
2520
2521 if (mHWData->mCpuIdLeafList.size() > 0)
2522 {
2523 i_setModified(IsModified_MachineData);
2524 mHWData.backup();
2525
2526 mHWData->mCpuIdLeafList.clear();
2527 }
2528
2529 return S_OK;
2530}
2531HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2532{
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 switch(aProperty)
2536 {
2537 case HWVirtExPropertyType_Enabled:
2538 *aValue = mHWData->mHWVirtExEnabled;
2539 break;
2540
2541 case HWVirtExPropertyType_VPID:
2542 *aValue = mHWData->mHWVirtExVPIDEnabled;
2543 break;
2544
2545 case HWVirtExPropertyType_NestedPaging:
2546 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2547 break;
2548
2549 case HWVirtExPropertyType_UnrestrictedExecution:
2550 *aValue = mHWData->mHWVirtExUXEnabled;
2551 break;
2552
2553 case HWVirtExPropertyType_LargePages:
2554 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2555#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2556 *aValue = FALSE;
2557#endif
2558 break;
2559
2560 case HWVirtExPropertyType_Force:
2561 *aValue = mHWData->mHWVirtExForceEnabled;
2562 break;
2563
2564 case HWVirtExPropertyType_UseNativeApi:
2565 *aValue = mHWData->mHWVirtExUseNativeApi;
2566 break;
2567
2568 default:
2569 return E_INVALIDARG;
2570 }
2571 return S_OK;
2572}
2573
2574HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2575{
2576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 HRESULT rc = i_checkStateDependency(MutableStateDep);
2579 if (FAILED(rc)) return rc;
2580
2581 switch (aProperty)
2582 {
2583 case HWVirtExPropertyType_Enabled:
2584 i_setModified(IsModified_MachineData);
2585 mHWData.backup();
2586 mHWData->mHWVirtExEnabled = !!aValue;
2587 break;
2588
2589 case HWVirtExPropertyType_VPID:
2590 i_setModified(IsModified_MachineData);
2591 mHWData.backup();
2592 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2593 break;
2594
2595 case HWVirtExPropertyType_NestedPaging:
2596 i_setModified(IsModified_MachineData);
2597 mHWData.backup();
2598 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2599 break;
2600
2601 case HWVirtExPropertyType_UnrestrictedExecution:
2602 i_setModified(IsModified_MachineData);
2603 mHWData.backup();
2604 mHWData->mHWVirtExUXEnabled = !!aValue;
2605 break;
2606
2607 case HWVirtExPropertyType_LargePages:
2608 i_setModified(IsModified_MachineData);
2609 mHWData.backup();
2610 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2611 break;
2612
2613 case HWVirtExPropertyType_Force:
2614 i_setModified(IsModified_MachineData);
2615 mHWData.backup();
2616 mHWData->mHWVirtExForceEnabled = !!aValue;
2617 break;
2618
2619 case HWVirtExPropertyType_UseNativeApi:
2620 i_setModified(IsModified_MachineData);
2621 mHWData.backup();
2622 mHWData->mHWVirtExUseNativeApi = !!aValue;
2623 break;
2624
2625 default:
2626 return E_INVALIDARG;
2627 }
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2642{
2643 /** @todo (r=dmik):
2644 * 1. Allow to change the name of the snapshot folder containing snapshots
2645 * 2. Rename the folder on disk instead of just changing the property
2646 * value (to be smart and not to leave garbage). Note that it cannot be
2647 * done here because the change may be rolled back. Thus, the right
2648 * place is #saveSettings().
2649 */
2650
2651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 HRESULT rc = i_checkStateDependency(MutableStateDep);
2654 if (FAILED(rc)) return rc;
2655
2656 if (!mData->mCurrentSnapshot.isNull())
2657 return setError(E_FAIL,
2658 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2659
2660 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2661
2662 if (strSnapshotFolder.isEmpty())
2663 strSnapshotFolder = "Snapshots";
2664 int vrc = i_calculateFullPath(strSnapshotFolder,
2665 strSnapshotFolder);
2666 if (RT_FAILURE(vrc))
2667 return setErrorBoth(E_FAIL, vrc,
2668 tr("Invalid snapshot folder '%s' (%Rrc)"),
2669 strSnapshotFolder.c_str(), vrc);
2670
2671 i_setModified(IsModified_MachineData);
2672 mUserData.backup();
2673
2674 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2680{
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 aMediumAttachments.resize(mMediumAttachments->size());
2684 size_t i = 0;
2685 for (MediumAttachmentList::const_iterator
2686 it = mMediumAttachments->begin();
2687 it != mMediumAttachments->end();
2688 ++it, ++i)
2689 aMediumAttachments[i] = *it;
2690
2691 return S_OK;
2692}
2693
2694HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2695{
2696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2697
2698 Assert(!!mVRDEServer);
2699
2700 aVRDEServer = mVRDEServer;
2701
2702 return S_OK;
2703}
2704
2705HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2706{
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 aAudioAdapter = mAudioAdapter;
2710
2711 return S_OK;
2712}
2713
2714HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2715{
2716#ifdef VBOX_WITH_VUSB
2717 clearError();
2718 MultiResult rc(S_OK);
2719
2720# ifdef VBOX_WITH_USB
2721 rc = mParent->i_host()->i_checkUSBProxyService();
2722 if (FAILED(rc)) return rc;
2723# endif
2724
2725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2726
2727 aUSBControllers.resize(mUSBControllers->size());
2728 size_t i = 0;
2729 for (USBControllerList::const_iterator
2730 it = mUSBControllers->begin();
2731 it != mUSBControllers->end();
2732 ++it, ++i)
2733 aUSBControllers[i] = *it;
2734
2735 return S_OK;
2736#else
2737 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2738 * extended error info to indicate that USB is simply not available
2739 * (w/o treating it as a failure), for example, as in OSE */
2740 NOREF(aUSBControllers);
2741 ReturnComNotImplemented();
2742#endif /* VBOX_WITH_VUSB */
2743}
2744
2745HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2746{
2747#ifdef VBOX_WITH_VUSB
2748 clearError();
2749 MultiResult rc(S_OK);
2750
2751# ifdef VBOX_WITH_USB
2752 rc = mParent->i_host()->i_checkUSBProxyService();
2753 if (FAILED(rc)) return rc;
2754# endif
2755
2756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2757
2758 aUSBDeviceFilters = mUSBDeviceFilters;
2759 return rc;
2760#else
2761 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2762 * extended error info to indicate that USB is simply not available
2763 * (w/o treating it as a failure), for example, as in OSE */
2764 NOREF(aUSBDeviceFilters);
2765 ReturnComNotImplemented();
2766#endif /* VBOX_WITH_VUSB */
2767}
2768
2769HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2770{
2771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2772
2773 aSettingsFilePath = mData->m_strConfigFileFull;
2774
2775 return S_OK;
2776}
2777
2778HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2779{
2780 RT_NOREF(aSettingsFilePath);
2781 ReturnComNotImplemented();
2782}
2783
2784HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2785{
2786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2789 if (FAILED(rc)) return rc;
2790
2791 if (!mData->pMachineConfigFile->fileExists())
2792 // this is a new machine, and no config file exists yet:
2793 *aSettingsModified = TRUE;
2794 else
2795 *aSettingsModified = (mData->flModifications != 0);
2796
2797 return S_OK;
2798}
2799
2800HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2801{
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 *aSessionState = mData->mSession.mState;
2805
2806 return S_OK;
2807}
2808
2809HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2810{
2811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 aSessionName = mData->mSession.mName;
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 *aSessionPID = mData->mSession.mPID;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::getState(MachineState_T *aState)
2828{
2829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2830
2831 *aState = mData->mMachineState;
2832 Assert(mData->mMachineState != MachineState_Null);
2833
2834 return S_OK;
2835}
2836
2837HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2838{
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2842
2843 return S_OK;
2844}
2845
2846HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2847{
2848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2849
2850 aStateFilePath = mSSData->strStateFilePath;
2851
2852 return S_OK;
2853}
2854
2855HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2856{
2857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 i_getLogFolder(aLogFolder);
2860
2861 return S_OK;
2862}
2863
2864HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2865{
2866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2867
2868 aCurrentSnapshot = mData->mCurrentSnapshot;
2869
2870 return S_OK;
2871}
2872
2873HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2874{
2875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2878 ? 0
2879 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2880
2881 return S_OK;
2882}
2883
2884HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2885{
2886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 /* Note: for machines with no snapshots, we always return FALSE
2889 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2890 * reasons :) */
2891
2892 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2893 ? FALSE
2894 : mData->mCurrentStateModified;
2895
2896 return S_OK;
2897}
2898
2899HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2900{
2901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 aSharedFolders.resize(mHWData->mSharedFolders.size());
2904 size_t i = 0;
2905 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2906 it = mHWData->mSharedFolders.begin();
2907 it != mHWData->mSharedFolders.end();
2908 ++it, ++i)
2909 aSharedFolders[i] = *it;
2910
2911 return S_OK;
2912}
2913
2914HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2915{
2916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2917
2918 *aClipboardMode = mHWData->mClipboardMode;
2919
2920 return S_OK;
2921}
2922
2923HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2924{
2925 HRESULT rc = S_OK;
2926
2927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 alock.release();
2930 rc = i_onClipboardModeChange(aClipboardMode);
2931 alock.acquire();
2932 if (FAILED(rc)) return rc;
2933
2934 i_setModified(IsModified_MachineData);
2935 mHWData.backup();
2936 mHWData->mClipboardMode = aClipboardMode;
2937
2938 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2939 if (Global::IsOnline(mData->mMachineState))
2940 i_saveSettings(NULL);
2941
2942 return S_OK;
2943}
2944
2945HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2946{
2947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2948
2949 *aDnDMode = mHWData->mDnDMode;
2950
2951 return S_OK;
2952}
2953
2954HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2955{
2956 HRESULT rc = S_OK;
2957
2958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2959
2960 alock.release();
2961 rc = i_onDnDModeChange(aDnDMode);
2962
2963 alock.acquire();
2964 if (FAILED(rc)) return rc;
2965
2966 i_setModified(IsModified_MachineData);
2967 mHWData.backup();
2968 mHWData->mDnDMode = aDnDMode;
2969
2970 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2971 if (Global::IsOnline(mData->mMachineState))
2972 i_saveSettings(NULL);
2973
2974 return S_OK;
2975}
2976
2977HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2978{
2979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 aStorageControllers.resize(mStorageControllers->size());
2982 size_t i = 0;
2983 for (StorageControllerList::const_iterator
2984 it = mStorageControllers->begin();
2985 it != mStorageControllers->end();
2986 ++it, ++i)
2987 aStorageControllers[i] = *it;
2988
2989 return S_OK;
2990}
2991
2992HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2993{
2994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2995
2996 *aEnabled = mUserData->s.fTeleporterEnabled;
2997
2998 return S_OK;
2999}
3000
3001HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3002{
3003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3004
3005 /* Only allow it to be set to true when PoweredOff or Aborted.
3006 (Clearing it is always permitted.) */
3007 if ( aTeleporterEnabled
3008 && mData->mRegistered
3009 && ( !i_isSessionMachine()
3010 || ( mData->mMachineState != MachineState_PoweredOff
3011 && mData->mMachineState != MachineState_Teleported
3012 && mData->mMachineState != MachineState_Aborted
3013 )
3014 )
3015 )
3016 return setError(VBOX_E_INVALID_VM_STATE,
3017 tr("The machine is not powered off (state is %s)"),
3018 Global::stringifyMachineState(mData->mMachineState));
3019
3020 i_setModified(IsModified_MachineData);
3021 mUserData.backup();
3022 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3023
3024 return S_OK;
3025}
3026
3027HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3028{
3029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3030
3031 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3032
3033 return S_OK;
3034}
3035
3036HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3037{
3038 if (aTeleporterPort >= _64K)
3039 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3040
3041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3044 if (FAILED(rc)) return rc;
3045
3046 i_setModified(IsModified_MachineData);
3047 mUserData.backup();
3048 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3049
3050 return S_OK;
3051}
3052
3053HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3054{
3055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3058
3059 return S_OK;
3060}
3061
3062HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3063{
3064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3067 if (FAILED(rc)) return rc;
3068
3069 i_setModified(IsModified_MachineData);
3070 mUserData.backup();
3071 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3072
3073 return S_OK;
3074}
3075
3076HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3077{
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3080
3081 return S_OK;
3082}
3083
3084HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3085{
3086 /*
3087 * Hash the password first.
3088 */
3089 com::Utf8Str aT = aTeleporterPassword;
3090
3091 if (!aT.isEmpty())
3092 {
3093 if (VBoxIsPasswordHashed(&aT))
3094 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3095 VBoxHashPassword(&aT);
3096 }
3097
3098 /*
3099 * Do the update.
3100 */
3101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3102 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3103 if (SUCCEEDED(hrc))
3104 {
3105 i_setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.strTeleporterPassword = aT;
3108 }
3109
3110 return hrc;
3111}
3112
3113HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3114{
3115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3116
3117 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3118 return S_OK;
3119}
3120
3121HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3122{
3123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 /** @todo deal with running state change. */
3126 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3127 if (FAILED(rc)) return rc;
3128
3129 i_setModified(IsModified_MachineData);
3130 mUserData.backup();
3131 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3132 return S_OK;
3133}
3134
3135HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3136{
3137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3140 return S_OK;
3141}
3142
3143HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3144{
3145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3146
3147 /** @todo deal with running state change. */
3148 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3149 if (FAILED(rc)) return rc;
3150
3151 i_setModified(IsModified_MachineData);
3152 mUserData.backup();
3153 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3154 return S_OK;
3155}
3156
3157HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3158{
3159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3162 return S_OK;
3163}
3164
3165HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3166{
3167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3168
3169 /** @todo deal with running state change. */
3170 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3171 if (FAILED(rc)) return rc;
3172
3173 i_setModified(IsModified_MachineData);
3174 mUserData.backup();
3175 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3176 return S_OK;
3177}
3178
3179HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3180{
3181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3182
3183 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3184
3185 return S_OK;
3186}
3187
3188HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3189{
3190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3191
3192 /** @todo deal with running state change. */
3193 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3194 if (FAILED(rc)) return rc;
3195
3196 i_setModified(IsModified_MachineData);
3197 mUserData.backup();
3198 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3199
3200 return S_OK;
3201}
3202
3203HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3204{
3205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3206
3207 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3208 return S_OK;
3209}
3210
3211HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3212{
3213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3214
3215 /** @todo deal with running state change. */
3216 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3217 if (FAILED(rc)) return rc;
3218
3219 i_setModified(IsModified_MachineData);
3220 mUserData.backup();
3221 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3222 return S_OK;
3223}
3224
3225HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3226{
3227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3228
3229 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3230
3231 return S_OK;
3232}
3233
3234HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3235{
3236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3237
3238 /* Only allow it to be set to true when PoweredOff or Aborted.
3239 (Clearing it is always permitted.) */
3240 if ( aRTCUseUTC
3241 && mData->mRegistered
3242 && ( !i_isSessionMachine()
3243 || ( mData->mMachineState != MachineState_PoweredOff
3244 && mData->mMachineState != MachineState_Teleported
3245 && mData->mMachineState != MachineState_Aborted
3246 )
3247 )
3248 )
3249 return setError(VBOX_E_INVALID_VM_STATE,
3250 tr("The machine is not powered off (state is %s)"),
3251 Global::stringifyMachineState(mData->mMachineState));
3252
3253 i_setModified(IsModified_MachineData);
3254 mUserData.backup();
3255 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3256
3257 return S_OK;
3258}
3259
3260HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3261{
3262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3263
3264 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3265
3266 return S_OK;
3267}
3268
3269HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3270{
3271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3272
3273 HRESULT rc = i_checkStateDependency(MutableStateDep);
3274 if (FAILED(rc)) return rc;
3275
3276 i_setModified(IsModified_MachineData);
3277 mHWData.backup();
3278 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3279
3280 return S_OK;
3281}
3282
3283HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3284{
3285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3286
3287 *aIOCacheSize = mHWData->mIOCacheSize;
3288
3289 return S_OK;
3290}
3291
3292HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3293{
3294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3295
3296 HRESULT rc = i_checkStateDependency(MutableStateDep);
3297 if (FAILED(rc)) return rc;
3298
3299 i_setModified(IsModified_MachineData);
3300 mHWData.backup();
3301 mHWData->mIOCacheSize = aIOCacheSize;
3302
3303 return S_OK;
3304}
3305
3306
3307/**
3308 * @note Locks objects!
3309 */
3310HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3311 LockType_T aLockType)
3312{
3313 /* check the session state */
3314 SessionState_T state;
3315 HRESULT rc = aSession->COMGETTER(State)(&state);
3316 if (FAILED(rc)) return rc;
3317
3318 if (state != SessionState_Unlocked)
3319 return setError(VBOX_E_INVALID_OBJECT_STATE,
3320 tr("The given session is busy"));
3321
3322 // get the client's IInternalSessionControl interface
3323 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3324 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3325 E_INVALIDARG);
3326
3327 // session name (only used in some code paths)
3328 Utf8Str strSessionName;
3329
3330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3331
3332 if (!mData->mRegistered)
3333 return setError(E_UNEXPECTED,
3334 tr("The machine '%s' is not registered"),
3335 mUserData->s.strName.c_str());
3336
3337 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3338
3339 SessionState_T oldState = mData->mSession.mState;
3340 /* Hack: in case the session is closing and there is a progress object
3341 * which allows waiting for the session to be closed, take the opportunity
3342 * and do a limited wait (max. 1 second). This helps a lot when the system
3343 * is busy and thus session closing can take a little while. */
3344 if ( mData->mSession.mState == SessionState_Unlocking
3345 && mData->mSession.mProgress)
3346 {
3347 alock.release();
3348 mData->mSession.mProgress->WaitForCompletion(1000);
3349 alock.acquire();
3350 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3351 }
3352
3353 // try again now
3354 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3355 // (i.e. session machine exists)
3356 && (aLockType == LockType_Shared) // caller wants a shared link to the
3357 // existing session that holds the write lock:
3358 )
3359 {
3360 // OK, share the session... we are now dealing with three processes:
3361 // 1) VBoxSVC (where this code runs);
3362 // 2) process C: the caller's client process (who wants a shared session);
3363 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3364
3365 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3366 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3367 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3368 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3369 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3370
3371 /*
3372 * Release the lock before calling the client process. It's safe here
3373 * since the only thing to do after we get the lock again is to add
3374 * the remote control to the list (which doesn't directly influence
3375 * anything).
3376 */
3377 alock.release();
3378
3379 // get the console of the session holding the write lock (this is a remote call)
3380 ComPtr<IConsole> pConsoleW;
3381 if (mData->mSession.mLockType == LockType_VM)
3382 {
3383 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3384 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3385 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3386 if (FAILED(rc))
3387 // the failure may occur w/o any error info (from RPC), so provide one
3388 return setError(VBOX_E_VM_ERROR,
3389 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3390 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3391 }
3392
3393 // share the session machine and W's console with the caller's session
3394 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3395 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3396 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3397
3398 if (FAILED(rc))
3399 // the failure may occur w/o any error info (from RPC), so provide one
3400 return setError(VBOX_E_VM_ERROR,
3401 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3402 alock.acquire();
3403
3404 // need to revalidate the state after acquiring the lock again
3405 if (mData->mSession.mState != SessionState_Locked)
3406 {
3407 pSessionControl->Uninitialize();
3408 return setError(VBOX_E_INVALID_SESSION_STATE,
3409 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3410 mUserData->s.strName.c_str());
3411 }
3412
3413 // add the caller's session to the list
3414 mData->mSession.mRemoteControls.push_back(pSessionControl);
3415 }
3416 else if ( mData->mSession.mState == SessionState_Locked
3417 || mData->mSession.mState == SessionState_Unlocking
3418 )
3419 {
3420 // sharing not permitted, or machine still unlocking:
3421 return setError(VBOX_E_INVALID_OBJECT_STATE,
3422 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3423 mUserData->s.strName.c_str());
3424 }
3425 else
3426 {
3427 // machine is not locked: then write-lock the machine (create the session machine)
3428
3429 // must not be busy
3430 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3431
3432 // get the caller's session PID
3433 RTPROCESS pid = NIL_RTPROCESS;
3434 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3435 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3436 Assert(pid != NIL_RTPROCESS);
3437
3438 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3439
3440 if (fLaunchingVMProcess)
3441 {
3442 if (mData->mSession.mPID == NIL_RTPROCESS)
3443 {
3444 // two or more clients racing for a lock, the one which set the
3445 // session state to Spawning will win, the others will get an
3446 // error as we can't decide here if waiting a little would help
3447 // (only for shared locks this would avoid an error)
3448 return setError(VBOX_E_INVALID_OBJECT_STATE,
3449 tr("The machine '%s' already has a lock request pending"),
3450 mUserData->s.strName.c_str());
3451 }
3452
3453 // this machine is awaiting for a spawning session to be opened:
3454 // then the calling process must be the one that got started by
3455 // LaunchVMProcess()
3456
3457 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3458 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3459
3460#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3461 /* Hardened windows builds spawns three processes when a VM is
3462 launched, the 3rd one is the one that will end up here. */
3463 RTPROCESS ppid;
3464 int rc = RTProcQueryParent(pid, &ppid);
3465 if (RT_SUCCESS(rc))
3466 rc = RTProcQueryParent(ppid, &ppid);
3467 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3468 || rc == VERR_ACCESS_DENIED)
3469 {
3470 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3471 mData->mSession.mPID = pid;
3472 }
3473#endif
3474
3475 if (mData->mSession.mPID != pid)
3476 return setError(E_ACCESSDENIED,
3477 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3478 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3479 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3480 }
3481
3482 // create the mutable SessionMachine from the current machine
3483 ComObjPtr<SessionMachine> sessionMachine;
3484 sessionMachine.createObject();
3485 rc = sessionMachine->init(this);
3486 AssertComRC(rc);
3487
3488 /* NOTE: doing return from this function after this point but
3489 * before the end is forbidden since it may call SessionMachine::uninit()
3490 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3491 * lock while still holding the Machine lock in alock so that a deadlock
3492 * is possible due to the wrong lock order. */
3493
3494 if (SUCCEEDED(rc))
3495 {
3496 /*
3497 * Set the session state to Spawning to protect against subsequent
3498 * attempts to open a session and to unregister the machine after
3499 * we release the lock.
3500 */
3501 SessionState_T origState = mData->mSession.mState;
3502 mData->mSession.mState = SessionState_Spawning;
3503
3504#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3505 /* Get the client token ID to be passed to the client process */
3506 Utf8Str strTokenId;
3507 sessionMachine->i_getTokenId(strTokenId);
3508 Assert(!strTokenId.isEmpty());
3509#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3510 /* Get the client token to be passed to the client process */
3511 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3512 /* The token is now "owned" by pToken, fix refcount */
3513 if (!pToken.isNull())
3514 pToken->Release();
3515#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3516
3517 /*
3518 * Release the lock before calling the client process -- it will call
3519 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3520 * because the state is Spawning, so that LaunchVMProcess() and
3521 * LockMachine() calls will fail. This method, called before we
3522 * acquire the lock again, will fail because of the wrong PID.
3523 *
3524 * Note that mData->mSession.mRemoteControls accessed outside
3525 * the lock may not be modified when state is Spawning, so it's safe.
3526 */
3527 alock.release();
3528
3529 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3530#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3531 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3532#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3533 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3534 /* Now the token is owned by the client process. */
3535 pToken.setNull();
3536#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3537 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3538
3539 /* The failure may occur w/o any error info (from RPC), so provide one */
3540 if (FAILED(rc))
3541 setError(VBOX_E_VM_ERROR,
3542 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3543
3544 // get session name, either to remember or to compare against
3545 // the already known session name.
3546 {
3547 Bstr bstrSessionName;
3548 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3549 if (SUCCEEDED(rc2))
3550 strSessionName = bstrSessionName;
3551 }
3552
3553 if ( SUCCEEDED(rc)
3554 && fLaunchingVMProcess
3555 )
3556 {
3557 /* complete the remote session initialization */
3558
3559 /* get the console from the direct session */
3560 ComPtr<IConsole> console;
3561 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3562 ComAssertComRC(rc);
3563
3564 if (SUCCEEDED(rc) && !console)
3565 {
3566 ComAssert(!!console);
3567 rc = E_FAIL;
3568 }
3569
3570 /* assign machine & console to the remote session */
3571 if (SUCCEEDED(rc))
3572 {
3573 /*
3574 * after LaunchVMProcess(), the first and the only
3575 * entry in remoteControls is that remote session
3576 */
3577 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3578 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3579 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3580
3581 /* The failure may occur w/o any error info (from RPC), so provide one */
3582 if (FAILED(rc))
3583 setError(VBOX_E_VM_ERROR,
3584 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3585 }
3586
3587 if (FAILED(rc))
3588 pSessionControl->Uninitialize();
3589 }
3590
3591 /* acquire the lock again */
3592 alock.acquire();
3593
3594 /* Restore the session state */
3595 mData->mSession.mState = origState;
3596 }
3597
3598 // finalize spawning anyway (this is why we don't return on errors above)
3599 if (fLaunchingVMProcess)
3600 {
3601 Assert(mData->mSession.mName == strSessionName);
3602 /* Note that the progress object is finalized later */
3603 /** @todo Consider checking mData->mSession.mProgress for cancellation
3604 * around here. */
3605
3606 /* We don't reset mSession.mPID here because it is necessary for
3607 * SessionMachine::uninit() to reap the child process later. */
3608
3609 if (FAILED(rc))
3610 {
3611 /* Close the remote session, remove the remote control from the list
3612 * and reset session state to Closed (@note keep the code in sync
3613 * with the relevant part in checkForSpawnFailure()). */
3614
3615 Assert(mData->mSession.mRemoteControls.size() == 1);
3616 if (mData->mSession.mRemoteControls.size() == 1)
3617 {
3618 ErrorInfoKeeper eik;
3619 mData->mSession.mRemoteControls.front()->Uninitialize();
3620 }
3621
3622 mData->mSession.mRemoteControls.clear();
3623 mData->mSession.mState = SessionState_Unlocked;
3624 }
3625 }
3626 else
3627 {
3628 /* memorize PID of the directly opened session */
3629 if (SUCCEEDED(rc))
3630 mData->mSession.mPID = pid;
3631 }
3632
3633 if (SUCCEEDED(rc))
3634 {
3635 mData->mSession.mLockType = aLockType;
3636 /* memorize the direct session control and cache IUnknown for it */
3637 mData->mSession.mDirectControl = pSessionControl;
3638 mData->mSession.mState = SessionState_Locked;
3639 if (!fLaunchingVMProcess)
3640 mData->mSession.mName = strSessionName;
3641 /* associate the SessionMachine with this Machine */
3642 mData->mSession.mMachine = sessionMachine;
3643
3644 /* request an IUnknown pointer early from the remote party for later
3645 * identity checks (it will be internally cached within mDirectControl
3646 * at least on XPCOM) */
3647 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3648 NOREF(unk);
3649 }
3650
3651 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3652 * would break the lock order */
3653 alock.release();
3654
3655 /* uninitialize the created session machine on failure */
3656 if (FAILED(rc))
3657 sessionMachine->uninit();
3658 }
3659
3660 if (SUCCEEDED(rc))
3661 {
3662 /*
3663 * tell the client watcher thread to update the set of
3664 * machines that have open sessions
3665 */
3666 mParent->i_updateClientWatcher();
3667
3668 if (oldState != SessionState_Locked)
3669 /* fire an event */
3670 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3671 }
3672
3673 return rc;
3674}
3675
3676/**
3677 * @note Locks objects!
3678 */
3679HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3680 const com::Utf8Str &aName,
3681 const com::Utf8Str &aEnvironment,
3682 ComPtr<IProgress> &aProgress)
3683{
3684 Utf8Str strFrontend(aName);
3685 /* "emergencystop" doesn't need the session, so skip the checks/interface
3686 * retrieval. This code doesn't quite fit in here, but introducing a
3687 * special API method would be even more effort, and would require explicit
3688 * support by every API client. It's better to hide the feature a bit. */
3689 if (strFrontend != "emergencystop")
3690 CheckComArgNotNull(aSession);
3691
3692 HRESULT rc = S_OK;
3693 if (strFrontend.isEmpty())
3694 {
3695 Bstr bstrFrontend;
3696 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3697 if (FAILED(rc))
3698 return rc;
3699 strFrontend = bstrFrontend;
3700 if (strFrontend.isEmpty())
3701 {
3702 ComPtr<ISystemProperties> systemProperties;
3703 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3704 if (FAILED(rc))
3705 return rc;
3706 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3707 if (FAILED(rc))
3708 return rc;
3709 strFrontend = bstrFrontend;
3710 }
3711 /* paranoia - emergencystop is not a valid default */
3712 if (strFrontend == "emergencystop")
3713 strFrontend = Utf8Str::Empty;
3714 }
3715 /* default frontend: Qt GUI */
3716 if (strFrontend.isEmpty())
3717 strFrontend = "GUI/Qt";
3718
3719 if (strFrontend != "emergencystop")
3720 {
3721 /* check the session state */
3722 SessionState_T state;
3723 rc = aSession->COMGETTER(State)(&state);
3724 if (FAILED(rc))
3725 return rc;
3726
3727 if (state != SessionState_Unlocked)
3728 return setError(VBOX_E_INVALID_OBJECT_STATE,
3729 tr("The given session is busy"));
3730
3731 /* get the IInternalSessionControl interface */
3732 ComPtr<IInternalSessionControl> control(aSession);
3733 ComAssertMsgRet(!control.isNull(),
3734 ("No IInternalSessionControl interface"),
3735 E_INVALIDARG);
3736
3737 /* get the teleporter enable state for the progress object init. */
3738 BOOL fTeleporterEnabled;
3739 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3740 if (FAILED(rc))
3741 return rc;
3742
3743 /* create a progress object */
3744 ComObjPtr<ProgressProxy> progress;
3745 progress.createObject();
3746 rc = progress->init(mParent,
3747 static_cast<IMachine*>(this),
3748 Bstr(tr("Starting VM")).raw(),
3749 TRUE /* aCancelable */,
3750 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3751 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3752 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3753 2 /* uFirstOperationWeight */,
3754 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3755
3756 if (SUCCEEDED(rc))
3757 {
3758 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3759 if (SUCCEEDED(rc))
3760 {
3761 aProgress = progress;
3762
3763 /* signal the client watcher thread */
3764 mParent->i_updateClientWatcher();
3765
3766 /* fire an event */
3767 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3768 }
3769 }
3770 }
3771 else
3772 {
3773 /* no progress object - either instant success or failure */
3774 aProgress = NULL;
3775
3776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3777
3778 if (mData->mSession.mState != SessionState_Locked)
3779 return setError(VBOX_E_INVALID_OBJECT_STATE,
3780 tr("The machine '%s' is not locked by a session"),
3781 mUserData->s.strName.c_str());
3782
3783 /* must have a VM process associated - do not kill normal API clients
3784 * with an open session */
3785 if (!Global::IsOnline(mData->mMachineState))
3786 return setError(VBOX_E_INVALID_OBJECT_STATE,
3787 tr("The machine '%s' does not have a VM process"),
3788 mUserData->s.strName.c_str());
3789
3790 /* forcibly terminate the VM process */
3791 if (mData->mSession.mPID != NIL_RTPROCESS)
3792 RTProcTerminate(mData->mSession.mPID);
3793
3794 /* signal the client watcher thread, as most likely the client has
3795 * been terminated */
3796 mParent->i_updateClientWatcher();
3797 }
3798
3799 return rc;
3800}
3801
3802HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3803{
3804 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3805 return setError(E_INVALIDARG,
3806 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3807 aPosition, SchemaDefs::MaxBootPosition);
3808
3809 if (aDevice == DeviceType_USB)
3810 return setError(E_NOTIMPL,
3811 tr("Booting from USB device is currently not supported"));
3812
3813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3814
3815 HRESULT rc = i_checkStateDependency(MutableStateDep);
3816 if (FAILED(rc)) return rc;
3817
3818 i_setModified(IsModified_MachineData);
3819 mHWData.backup();
3820 mHWData->mBootOrder[aPosition - 1] = aDevice;
3821
3822 return S_OK;
3823}
3824
3825HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3826{
3827 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3828 return setError(E_INVALIDARG,
3829 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3830 aPosition, SchemaDefs::MaxBootPosition);
3831
3832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3833
3834 *aDevice = mHWData->mBootOrder[aPosition - 1];
3835
3836 return S_OK;
3837}
3838
3839HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3840 LONG aControllerPort,
3841 LONG aDevice,
3842 DeviceType_T aType,
3843 const ComPtr<IMedium> &aMedium)
3844{
3845 IMedium *aM = aMedium;
3846 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3847 aName.c_str(), aControllerPort, aDevice, aType, aM));
3848
3849 // request the host lock first, since might be calling Host methods for getting host drives;
3850 // next, protect the media tree all the while we're in here, as well as our member variables
3851 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3852 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3853
3854 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3855 if (FAILED(rc)) return rc;
3856
3857 /// @todo NEWMEDIA implicit machine registration
3858 if (!mData->mRegistered)
3859 return setError(VBOX_E_INVALID_OBJECT_STATE,
3860 tr("Cannot attach storage devices to an unregistered machine"));
3861
3862 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3863
3864 /* Check for an existing controller. */
3865 ComObjPtr<StorageController> ctl;
3866 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3867 if (FAILED(rc)) return rc;
3868
3869 StorageControllerType_T ctrlType;
3870 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3871 if (FAILED(rc))
3872 return setError(E_FAIL,
3873 tr("Could not get type of controller '%s'"),
3874 aName.c_str());
3875
3876 bool fSilent = false;
3877 Utf8Str strReconfig;
3878
3879 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3880 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3881 if ( mData->mMachineState == MachineState_Paused
3882 && strReconfig == "1")
3883 fSilent = true;
3884
3885 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3886 bool fHotplug = false;
3887 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3888 fHotplug = true;
3889
3890 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3891 return setError(VBOX_E_INVALID_VM_STATE,
3892 tr("Controller '%s' does not support hotplugging"),
3893 aName.c_str());
3894
3895 // check that the port and device are not out of range
3896 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3897 if (FAILED(rc)) return rc;
3898
3899 /* check if the device slot is already busy */
3900 MediumAttachment *pAttachTemp;
3901 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3902 aName,
3903 aControllerPort,
3904 aDevice)))
3905 {
3906 Medium *pMedium = pAttachTemp->i_getMedium();
3907 if (pMedium)
3908 {
3909 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3910 return setError(VBOX_E_OBJECT_IN_USE,
3911 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3912 pMedium->i_getLocationFull().c_str(),
3913 aControllerPort,
3914 aDevice,
3915 aName.c_str());
3916 }
3917 else
3918 return setError(VBOX_E_OBJECT_IN_USE,
3919 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3920 aControllerPort, aDevice, aName.c_str());
3921 }
3922
3923 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3924 if (aMedium && medium.isNull())
3925 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3926
3927 AutoCaller mediumCaller(medium);
3928 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3929
3930 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3931
3932 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3933 && !medium.isNull()
3934 )
3935 return setError(VBOX_E_OBJECT_IN_USE,
3936 tr("Medium '%s' is already attached to this virtual machine"),
3937 medium->i_getLocationFull().c_str());
3938
3939 if (!medium.isNull())
3940 {
3941 MediumType_T mtype = medium->i_getType();
3942 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3943 // For DVDs it's not written to the config file, so needs no global config
3944 // version bump. For floppies it's a new attribute "type", which is ignored
3945 // by older VirtualBox version, so needs no global config version bump either.
3946 // For hard disks this type is not accepted.
3947 if (mtype == MediumType_MultiAttach)
3948 {
3949 // This type is new with VirtualBox 4.0 and therefore requires settings
3950 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3951 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3952 // two reasons: The medium type is a property of the media registry tree, which
3953 // can reside in the global config file (for pre-4.0 media); we would therefore
3954 // possibly need to bump the global config version. We don't want to do that though
3955 // because that might make downgrading to pre-4.0 impossible.
3956 // As a result, we can only use these two new types if the medium is NOT in the
3957 // global registry:
3958 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3959 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3960 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3961 )
3962 return setError(VBOX_E_INVALID_OBJECT_STATE,
3963 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3964 "to machines that were created with VirtualBox 4.0 or later"),
3965 medium->i_getLocationFull().c_str());
3966 }
3967 }
3968
3969 bool fIndirect = false;
3970 if (!medium.isNull())
3971 fIndirect = medium->i_isReadOnly();
3972 bool associate = true;
3973
3974 do
3975 {
3976 if ( aType == DeviceType_HardDisk
3977 && mMediumAttachments.isBackedUp())
3978 {
3979 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3980
3981 /* check if the medium was attached to the VM before we started
3982 * changing attachments in which case the attachment just needs to
3983 * be restored */
3984 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3985 {
3986 AssertReturn(!fIndirect, E_FAIL);
3987
3988 /* see if it's the same bus/channel/device */
3989 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3990 {
3991 /* the simplest case: restore the whole attachment
3992 * and return, nothing else to do */
3993 mMediumAttachments->push_back(pAttachTemp);
3994
3995 /* Reattach the medium to the VM. */
3996 if (fHotplug || fSilent)
3997 {
3998 mediumLock.release();
3999 treeLock.release();
4000 alock.release();
4001
4002 MediumLockList *pMediumLockList(new MediumLockList());
4003
4004 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4005 medium /* pToLockWrite */,
4006 false /* fMediumLockWriteAll */,
4007 NULL,
4008 *pMediumLockList);
4009 alock.acquire();
4010 if (FAILED(rc))
4011 delete pMediumLockList;
4012 else
4013 {
4014 mData->mSession.mLockedMedia.Unlock();
4015 alock.release();
4016 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4017 mData->mSession.mLockedMedia.Lock();
4018 alock.acquire();
4019 }
4020 alock.release();
4021
4022 if (SUCCEEDED(rc))
4023 {
4024 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4025 /* Remove lock list in case of error. */
4026 if (FAILED(rc))
4027 {
4028 mData->mSession.mLockedMedia.Unlock();
4029 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4030 mData->mSession.mLockedMedia.Lock();
4031 }
4032 }
4033 }
4034
4035 return S_OK;
4036 }
4037
4038 /* bus/channel/device differ; we need a new attachment object,
4039 * but don't try to associate it again */
4040 associate = false;
4041 break;
4042 }
4043 }
4044
4045 /* go further only if the attachment is to be indirect */
4046 if (!fIndirect)
4047 break;
4048
4049 /* perform the so called smart attachment logic for indirect
4050 * attachments. Note that smart attachment is only applicable to base
4051 * hard disks. */
4052
4053 if (medium->i_getParent().isNull())
4054 {
4055 /* first, investigate the backup copy of the current hard disk
4056 * attachments to make it possible to re-attach existing diffs to
4057 * another device slot w/o losing their contents */
4058 if (mMediumAttachments.isBackedUp())
4059 {
4060 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4061
4062 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4063 uint32_t foundLevel = 0;
4064
4065 for (MediumAttachmentList::const_iterator
4066 it = oldAtts.begin();
4067 it != oldAtts.end();
4068 ++it)
4069 {
4070 uint32_t level = 0;
4071 MediumAttachment *pAttach = *it;
4072 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4073 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4074 if (pMedium.isNull())
4075 continue;
4076
4077 if (pMedium->i_getBase(&level) == medium)
4078 {
4079 /* skip the hard disk if its currently attached (we
4080 * cannot attach the same hard disk twice) */
4081 if (i_findAttachment(*mMediumAttachments.data(),
4082 pMedium))
4083 continue;
4084
4085 /* matched device, channel and bus (i.e. attached to the
4086 * same place) will win and immediately stop the search;
4087 * otherwise the attachment that has the youngest
4088 * descendant of medium will be used
4089 */
4090 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4091 {
4092 /* the simplest case: restore the whole attachment
4093 * and return, nothing else to do */
4094 mMediumAttachments->push_back(*it);
4095
4096 /* Reattach the medium to the VM. */
4097 if (fHotplug || fSilent)
4098 {
4099 mediumLock.release();
4100 treeLock.release();
4101 alock.release();
4102
4103 MediumLockList *pMediumLockList(new MediumLockList());
4104
4105 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4106 medium /* pToLockWrite */,
4107 false /* fMediumLockWriteAll */,
4108 NULL,
4109 *pMediumLockList);
4110 alock.acquire();
4111 if (FAILED(rc))
4112 delete pMediumLockList;
4113 else
4114 {
4115 mData->mSession.mLockedMedia.Unlock();
4116 alock.release();
4117 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4118 mData->mSession.mLockedMedia.Lock();
4119 alock.acquire();
4120 }
4121 alock.release();
4122
4123 if (SUCCEEDED(rc))
4124 {
4125 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4126 /* Remove lock list in case of error. */
4127 if (FAILED(rc))
4128 {
4129 mData->mSession.mLockedMedia.Unlock();
4130 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4131 mData->mSession.mLockedMedia.Lock();
4132 }
4133 }
4134 }
4135
4136 return S_OK;
4137 }
4138 else if ( foundIt == oldAtts.end()
4139 || level > foundLevel /* prefer younger */
4140 )
4141 {
4142 foundIt = it;
4143 foundLevel = level;
4144 }
4145 }
4146 }
4147
4148 if (foundIt != oldAtts.end())
4149 {
4150 /* use the previously attached hard disk */
4151 medium = (*foundIt)->i_getMedium();
4152 mediumCaller.attach(medium);
4153 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4154 mediumLock.attach(medium);
4155 /* not implicit, doesn't require association with this VM */
4156 fIndirect = false;
4157 associate = false;
4158 /* go right to the MediumAttachment creation */
4159 break;
4160 }
4161 }
4162
4163 /* must give up the medium lock and medium tree lock as below we
4164 * go over snapshots, which needs a lock with higher lock order. */
4165 mediumLock.release();
4166 treeLock.release();
4167
4168 /* then, search through snapshots for the best diff in the given
4169 * hard disk's chain to base the new diff on */
4170
4171 ComObjPtr<Medium> base;
4172 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4173 while (snap)
4174 {
4175 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4176
4177 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4178
4179 MediumAttachment *pAttachFound = NULL;
4180 uint32_t foundLevel = 0;
4181
4182 for (MediumAttachmentList::const_iterator
4183 it = snapAtts.begin();
4184 it != snapAtts.end();
4185 ++it)
4186 {
4187 MediumAttachment *pAttach = *it;
4188 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4189 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4190 if (pMedium.isNull())
4191 continue;
4192
4193 uint32_t level = 0;
4194 if (pMedium->i_getBase(&level) == medium)
4195 {
4196 /* matched device, channel and bus (i.e. attached to the
4197 * same place) will win and immediately stop the search;
4198 * otherwise the attachment that has the youngest
4199 * descendant of medium will be used
4200 */
4201 if ( pAttach->i_getDevice() == aDevice
4202 && pAttach->i_getPort() == aControllerPort
4203 && pAttach->i_getControllerName() == aName
4204 )
4205 {
4206 pAttachFound = pAttach;
4207 break;
4208 }
4209 else if ( !pAttachFound
4210 || level > foundLevel /* prefer younger */
4211 )
4212 {
4213 pAttachFound = pAttach;
4214 foundLevel = level;
4215 }
4216 }
4217 }
4218
4219 if (pAttachFound)
4220 {
4221 base = pAttachFound->i_getMedium();
4222 break;
4223 }
4224
4225 snap = snap->i_getParent();
4226 }
4227
4228 /* re-lock medium tree and the medium, as we need it below */
4229 treeLock.acquire();
4230 mediumLock.acquire();
4231
4232 /* found a suitable diff, use it as a base */
4233 if (!base.isNull())
4234 {
4235 medium = base;
4236 mediumCaller.attach(medium);
4237 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4238 mediumLock.attach(medium);
4239 }
4240 }
4241
4242 Utf8Str strFullSnapshotFolder;
4243 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4244
4245 ComObjPtr<Medium> diff;
4246 diff.createObject();
4247 // store this diff in the same registry as the parent
4248 Guid uuidRegistryParent;
4249 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4250 {
4251 // parent image has no registry: this can happen if we're attaching a new immutable
4252 // image that has not yet been attached (medium then points to the base and we're
4253 // creating the diff image for the immutable, and the parent is not yet registered);
4254 // put the parent in the machine registry then
4255 mediumLock.release();
4256 treeLock.release();
4257 alock.release();
4258 i_addMediumToRegistry(medium);
4259 alock.acquire();
4260 treeLock.acquire();
4261 mediumLock.acquire();
4262 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4263 }
4264 rc = diff->init(mParent,
4265 medium->i_getPreferredDiffFormat(),
4266 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4267 uuidRegistryParent,
4268 DeviceType_HardDisk);
4269 if (FAILED(rc)) return rc;
4270
4271 /* Apply the normal locking logic to the entire chain. */
4272 MediumLockList *pMediumLockList(new MediumLockList());
4273 mediumLock.release();
4274 treeLock.release();
4275 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4276 diff /* pToLockWrite */,
4277 false /* fMediumLockWriteAll */,
4278 medium,
4279 *pMediumLockList);
4280 treeLock.acquire();
4281 mediumLock.acquire();
4282 if (SUCCEEDED(rc))
4283 {
4284 mediumLock.release();
4285 treeLock.release();
4286 rc = pMediumLockList->Lock();
4287 treeLock.acquire();
4288 mediumLock.acquire();
4289 if (FAILED(rc))
4290 setError(rc,
4291 tr("Could not lock medium when creating diff '%s'"),
4292 diff->i_getLocationFull().c_str());
4293 else
4294 {
4295 /* will release the lock before the potentially lengthy
4296 * operation, so protect with the special state */
4297 MachineState_T oldState = mData->mMachineState;
4298 i_setMachineState(MachineState_SettingUp);
4299
4300 mediumLock.release();
4301 treeLock.release();
4302 alock.release();
4303
4304 rc = medium->i_createDiffStorage(diff,
4305 medium->i_getPreferredDiffVariant(),
4306 pMediumLockList,
4307 NULL /* aProgress */,
4308 true /* aWait */);
4309
4310 alock.acquire();
4311 treeLock.acquire();
4312 mediumLock.acquire();
4313
4314 i_setMachineState(oldState);
4315 }
4316 }
4317
4318 /* Unlock the media and free the associated memory. */
4319 delete pMediumLockList;
4320
4321 if (FAILED(rc)) return rc;
4322
4323 /* use the created diff for the actual attachment */
4324 medium = diff;
4325 mediumCaller.attach(medium);
4326 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4327 mediumLock.attach(medium);
4328 }
4329 while (0);
4330
4331 ComObjPtr<MediumAttachment> attachment;
4332 attachment.createObject();
4333 rc = attachment->init(this,
4334 medium,
4335 aName,
4336 aControllerPort,
4337 aDevice,
4338 aType,
4339 fIndirect,
4340 false /* fPassthrough */,
4341 false /* fTempEject */,
4342 false /* fNonRotational */,
4343 false /* fDiscard */,
4344 fHotplug /* fHotPluggable */,
4345 Utf8Str::Empty);
4346 if (FAILED(rc)) return rc;
4347
4348 if (associate && !medium.isNull())
4349 {
4350 // as the last step, associate the medium to the VM
4351 rc = medium->i_addBackReference(mData->mUuid);
4352 // here we can fail because of Deleting, or being in process of creating a Diff
4353 if (FAILED(rc)) return rc;
4354
4355 mediumLock.release();
4356 treeLock.release();
4357 alock.release();
4358 i_addMediumToRegistry(medium);
4359 alock.acquire();
4360 treeLock.acquire();
4361 mediumLock.acquire();
4362 }
4363
4364 /* success: finally remember the attachment */
4365 i_setModified(IsModified_Storage);
4366 mMediumAttachments.backup();
4367 mMediumAttachments->push_back(attachment);
4368
4369 mediumLock.release();
4370 treeLock.release();
4371 alock.release();
4372
4373 if (fHotplug || fSilent)
4374 {
4375 if (!medium.isNull())
4376 {
4377 MediumLockList *pMediumLockList(new MediumLockList());
4378
4379 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4380 medium /* pToLockWrite */,
4381 false /* fMediumLockWriteAll */,
4382 NULL,
4383 *pMediumLockList);
4384 alock.acquire();
4385 if (FAILED(rc))
4386 delete pMediumLockList;
4387 else
4388 {
4389 mData->mSession.mLockedMedia.Unlock();
4390 alock.release();
4391 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4392 mData->mSession.mLockedMedia.Lock();
4393 alock.acquire();
4394 }
4395 alock.release();
4396 }
4397
4398 if (SUCCEEDED(rc))
4399 {
4400 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4401 /* Remove lock list in case of error. */
4402 if (FAILED(rc))
4403 {
4404 mData->mSession.mLockedMedia.Unlock();
4405 mData->mSession.mLockedMedia.Remove(attachment);
4406 mData->mSession.mLockedMedia.Lock();
4407 }
4408 }
4409 }
4410
4411 /* Save modified registries, but skip this machine as it's the caller's
4412 * job to save its settings like all other settings changes. */
4413 mParent->i_unmarkRegistryModified(i_getId());
4414 mParent->i_saveModifiedRegistries();
4415
4416 return rc;
4417}
4418
4419HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4420 LONG aDevice)
4421{
4422 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4423 aName.c_str(), aControllerPort, aDevice));
4424
4425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4426
4427 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4428 if (FAILED(rc)) return rc;
4429
4430 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4431
4432 /* Check for an existing controller. */
4433 ComObjPtr<StorageController> ctl;
4434 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4435 if (FAILED(rc)) return rc;
4436
4437 StorageControllerType_T ctrlType;
4438 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4439 if (FAILED(rc))
4440 return setError(E_FAIL,
4441 tr("Could not get type of controller '%s'"),
4442 aName.c_str());
4443
4444 bool fSilent = false;
4445 Utf8Str strReconfig;
4446
4447 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4448 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4449 if ( mData->mMachineState == MachineState_Paused
4450 && strReconfig == "1")
4451 fSilent = true;
4452
4453 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4454 bool fHotplug = false;
4455 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4456 fHotplug = true;
4457
4458 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4459 return setError(VBOX_E_INVALID_VM_STATE,
4460 tr("Controller '%s' does not support hotplugging"),
4461 aName.c_str());
4462
4463 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4464 aName,
4465 aControllerPort,
4466 aDevice);
4467 if (!pAttach)
4468 return setError(VBOX_E_OBJECT_NOT_FOUND,
4469 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4470 aDevice, aControllerPort, aName.c_str());
4471
4472 if (fHotplug && !pAttach->i_getHotPluggable())
4473 return setError(VBOX_E_NOT_SUPPORTED,
4474 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4475 aDevice, aControllerPort, aName.c_str());
4476
4477 /*
4478 * The VM has to detach the device before we delete any implicit diffs.
4479 * If this fails we can roll back without loosing data.
4480 */
4481 if (fHotplug || fSilent)
4482 {
4483 alock.release();
4484 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4485 alock.acquire();
4486 }
4487 if (FAILED(rc)) return rc;
4488
4489 /* If we are here everything went well and we can delete the implicit now. */
4490 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4491
4492 alock.release();
4493
4494 /* Save modified registries, but skip this machine as it's the caller's
4495 * job to save its settings like all other settings changes. */
4496 mParent->i_unmarkRegistryModified(i_getId());
4497 mParent->i_saveModifiedRegistries();
4498
4499 return rc;
4500}
4501
4502HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4503 LONG aDevice, BOOL aPassthrough)
4504{
4505 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4506 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4507
4508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4509
4510 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4511 if (FAILED(rc)) return rc;
4512
4513 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4514
4515 /* Check for an existing controller. */
4516 ComObjPtr<StorageController> ctl;
4517 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4518 if (FAILED(rc)) return rc;
4519
4520 StorageControllerType_T ctrlType;
4521 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4522 if (FAILED(rc))
4523 return setError(E_FAIL,
4524 tr("Could not get type of controller '%s'"),
4525 aName.c_str());
4526
4527 bool fSilent = false;
4528 Utf8Str strReconfig;
4529
4530 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4531 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4532 if ( mData->mMachineState == MachineState_Paused
4533 && strReconfig == "1")
4534 fSilent = true;
4535
4536 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4537 bool fHotplug = false;
4538 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4539 fHotplug = true;
4540
4541 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4542 return setError(VBOX_E_INVALID_VM_STATE,
4543 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4544 aName.c_str());
4545
4546 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4547 aName,
4548 aControllerPort,
4549 aDevice);
4550 if (!pAttach)
4551 return setError(VBOX_E_OBJECT_NOT_FOUND,
4552 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4553 aDevice, aControllerPort, aName.c_str());
4554
4555
4556 i_setModified(IsModified_Storage);
4557 mMediumAttachments.backup();
4558
4559 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4560
4561 if (pAttach->i_getType() != DeviceType_DVD)
4562 return setError(E_INVALIDARG,
4563 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4564 aDevice, aControllerPort, aName.c_str());
4565 pAttach->i_updatePassthrough(!!aPassthrough);
4566
4567 attLock.release();
4568 alock.release();
4569 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4570
4571 return rc;
4572}
4573
4574HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4575 LONG aDevice, BOOL aTemporaryEject)
4576{
4577
4578 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4579 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4580
4581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4582
4583 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4584 if (FAILED(rc)) return rc;
4585
4586 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4587 aName,
4588 aControllerPort,
4589 aDevice);
4590 if (!pAttach)
4591 return setError(VBOX_E_OBJECT_NOT_FOUND,
4592 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4593 aDevice, aControllerPort, aName.c_str());
4594
4595
4596 i_setModified(IsModified_Storage);
4597 mMediumAttachments.backup();
4598
4599 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4600
4601 if (pAttach->i_getType() != DeviceType_DVD)
4602 return setError(E_INVALIDARG,
4603 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4604 aDevice, aControllerPort, aName.c_str());
4605 pAttach->i_updateTempEject(!!aTemporaryEject);
4606
4607 return S_OK;
4608}
4609
4610HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4611 LONG aDevice, BOOL aNonRotational)
4612{
4613
4614 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4615 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4616
4617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4618
4619 HRESULT rc = i_checkStateDependency(MutableStateDep);
4620 if (FAILED(rc)) return rc;
4621
4622 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4623
4624 if (Global::IsOnlineOrTransient(mData->mMachineState))
4625 return setError(VBOX_E_INVALID_VM_STATE,
4626 tr("Invalid machine state: %s"),
4627 Global::stringifyMachineState(mData->mMachineState));
4628
4629 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4630 aName,
4631 aControllerPort,
4632 aDevice);
4633 if (!pAttach)
4634 return setError(VBOX_E_OBJECT_NOT_FOUND,
4635 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4636 aDevice, aControllerPort, aName.c_str());
4637
4638
4639 i_setModified(IsModified_Storage);
4640 mMediumAttachments.backup();
4641
4642 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4643
4644 if (pAttach->i_getType() != DeviceType_HardDisk)
4645 return setError(E_INVALIDARG,
4646 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"),
4647 aDevice, aControllerPort, aName.c_str());
4648 pAttach->i_updateNonRotational(!!aNonRotational);
4649
4650 return S_OK;
4651}
4652
4653HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4654 LONG aDevice, BOOL aDiscard)
4655{
4656
4657 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4658 aName.c_str(), aControllerPort, aDevice, aDiscard));
4659
4660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4661
4662 HRESULT rc = i_checkStateDependency(MutableStateDep);
4663 if (FAILED(rc)) return rc;
4664
4665 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4666
4667 if (Global::IsOnlineOrTransient(mData->mMachineState))
4668 return setError(VBOX_E_INVALID_VM_STATE,
4669 tr("Invalid machine state: %s"),
4670 Global::stringifyMachineState(mData->mMachineState));
4671
4672 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4673 aName,
4674 aControllerPort,
4675 aDevice);
4676 if (!pAttach)
4677 return setError(VBOX_E_OBJECT_NOT_FOUND,
4678 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4679 aDevice, aControllerPort, aName.c_str());
4680
4681
4682 i_setModified(IsModified_Storage);
4683 mMediumAttachments.backup();
4684
4685 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4686
4687 if (pAttach->i_getType() != DeviceType_HardDisk)
4688 return setError(E_INVALIDARG,
4689 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"),
4690 aDevice, aControllerPort, aName.c_str());
4691 pAttach->i_updateDiscard(!!aDiscard);
4692
4693 return S_OK;
4694}
4695
4696HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4697 LONG aDevice, BOOL aHotPluggable)
4698{
4699 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4700 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4701
4702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4703
4704 HRESULT rc = i_checkStateDependency(MutableStateDep);
4705 if (FAILED(rc)) return rc;
4706
4707 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4708
4709 if (Global::IsOnlineOrTransient(mData->mMachineState))
4710 return setError(VBOX_E_INVALID_VM_STATE,
4711 tr("Invalid machine state: %s"),
4712 Global::stringifyMachineState(mData->mMachineState));
4713
4714 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4715 aName,
4716 aControllerPort,
4717 aDevice);
4718 if (!pAttach)
4719 return setError(VBOX_E_OBJECT_NOT_FOUND,
4720 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4721 aDevice, aControllerPort, aName.c_str());
4722
4723 /* Check for an existing controller. */
4724 ComObjPtr<StorageController> ctl;
4725 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4726 if (FAILED(rc)) return rc;
4727
4728 StorageControllerType_T ctrlType;
4729 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4730 if (FAILED(rc))
4731 return setError(E_FAIL,
4732 tr("Could not get type of controller '%s'"),
4733 aName.c_str());
4734
4735 if (!i_isControllerHotplugCapable(ctrlType))
4736 return setError(VBOX_E_NOT_SUPPORTED,
4737 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4738 aName.c_str());
4739
4740 i_setModified(IsModified_Storage);
4741 mMediumAttachments.backup();
4742
4743 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4744
4745 if (pAttach->i_getType() == DeviceType_Floppy)
4746 return setError(E_INVALIDARG,
4747 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"),
4748 aDevice, aControllerPort, aName.c_str());
4749 pAttach->i_updateHotPluggable(!!aHotPluggable);
4750
4751 return S_OK;
4752}
4753
4754HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4755 LONG aDevice)
4756{
4757 int rc = S_OK;
4758 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4759 aName.c_str(), aControllerPort, aDevice));
4760
4761 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4762
4763 return rc;
4764}
4765
4766HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4767 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4768{
4769 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4770 aName.c_str(), aControllerPort, aDevice));
4771
4772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4773
4774 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4775 if (FAILED(rc)) return rc;
4776
4777 if (Global::IsOnlineOrTransient(mData->mMachineState))
4778 return setError(VBOX_E_INVALID_VM_STATE,
4779 tr("Invalid machine state: %s"),
4780 Global::stringifyMachineState(mData->mMachineState));
4781
4782 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4783 aName,
4784 aControllerPort,
4785 aDevice);
4786 if (!pAttach)
4787 return setError(VBOX_E_OBJECT_NOT_FOUND,
4788 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4789 aDevice, aControllerPort, aName.c_str());
4790
4791
4792 i_setModified(IsModified_Storage);
4793 mMediumAttachments.backup();
4794
4795 IBandwidthGroup *iB = aBandwidthGroup;
4796 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4797 if (aBandwidthGroup && group.isNull())
4798 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4799
4800 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4801
4802 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4803 if (strBandwidthGroupOld.isNotEmpty())
4804 {
4805 /* Get the bandwidth group object and release it - this must not fail. */
4806 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4807 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4808 Assert(SUCCEEDED(rc));
4809
4810 pBandwidthGroupOld->i_release();
4811 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4812 }
4813
4814 if (!group.isNull())
4815 {
4816 group->i_reference();
4817 pAttach->i_updateBandwidthGroup(group->i_getName());
4818 }
4819
4820 return S_OK;
4821}
4822
4823HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4824 LONG aControllerPort,
4825 LONG aDevice,
4826 DeviceType_T aType)
4827{
4828 HRESULT rc = S_OK;
4829
4830 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4831 aName.c_str(), aControllerPort, aDevice, aType));
4832
4833 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4834
4835 return rc;
4836}
4837
4838
4839HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4840 LONG aControllerPort,
4841 LONG aDevice,
4842 BOOL aForce)
4843{
4844 int rc = S_OK;
4845 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4846 aName.c_str(), aControllerPort, aForce));
4847
4848 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4849
4850 return rc;
4851}
4852
4853HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4854 LONG aControllerPort,
4855 LONG aDevice,
4856 const ComPtr<IMedium> &aMedium,
4857 BOOL aForce)
4858{
4859 int rc = S_OK;
4860 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4861 aName.c_str(), aControllerPort, aDevice, aForce));
4862
4863 // request the host lock first, since might be calling Host methods for getting host drives;
4864 // next, protect the media tree all the while we're in here, as well as our member variables
4865 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4866 this->lockHandle(),
4867 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4868
4869 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4870 aName,
4871 aControllerPort,
4872 aDevice);
4873 if (pAttach.isNull())
4874 return setError(VBOX_E_OBJECT_NOT_FOUND,
4875 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4876 aDevice, aControllerPort, aName.c_str());
4877
4878 /* Remember previously mounted medium. The medium before taking the
4879 * backup is not necessarily the same thing. */
4880 ComObjPtr<Medium> oldmedium;
4881 oldmedium = pAttach->i_getMedium();
4882
4883 IMedium *iM = aMedium;
4884 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4885 if (aMedium && pMedium.isNull())
4886 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4887
4888 AutoCaller mediumCaller(pMedium);
4889 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4890
4891 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4892 if (pMedium)
4893 {
4894 DeviceType_T mediumType = pAttach->i_getType();
4895 switch (mediumType)
4896 {
4897 case DeviceType_DVD:
4898 case DeviceType_Floppy:
4899 break;
4900
4901 default:
4902 return setError(VBOX_E_INVALID_OBJECT_STATE,
4903 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4904 aControllerPort,
4905 aDevice,
4906 aName.c_str());
4907 }
4908 }
4909
4910 i_setModified(IsModified_Storage);
4911 mMediumAttachments.backup();
4912
4913 {
4914 // The backup operation makes the pAttach reference point to the
4915 // old settings. Re-get the correct reference.
4916 pAttach = i_findAttachment(*mMediumAttachments.data(),
4917 aName,
4918 aControllerPort,
4919 aDevice);
4920 if (!oldmedium.isNull())
4921 oldmedium->i_removeBackReference(mData->mUuid);
4922 if (!pMedium.isNull())
4923 {
4924 pMedium->i_addBackReference(mData->mUuid);
4925
4926 mediumLock.release();
4927 multiLock.release();
4928 i_addMediumToRegistry(pMedium);
4929 multiLock.acquire();
4930 mediumLock.acquire();
4931 }
4932
4933 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4934 pAttach->i_updateMedium(pMedium);
4935 }
4936
4937 i_setModified(IsModified_Storage);
4938
4939 mediumLock.release();
4940 multiLock.release();
4941 rc = i_onMediumChange(pAttach, aForce);
4942 multiLock.acquire();
4943 mediumLock.acquire();
4944
4945 /* On error roll back this change only. */
4946 if (FAILED(rc))
4947 {
4948 if (!pMedium.isNull())
4949 pMedium->i_removeBackReference(mData->mUuid);
4950 pAttach = i_findAttachment(*mMediumAttachments.data(),
4951 aName,
4952 aControllerPort,
4953 aDevice);
4954 /* If the attachment is gone in the meantime, bail out. */
4955 if (pAttach.isNull())
4956 return rc;
4957 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4958 if (!oldmedium.isNull())
4959 oldmedium->i_addBackReference(mData->mUuid);
4960 pAttach->i_updateMedium(oldmedium);
4961 }
4962
4963 mediumLock.release();
4964 multiLock.release();
4965
4966 /* Save modified registries, but skip this machine as it's the caller's
4967 * job to save its settings like all other settings changes. */
4968 mParent->i_unmarkRegistryModified(i_getId());
4969 mParent->i_saveModifiedRegistries();
4970
4971 return rc;
4972}
4973HRESULT Machine::getMedium(const com::Utf8Str &aName,
4974 LONG aControllerPort,
4975 LONG aDevice,
4976 ComPtr<IMedium> &aMedium)
4977{
4978 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4979 aName.c_str(), aControllerPort, aDevice));
4980
4981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4982
4983 aMedium = NULL;
4984
4985 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4986 aName,
4987 aControllerPort,
4988 aDevice);
4989 if (pAttach.isNull())
4990 return setError(VBOX_E_OBJECT_NOT_FOUND,
4991 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4992 aDevice, aControllerPort, aName.c_str());
4993
4994 aMedium = pAttach->i_getMedium();
4995
4996 return S_OK;
4997}
4998
4999HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5000{
5001
5002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5003
5004 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5005
5006 return S_OK;
5007}
5008
5009HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5010{
5011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5012
5013 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5014
5015 return S_OK;
5016}
5017
5018HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5019{
5020 /* Do not assert if slot is out of range, just return the advertised
5021 status. testdriver/vbox.py triggers this in logVmInfo. */
5022 if (aSlot >= mNetworkAdapters.size())
5023 return setError(E_INVALIDARG,
5024 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5025 aSlot, mNetworkAdapters.size());
5026
5027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5028
5029 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5030
5031 return S_OK;
5032}
5033
5034HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5035{
5036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5037
5038 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5039 size_t i = 0;
5040 for (settings::StringsMap::const_iterator
5041 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5042 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5043 ++it, ++i)
5044 aKeys[i] = it->first;
5045
5046 return S_OK;
5047}
5048
5049 /**
5050 * @note Locks this object for reading.
5051 */
5052HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5053 com::Utf8Str &aValue)
5054{
5055 /* start with nothing found */
5056 aValue = "";
5057
5058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5059
5060 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5061 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5062 // found:
5063 aValue = it->second; // source is a Utf8Str
5064
5065 /* return the result to caller (may be empty) */
5066 return S_OK;
5067}
5068
5069 /**
5070 * @note Locks mParent for writing + this object for writing.
5071 */
5072HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5073{
5074 Utf8Str strOldValue; // empty
5075
5076 // locking note: we only hold the read lock briefly to look up the old value,
5077 // then release it and call the onExtraCanChange callbacks. There is a small
5078 // chance of a race insofar as the callback might be called twice if two callers
5079 // change the same key at the same time, but that's a much better solution
5080 // than the deadlock we had here before. The actual changing of the extradata
5081 // is then performed under the write lock and race-free.
5082
5083 // look up the old value first; if nothing has changed then we need not do anything
5084 {
5085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5086
5087 // For snapshots don't even think about allowing changes, extradata
5088 // is global for a machine, so there is nothing snapshot specific.
5089 if (i_isSnapshotMachine())
5090 return setError(VBOX_E_INVALID_VM_STATE,
5091 tr("Cannot set extradata for a snapshot"));
5092
5093 // check if the right IMachine instance is used
5094 if (mData->mRegistered && !i_isSessionMachine())
5095 return setError(VBOX_E_INVALID_VM_STATE,
5096 tr("Cannot set extradata for an immutable machine"));
5097
5098 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5099 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5100 strOldValue = it->second;
5101 }
5102
5103 bool fChanged;
5104 if ((fChanged = (strOldValue != aValue)))
5105 {
5106 // ask for permission from all listeners outside the locks;
5107 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5108 // lock to copy the list of callbacks to invoke
5109 Bstr error;
5110 Bstr bstrValue(aValue);
5111
5112 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5113 {
5114 const char *sep = error.isEmpty() ? "" : ": ";
5115 CBSTR err = error.raw();
5116 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5117 return setError(E_ACCESSDENIED,
5118 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5119 aKey.c_str(),
5120 aValue.c_str(),
5121 sep,
5122 err);
5123 }
5124
5125 // data is changing and change not vetoed: then write it out under the lock
5126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5127
5128 if (aValue.isEmpty())
5129 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5130 else
5131 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5132 // creates a new key if needed
5133
5134 bool fNeedsGlobalSaveSettings = false;
5135 // This saving of settings is tricky: there is no "old state" for the
5136 // extradata items at all (unlike all other settings), so the old/new
5137 // settings comparison would give a wrong result!
5138 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5139
5140 if (fNeedsGlobalSaveSettings)
5141 {
5142 // save the global settings; for that we should hold only the VirtualBox lock
5143 alock.release();
5144 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5145 mParent->i_saveSettings();
5146 }
5147 }
5148
5149 // fire notification outside the lock
5150 if (fChanged)
5151 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5152
5153 return S_OK;
5154}
5155
5156HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5157{
5158 aProgress = NULL;
5159 NOREF(aSettingsFilePath);
5160 ReturnComNotImplemented();
5161}
5162
5163HRESULT Machine::saveSettings()
5164{
5165 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5166
5167 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5168 if (FAILED(rc)) return rc;
5169
5170 /* the settings file path may never be null */
5171 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5172
5173 /* save all VM data excluding snapshots */
5174 bool fNeedsGlobalSaveSettings = false;
5175 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5176 mlock.release();
5177
5178 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5179 {
5180 // save the global settings; for that we should hold only the VirtualBox lock
5181 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5182 rc = mParent->i_saveSettings();
5183 }
5184
5185 return rc;
5186}
5187
5188
5189HRESULT Machine::discardSettings()
5190{
5191 /*
5192 * We need to take the machine list lock here as well as the machine one
5193 * or we'll get into trouble should any media stuff require rolling back.
5194 *
5195 * Details:
5196 *
5197 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5198 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5199 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5200 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5201 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5202 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5203 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5204 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5205 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5206 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5207 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5208 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5209 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5210 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5211 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5212 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5213 * 0:005> k
5214 * # Child-SP RetAddr Call Site
5215 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5216 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5217 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5218 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5219 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5220 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5221 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5222 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5223 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5224 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5225 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5226 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5227 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5228 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5229 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5230 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5231 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5232 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5233 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5234 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5235 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5236 *
5237 */
5238 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5240
5241 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5242 if (FAILED(rc)) return rc;
5243
5244 /*
5245 * during this rollback, the session will be notified if data has
5246 * been actually changed
5247 */
5248 i_rollback(true /* aNotify */);
5249
5250 return S_OK;
5251}
5252
5253/** @note Locks objects! */
5254HRESULT Machine::unregister(AutoCaller &autoCaller,
5255 CleanupMode_T aCleanupMode,
5256 std::vector<ComPtr<IMedium> > &aMedia)
5257{
5258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5259
5260 Guid id(i_getId());
5261
5262 if (mData->mSession.mState != SessionState_Unlocked)
5263 return setError(VBOX_E_INVALID_OBJECT_STATE,
5264 tr("Cannot unregister the machine '%s' while it is locked"),
5265 mUserData->s.strName.c_str());
5266
5267 // wait for state dependents to drop to zero
5268 i_ensureNoStateDependencies();
5269
5270 if (!mData->mAccessible)
5271 {
5272 // inaccessible maschines can only be unregistered; uninitialize ourselves
5273 // here because currently there may be no unregistered that are inaccessible
5274 // (this state combination is not supported). Note releasing the caller and
5275 // leaving the lock before calling uninit()
5276 alock.release();
5277 autoCaller.release();
5278
5279 uninit();
5280
5281 mParent->i_unregisterMachine(this, id);
5282 // calls VirtualBox::i_saveSettings()
5283
5284 return S_OK;
5285 }
5286
5287 HRESULT rc = S_OK;
5288
5289 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5290 // discard saved state
5291 if (mData->mMachineState == MachineState_Saved)
5292 {
5293 // add the saved state file to the list of files the caller should delete
5294 Assert(!mSSData->strStateFilePath.isEmpty());
5295 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5296
5297 mSSData->strStateFilePath.setNull();
5298
5299 // unconditionally set the machine state to powered off, we now
5300 // know no session has locked the machine
5301 mData->mMachineState = MachineState_PoweredOff;
5302 }
5303
5304 size_t cSnapshots = 0;
5305 if (mData->mFirstSnapshot)
5306 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5307 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5308 // fail now before we start detaching media
5309 return setError(VBOX_E_INVALID_OBJECT_STATE,
5310 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5311 mUserData->s.strName.c_str(), cSnapshots);
5312
5313 // This list collects the medium objects from all medium attachments
5314 // which we will detach from the machine and its snapshots, in a specific
5315 // order which allows for closing all media without getting "media in use"
5316 // errors, simply by going through the list from the front to the back:
5317 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5318 // and must be closed before the parent media from the snapshots, or closing the parents
5319 // will fail because they still have children);
5320 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5321 // the root ("first") snapshot of the machine.
5322 MediaList llMedia;
5323
5324 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5325 && mMediumAttachments->size()
5326 )
5327 {
5328 // we have media attachments: detach them all and add the Medium objects to our list
5329 if (aCleanupMode != CleanupMode_UnregisterOnly)
5330 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5331 else
5332 return setError(VBOX_E_INVALID_OBJECT_STATE,
5333 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5334 mUserData->s.strName.c_str(), mMediumAttachments->size());
5335 }
5336
5337 if (cSnapshots)
5338 {
5339 // add the media from the medium attachments of the snapshots to llMedia
5340 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5341 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5342 // into the children first
5343
5344 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5345 MachineState_T oldState = mData->mMachineState;
5346 mData->mMachineState = MachineState_DeletingSnapshot;
5347
5348 // make a copy of the first snapshot so the refcount does not drop to 0
5349 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5350 // because of the AutoCaller voodoo)
5351 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5352
5353 // GO!
5354 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5355
5356 mData->mMachineState = oldState;
5357 }
5358
5359 if (FAILED(rc))
5360 {
5361 i_rollbackMedia();
5362 return rc;
5363 }
5364
5365 // commit all the media changes made above
5366 i_commitMedia();
5367
5368 mData->mRegistered = false;
5369
5370 // machine lock no longer needed
5371 alock.release();
5372
5373 // return media to caller
5374 aMedia.resize(llMedia.size());
5375 size_t i = 0;
5376 for (MediaList::const_iterator
5377 it = llMedia.begin();
5378 it != llMedia.end();
5379 ++it, ++i)
5380 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5381
5382 mParent->i_unregisterMachine(this, id);
5383 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5384
5385 return S_OK;
5386}
5387
5388/**
5389 * Task record for deleting a machine config.
5390 */
5391class Machine::DeleteConfigTask
5392 : public Machine::Task
5393{
5394public:
5395 DeleteConfigTask(Machine *m,
5396 Progress *p,
5397 const Utf8Str &t,
5398 const RTCList<ComPtr<IMedium> > &llMediums,
5399 const StringsList &llFilesToDelete)
5400 : Task(m, p, t),
5401 m_llMediums(llMediums),
5402 m_llFilesToDelete(llFilesToDelete)
5403 {}
5404
5405private:
5406 void handler()
5407 {
5408 try
5409 {
5410 m_pMachine->i_deleteConfigHandler(*this);
5411 }
5412 catch (...)
5413 {
5414 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5415 }
5416 }
5417
5418 RTCList<ComPtr<IMedium> > m_llMediums;
5419 StringsList m_llFilesToDelete;
5420
5421 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5422};
5423
5424/**
5425 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5426 * SessionMachine::taskHandler().
5427 *
5428 * @note Locks this object for writing.
5429 *
5430 * @param task
5431 * @return
5432 */
5433void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5434{
5435 LogFlowThisFuncEnter();
5436
5437 AutoCaller autoCaller(this);
5438 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5439 if (FAILED(autoCaller.rc()))
5440 {
5441 /* we might have been uninitialized because the session was accidentally
5442 * closed by the client, so don't assert */
5443 HRESULT rc = setError(E_FAIL,
5444 tr("The session has been accidentally closed"));
5445 task.m_pProgress->i_notifyComplete(rc);
5446 LogFlowThisFuncLeave();
5447 return;
5448 }
5449
5450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5451
5452 HRESULT rc = S_OK;
5453
5454 try
5455 {
5456 ULONG uLogHistoryCount = 3;
5457 ComPtr<ISystemProperties> systemProperties;
5458 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5459 if (FAILED(rc)) throw rc;
5460
5461 if (!systemProperties.isNull())
5462 {
5463 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5464 if (FAILED(rc)) throw rc;
5465 }
5466
5467 MachineState_T oldState = mData->mMachineState;
5468 i_setMachineState(MachineState_SettingUp);
5469 alock.release();
5470 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5471 {
5472 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5473 {
5474 AutoCaller mac(pMedium);
5475 if (FAILED(mac.rc())) throw mac.rc();
5476 Utf8Str strLocation = pMedium->i_getLocationFull();
5477 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5478 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5479 if (FAILED(rc)) throw rc;
5480 }
5481 if (pMedium->i_isMediumFormatFile())
5482 {
5483 ComPtr<IProgress> pProgress2;
5484 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5485 if (FAILED(rc)) throw rc;
5486 rc = task.m_pProgress->i_waitForOtherProgressCompletion(pProgress2);
5487 if (FAILED(rc)) throw rc;
5488 }
5489
5490 /* Close the medium, deliberately without checking the return
5491 * code, and without leaving any trace in the error info, as
5492 * a failure here is a very minor issue, which shouldn't happen
5493 * as above we even managed to delete the medium. */
5494 {
5495 ErrorInfoKeeper eik;
5496 pMedium->Close();
5497 }
5498 }
5499 i_setMachineState(oldState);
5500 alock.acquire();
5501
5502 // delete the files pushed on the task list by Machine::Delete()
5503 // (this includes saved states of the machine and snapshots and
5504 // medium storage files from the IMedium list passed in, and the
5505 // machine XML file)
5506 for (StringsList::const_iterator
5507 it = task.m_llFilesToDelete.begin();
5508 it != task.m_llFilesToDelete.end();
5509 ++it)
5510 {
5511 const Utf8Str &strFile = *it;
5512 LogFunc(("Deleting file %s\n", strFile.c_str()));
5513 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5514 if (FAILED(rc)) throw rc;
5515
5516 int vrc = RTFileDelete(strFile.c_str());
5517 if (RT_FAILURE(vrc))
5518 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5519 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5520 }
5521
5522 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5523 if (FAILED(rc)) throw rc;
5524
5525 /* delete the settings only when the file actually exists */
5526 if (mData->pMachineConfigFile->fileExists())
5527 {
5528 /* Delete any backup or uncommitted XML files. Ignore failures.
5529 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5530 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5531 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5532 RTFileDelete(otherXml.c_str());
5533 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5534 RTFileDelete(otherXml.c_str());
5535
5536 /* delete the Logs folder, nothing important should be left
5537 * there (we don't check for errors because the user might have
5538 * some private files there that we don't want to delete) */
5539 Utf8Str logFolder;
5540 getLogFolder(logFolder);
5541 Assert(logFolder.length());
5542 if (RTDirExists(logFolder.c_str()))
5543 {
5544 /* Delete all VBox.log[.N] files from the Logs folder
5545 * (this must be in sync with the rotation logic in
5546 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5547 * files that may have been created by the GUI. */
5548 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5549 logFolder.c_str(), RTPATH_DELIMITER);
5550 RTFileDelete(log.c_str());
5551 log = Utf8StrFmt("%s%cVBox.png",
5552 logFolder.c_str(), RTPATH_DELIMITER);
5553 RTFileDelete(log.c_str());
5554 for (int i = uLogHistoryCount; i > 0; i--)
5555 {
5556 log = Utf8StrFmt("%s%cVBox.log.%d",
5557 logFolder.c_str(), RTPATH_DELIMITER, i);
5558 RTFileDelete(log.c_str());
5559 log = Utf8StrFmt("%s%cVBox.png.%d",
5560 logFolder.c_str(), RTPATH_DELIMITER, i);
5561 RTFileDelete(log.c_str());
5562 }
5563#if defined(RT_OS_WINDOWS)
5564 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5565 RTFileDelete(log.c_str());
5566 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5567 RTFileDelete(log.c_str());
5568#endif
5569
5570 RTDirRemove(logFolder.c_str());
5571 }
5572
5573 /* delete the Snapshots folder, nothing important should be left
5574 * there (we don't check for errors because the user might have
5575 * some private files there that we don't want to delete) */
5576 Utf8Str strFullSnapshotFolder;
5577 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5578 Assert(!strFullSnapshotFolder.isEmpty());
5579 if (RTDirExists(strFullSnapshotFolder.c_str()))
5580 RTDirRemove(strFullSnapshotFolder.c_str());
5581
5582 // delete the directory that contains the settings file, but only
5583 // if it matches the VM name
5584 Utf8Str settingsDir;
5585 if (i_isInOwnDir(&settingsDir))
5586 RTDirRemove(settingsDir.c_str());
5587 }
5588
5589 alock.release();
5590
5591 mParent->i_saveModifiedRegistries();
5592 }
5593 catch (HRESULT aRC) { rc = aRC; }
5594
5595 task.m_pProgress->i_notifyComplete(rc);
5596
5597 LogFlowThisFuncLeave();
5598}
5599
5600HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5601{
5602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5603
5604 HRESULT rc = i_checkStateDependency(MutableStateDep);
5605 if (FAILED(rc)) return rc;
5606
5607 if (mData->mRegistered)
5608 return setError(VBOX_E_INVALID_VM_STATE,
5609 tr("Cannot delete settings of a registered machine"));
5610
5611 // collect files to delete
5612 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5613 if (mData->pMachineConfigFile->fileExists())
5614 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5615
5616 RTCList<ComPtr<IMedium> > llMediums;
5617 for (size_t i = 0; i < aMedia.size(); ++i)
5618 {
5619 IMedium *pIMedium(aMedia[i]);
5620 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5621 if (pMedium.isNull())
5622 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5623 SafeArray<BSTR> ids;
5624 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5625 if (FAILED(rc)) return rc;
5626 /* At this point the medium should not have any back references
5627 * anymore. If it has it is attached to another VM and *must* not
5628 * deleted. */
5629 if (ids.size() < 1)
5630 llMediums.append(pMedium);
5631 }
5632
5633 ComObjPtr<Progress> pProgress;
5634 pProgress.createObject();
5635 rc = pProgress->init(i_getVirtualBox(),
5636 static_cast<IMachine*>(this) /* aInitiator */,
5637 tr("Deleting files"),
5638 true /* fCancellable */,
5639 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5640 tr("Collecting file inventory"));
5641 if (FAILED(rc))
5642 return rc;
5643
5644 /* create and start the task on a separate thread (note that it will not
5645 * start working until we release alock) */
5646 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5647 rc = pTask->createThread();
5648 if (FAILED(rc))
5649 return rc;
5650
5651 pProgress.queryInterfaceTo(aProgress.asOutParam());
5652
5653 LogFlowFuncLeave();
5654
5655 return S_OK;
5656}
5657
5658HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5659{
5660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5661
5662 ComObjPtr<Snapshot> pSnapshot;
5663 HRESULT rc;
5664
5665 if (aNameOrId.isEmpty())
5666 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5667 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5668 else
5669 {
5670 Guid uuid(aNameOrId);
5671 if (uuid.isValid())
5672 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5673 else
5674 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5675 }
5676 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5677
5678 return rc;
5679}
5680
5681HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5682{
5683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5684
5685 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5686 if (FAILED(rc)) return rc;
5687
5688 ComObjPtr<SharedFolder> sharedFolder;
5689 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5690 if (SUCCEEDED(rc))
5691 return setError(VBOX_E_OBJECT_IN_USE,
5692 tr("Shared folder named '%s' already exists"),
5693 aName.c_str());
5694
5695 sharedFolder.createObject();
5696 rc = sharedFolder->init(i_getMachine(),
5697 aName,
5698 aHostPath,
5699 !!aWritable,
5700 !!aAutomount,
5701 true /* fFailOnError */);
5702 if (FAILED(rc)) return rc;
5703
5704 i_setModified(IsModified_SharedFolders);
5705 mHWData.backup();
5706 mHWData->mSharedFolders.push_back(sharedFolder);
5707
5708 /* inform the direct session if any */
5709 alock.release();
5710 i_onSharedFolderChange();
5711
5712 return S_OK;
5713}
5714
5715HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5716{
5717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5718
5719 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5720 if (FAILED(rc)) return rc;
5721
5722 ComObjPtr<SharedFolder> sharedFolder;
5723 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5724 if (FAILED(rc)) return rc;
5725
5726 i_setModified(IsModified_SharedFolders);
5727 mHWData.backup();
5728 mHWData->mSharedFolders.remove(sharedFolder);
5729
5730 /* inform the direct session if any */
5731 alock.release();
5732 i_onSharedFolderChange();
5733
5734 return S_OK;
5735}
5736
5737HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5738{
5739 /* start with No */
5740 *aCanShow = FALSE;
5741
5742 ComPtr<IInternalSessionControl> directControl;
5743 {
5744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5745
5746 if (mData->mSession.mState != SessionState_Locked)
5747 return setError(VBOX_E_INVALID_VM_STATE,
5748 tr("Machine is not locked for session (session state: %s)"),
5749 Global::stringifySessionState(mData->mSession.mState));
5750
5751 if (mData->mSession.mLockType == LockType_VM)
5752 directControl = mData->mSession.mDirectControl;
5753 }
5754
5755 /* ignore calls made after #OnSessionEnd() is called */
5756 if (!directControl)
5757 return S_OK;
5758
5759 LONG64 dummy;
5760 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5761}
5762
5763HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5764{
5765 ComPtr<IInternalSessionControl> directControl;
5766 {
5767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5768
5769 if (mData->mSession.mState != SessionState_Locked)
5770 return setError(E_FAIL,
5771 tr("Machine is not locked for session (session state: %s)"),
5772 Global::stringifySessionState(mData->mSession.mState));
5773
5774 if (mData->mSession.mLockType == LockType_VM)
5775 directControl = mData->mSession.mDirectControl;
5776 }
5777
5778 /* ignore calls made after #OnSessionEnd() is called */
5779 if (!directControl)
5780 return S_OK;
5781
5782 BOOL dummy;
5783 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5784}
5785
5786#ifdef VBOX_WITH_GUEST_PROPS
5787/**
5788 * Look up a guest property in VBoxSVC's internal structures.
5789 */
5790HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5791 com::Utf8Str &aValue,
5792 LONG64 *aTimestamp,
5793 com::Utf8Str &aFlags) const
5794{
5795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5796
5797 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5798 if (it != mHWData->mGuestProperties.end())
5799 {
5800 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5801 aValue = it->second.strValue;
5802 *aTimestamp = it->second.mTimestamp;
5803 GuestPropWriteFlags(it->second.mFlags, szFlags);
5804 aFlags = Utf8Str(szFlags);
5805 }
5806
5807 return S_OK;
5808}
5809
5810/**
5811 * Query the VM that a guest property belongs to for the property.
5812 * @returns E_ACCESSDENIED if the VM process is not available or not
5813 * currently handling queries and the lookup should then be done in
5814 * VBoxSVC.
5815 */
5816HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5817 com::Utf8Str &aValue,
5818 LONG64 *aTimestamp,
5819 com::Utf8Str &aFlags) const
5820{
5821 HRESULT rc = S_OK;
5822 BSTR bValue = NULL;
5823 BSTR bFlags = NULL;
5824
5825 ComPtr<IInternalSessionControl> directControl;
5826 {
5827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5828 if (mData->mSession.mLockType == LockType_VM)
5829 directControl = mData->mSession.mDirectControl;
5830 }
5831
5832 /* ignore calls made after #OnSessionEnd() is called */
5833 if (!directControl)
5834 rc = E_ACCESSDENIED;
5835 else
5836 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5837 0 /* accessMode */,
5838 &bValue, aTimestamp, &bFlags);
5839
5840 aValue = bValue;
5841 aFlags = bFlags;
5842
5843 return rc;
5844}
5845#endif // VBOX_WITH_GUEST_PROPS
5846
5847HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5848 com::Utf8Str &aValue,
5849 LONG64 *aTimestamp,
5850 com::Utf8Str &aFlags)
5851{
5852#ifndef VBOX_WITH_GUEST_PROPS
5853 ReturnComNotImplemented();
5854#else // VBOX_WITH_GUEST_PROPS
5855
5856 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5857
5858 if (rc == E_ACCESSDENIED)
5859 /* The VM is not running or the service is not (yet) accessible */
5860 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5861 return rc;
5862#endif // VBOX_WITH_GUEST_PROPS
5863}
5864
5865HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5866{
5867 LONG64 dummyTimestamp;
5868 com::Utf8Str dummyFlags;
5869 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5870 return rc;
5871
5872}
5873HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5874{
5875 com::Utf8Str dummyFlags;
5876 com::Utf8Str dummyValue;
5877 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5878 return rc;
5879}
5880
5881#ifdef VBOX_WITH_GUEST_PROPS
5882/**
5883 * Set a guest property in VBoxSVC's internal structures.
5884 */
5885HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5886 const com::Utf8Str &aFlags, bool fDelete)
5887{
5888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5889 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5890 if (FAILED(rc)) return rc;
5891
5892 try
5893 {
5894 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5895 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5896 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5897
5898 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5899 if (it == mHWData->mGuestProperties.end())
5900 {
5901 if (!fDelete)
5902 {
5903 i_setModified(IsModified_MachineData);
5904 mHWData.backupEx();
5905
5906 RTTIMESPEC time;
5907 HWData::GuestProperty prop;
5908 prop.strValue = Bstr(aValue).raw();
5909 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5910 prop.mFlags = fFlags;
5911 mHWData->mGuestProperties[aName] = prop;
5912 }
5913 }
5914 else
5915 {
5916 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5917 {
5918 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5919 }
5920 else
5921 {
5922 i_setModified(IsModified_MachineData);
5923 mHWData.backupEx();
5924
5925 /* The backupEx() operation invalidates our iterator,
5926 * so get a new one. */
5927 it = mHWData->mGuestProperties.find(aName);
5928 Assert(it != mHWData->mGuestProperties.end());
5929
5930 if (!fDelete)
5931 {
5932 RTTIMESPEC time;
5933 it->second.strValue = aValue;
5934 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5935 it->second.mFlags = fFlags;
5936 }
5937 else
5938 mHWData->mGuestProperties.erase(it);
5939 }
5940 }
5941
5942 if (SUCCEEDED(rc))
5943 {
5944 alock.release();
5945
5946 mParent->i_onGuestPropertyChange(mData->mUuid,
5947 Bstr(aName).raw(),
5948 Bstr(aValue).raw(),
5949 Bstr(aFlags).raw());
5950 }
5951 }
5952 catch (std::bad_alloc &)
5953 {
5954 rc = E_OUTOFMEMORY;
5955 }
5956
5957 return rc;
5958}
5959
5960/**
5961 * Set a property on the VM that that property belongs to.
5962 * @returns E_ACCESSDENIED if the VM process is not available or not
5963 * currently handling queries and the setting should then be done in
5964 * VBoxSVC.
5965 */
5966HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5967 const com::Utf8Str &aFlags, bool fDelete)
5968{
5969 HRESULT rc;
5970
5971 try
5972 {
5973 ComPtr<IInternalSessionControl> directControl;
5974 {
5975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5976 if (mData->mSession.mLockType == LockType_VM)
5977 directControl = mData->mSession.mDirectControl;
5978 }
5979
5980 BSTR dummy = NULL; /* will not be changed (setter) */
5981 LONG64 dummy64;
5982 if (!directControl)
5983 rc = E_ACCESSDENIED;
5984 else
5985 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5986 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5987 fDelete? 2: 1 /* accessMode */,
5988 &dummy, &dummy64, &dummy);
5989 }
5990 catch (std::bad_alloc &)
5991 {
5992 rc = E_OUTOFMEMORY;
5993 }
5994
5995 return rc;
5996}
5997#endif // VBOX_WITH_GUEST_PROPS
5998
5999HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6000 const com::Utf8Str &aFlags)
6001{
6002#ifndef VBOX_WITH_GUEST_PROPS
6003 ReturnComNotImplemented();
6004#else // VBOX_WITH_GUEST_PROPS
6005 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6006 if (rc == E_ACCESSDENIED)
6007 /* The VM is not running or the service is not (yet) accessible */
6008 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6009 return rc;
6010#endif // VBOX_WITH_GUEST_PROPS
6011}
6012
6013HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6014{
6015 return setGuestProperty(aProperty, aValue, "");
6016}
6017
6018HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6019{
6020#ifndef VBOX_WITH_GUEST_PROPS
6021 ReturnComNotImplemented();
6022#else // VBOX_WITH_GUEST_PROPS
6023 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6024 if (rc == E_ACCESSDENIED)
6025 /* The VM is not running or the service is not (yet) accessible */
6026 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6027 return rc;
6028#endif // VBOX_WITH_GUEST_PROPS
6029}
6030
6031#ifdef VBOX_WITH_GUEST_PROPS
6032/**
6033 * Enumerate the guest properties in VBoxSVC's internal structures.
6034 */
6035HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6036 std::vector<com::Utf8Str> &aNames,
6037 std::vector<com::Utf8Str> &aValues,
6038 std::vector<LONG64> &aTimestamps,
6039 std::vector<com::Utf8Str> &aFlags)
6040{
6041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6042 Utf8Str strPatterns(aPatterns);
6043
6044 /*
6045 * Look for matching patterns and build up a list.
6046 */
6047 HWData::GuestPropertyMap propMap;
6048 for (HWData::GuestPropertyMap::const_iterator
6049 it = mHWData->mGuestProperties.begin();
6050 it != mHWData->mGuestProperties.end();
6051 ++it)
6052 {
6053 if ( strPatterns.isEmpty()
6054 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6055 RTSTR_MAX,
6056 it->first.c_str(),
6057 RTSTR_MAX,
6058 NULL)
6059 )
6060 propMap.insert(*it);
6061 }
6062
6063 alock.release();
6064
6065 /*
6066 * And build up the arrays for returning the property information.
6067 */
6068 size_t cEntries = propMap.size();
6069
6070 aNames.resize(cEntries);
6071 aValues.resize(cEntries);
6072 aTimestamps.resize(cEntries);
6073 aFlags.resize(cEntries);
6074
6075 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6076 size_t i = 0;
6077 for (HWData::GuestPropertyMap::const_iterator
6078 it = propMap.begin();
6079 it != propMap.end();
6080 ++it, ++i)
6081 {
6082 aNames[i] = it->first;
6083 aValues[i] = it->second.strValue;
6084 aTimestamps[i] = it->second.mTimestamp;
6085 GuestPropWriteFlags(it->second.mFlags, szFlags);
6086 aFlags[i] = Utf8Str(szFlags);
6087 }
6088
6089 return S_OK;
6090}
6091
6092/**
6093 * Enumerate the properties managed by a VM.
6094 * @returns E_ACCESSDENIED if the VM process is not available or not
6095 * currently handling queries and the setting should then be done in
6096 * VBoxSVC.
6097 */
6098HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6099 std::vector<com::Utf8Str> &aNames,
6100 std::vector<com::Utf8Str> &aValues,
6101 std::vector<LONG64> &aTimestamps,
6102 std::vector<com::Utf8Str> &aFlags)
6103{
6104 HRESULT rc;
6105 ComPtr<IInternalSessionControl> directControl;
6106 {
6107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6108 if (mData->mSession.mLockType == LockType_VM)
6109 directControl = mData->mSession.mDirectControl;
6110 }
6111
6112 com::SafeArray<BSTR> bNames;
6113 com::SafeArray<BSTR> bValues;
6114 com::SafeArray<LONG64> bTimestamps;
6115 com::SafeArray<BSTR> bFlags;
6116
6117 if (!directControl)
6118 rc = E_ACCESSDENIED;
6119 else
6120 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6121 ComSafeArrayAsOutParam(bNames),
6122 ComSafeArrayAsOutParam(bValues),
6123 ComSafeArrayAsOutParam(bTimestamps),
6124 ComSafeArrayAsOutParam(bFlags));
6125 size_t i;
6126 aNames.resize(bNames.size());
6127 for (i = 0; i < bNames.size(); ++i)
6128 aNames[i] = Utf8Str(bNames[i]);
6129 aValues.resize(bValues.size());
6130 for (i = 0; i < bValues.size(); ++i)
6131 aValues[i] = Utf8Str(bValues[i]);
6132 aTimestamps.resize(bTimestamps.size());
6133 for (i = 0; i < bTimestamps.size(); ++i)
6134 aTimestamps[i] = bTimestamps[i];
6135 aFlags.resize(bFlags.size());
6136 for (i = 0; i < bFlags.size(); ++i)
6137 aFlags[i] = Utf8Str(bFlags[i]);
6138
6139 return rc;
6140}
6141#endif // VBOX_WITH_GUEST_PROPS
6142HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6143 std::vector<com::Utf8Str> &aNames,
6144 std::vector<com::Utf8Str> &aValues,
6145 std::vector<LONG64> &aTimestamps,
6146 std::vector<com::Utf8Str> &aFlags)
6147{
6148#ifndef VBOX_WITH_GUEST_PROPS
6149 ReturnComNotImplemented();
6150#else // VBOX_WITH_GUEST_PROPS
6151
6152 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6153
6154 if (rc == E_ACCESSDENIED)
6155 /* The VM is not running or the service is not (yet) accessible */
6156 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6157 return rc;
6158#endif // VBOX_WITH_GUEST_PROPS
6159}
6160
6161HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6162 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6163{
6164 MediumAttachmentList atts;
6165
6166 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6167 if (FAILED(rc)) return rc;
6168
6169 aMediumAttachments.resize(atts.size());
6170 size_t i = 0;
6171 for (MediumAttachmentList::const_iterator
6172 it = atts.begin();
6173 it != atts.end();
6174 ++it, ++i)
6175 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6176
6177 return S_OK;
6178}
6179
6180HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6181 LONG aControllerPort,
6182 LONG aDevice,
6183 ComPtr<IMediumAttachment> &aAttachment)
6184{
6185 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6186 aName.c_str(), aControllerPort, aDevice));
6187
6188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6189
6190 aAttachment = NULL;
6191
6192 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6193 aName,
6194 aControllerPort,
6195 aDevice);
6196 if (pAttach.isNull())
6197 return setError(VBOX_E_OBJECT_NOT_FOUND,
6198 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6199 aDevice, aControllerPort, aName.c_str());
6200
6201 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6202
6203 return S_OK;
6204}
6205
6206
6207HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6208 StorageBus_T aConnectionType,
6209 ComPtr<IStorageController> &aController)
6210{
6211 if ( (aConnectionType <= StorageBus_Null)
6212 || (aConnectionType > StorageBus_PCIe))
6213 return setError(E_INVALIDARG,
6214 tr("Invalid connection type: %d"),
6215 aConnectionType);
6216
6217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6218
6219 HRESULT rc = i_checkStateDependency(MutableStateDep);
6220 if (FAILED(rc)) return rc;
6221
6222 /* try to find one with the name first. */
6223 ComObjPtr<StorageController> ctrl;
6224
6225 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6226 if (SUCCEEDED(rc))
6227 return setError(VBOX_E_OBJECT_IN_USE,
6228 tr("Storage controller named '%s' already exists"),
6229 aName.c_str());
6230
6231 ctrl.createObject();
6232
6233 /* get a new instance number for the storage controller */
6234 ULONG ulInstance = 0;
6235 bool fBootable = true;
6236 for (StorageControllerList::const_iterator
6237 it = mStorageControllers->begin();
6238 it != mStorageControllers->end();
6239 ++it)
6240 {
6241 if ((*it)->i_getStorageBus() == aConnectionType)
6242 {
6243 ULONG ulCurInst = (*it)->i_getInstance();
6244
6245 if (ulCurInst >= ulInstance)
6246 ulInstance = ulCurInst + 1;
6247
6248 /* Only one controller of each type can be marked as bootable. */
6249 if ((*it)->i_getBootable())
6250 fBootable = false;
6251 }
6252 }
6253
6254 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6255 if (FAILED(rc)) return rc;
6256
6257 i_setModified(IsModified_Storage);
6258 mStorageControllers.backup();
6259 mStorageControllers->push_back(ctrl);
6260
6261 ctrl.queryInterfaceTo(aController.asOutParam());
6262
6263 /* inform the direct session if any */
6264 alock.release();
6265 i_onStorageControllerChange();
6266
6267 return S_OK;
6268}
6269
6270HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6271 ComPtr<IStorageController> &aStorageController)
6272{
6273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6274
6275 ComObjPtr<StorageController> ctrl;
6276
6277 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6278 if (SUCCEEDED(rc))
6279 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6280
6281 return rc;
6282}
6283
6284HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6285 ULONG aInstance,
6286 ComPtr<IStorageController> &aStorageController)
6287{
6288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6289
6290 for (StorageControllerList::const_iterator
6291 it = mStorageControllers->begin();
6292 it != mStorageControllers->end();
6293 ++it)
6294 {
6295 if ( (*it)->i_getStorageBus() == aConnectionType
6296 && (*it)->i_getInstance() == aInstance)
6297 {
6298 (*it).queryInterfaceTo(aStorageController.asOutParam());
6299 return S_OK;
6300 }
6301 }
6302
6303 return setError(VBOX_E_OBJECT_NOT_FOUND,
6304 tr("Could not find a storage controller with instance number '%lu'"),
6305 aInstance);
6306}
6307
6308HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6309{
6310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6311
6312 HRESULT rc = i_checkStateDependency(MutableStateDep);
6313 if (FAILED(rc)) return rc;
6314
6315 ComObjPtr<StorageController> ctrl;
6316
6317 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6318 if (SUCCEEDED(rc))
6319 {
6320 /* Ensure that only one controller of each type is marked as bootable. */
6321 if (aBootable == TRUE)
6322 {
6323 for (StorageControllerList::const_iterator
6324 it = mStorageControllers->begin();
6325 it != mStorageControllers->end();
6326 ++it)
6327 {
6328 ComObjPtr<StorageController> aCtrl = (*it);
6329
6330 if ( (aCtrl->i_getName() != aName)
6331 && aCtrl->i_getBootable() == TRUE
6332 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6333 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6334 {
6335 aCtrl->i_setBootable(FALSE);
6336 break;
6337 }
6338 }
6339 }
6340
6341 if (SUCCEEDED(rc))
6342 {
6343 ctrl->i_setBootable(aBootable);
6344 i_setModified(IsModified_Storage);
6345 }
6346 }
6347
6348 if (SUCCEEDED(rc))
6349 {
6350 /* inform the direct session if any */
6351 alock.release();
6352 i_onStorageControllerChange();
6353 }
6354
6355 return rc;
6356}
6357
6358HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6359{
6360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6361
6362 HRESULT rc = i_checkStateDependency(MutableStateDep);
6363 if (FAILED(rc)) return rc;
6364
6365 ComObjPtr<StorageController> ctrl;
6366 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6367 if (FAILED(rc)) return rc;
6368
6369 {
6370 /* find all attached devices to the appropriate storage controller and detach them all */
6371 // make a temporary list because detachDevice invalidates iterators into
6372 // mMediumAttachments
6373 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6374
6375 for (MediumAttachmentList::const_iterator
6376 it = llAttachments2.begin();
6377 it != llAttachments2.end();
6378 ++it)
6379 {
6380 MediumAttachment *pAttachTemp = *it;
6381
6382 AutoCaller localAutoCaller(pAttachTemp);
6383 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6384
6385 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6386
6387 if (pAttachTemp->i_getControllerName() == aName)
6388 {
6389 rc = i_detachDevice(pAttachTemp, alock, NULL);
6390 if (FAILED(rc)) return rc;
6391 }
6392 }
6393 }
6394
6395 /* We can remove it now. */
6396 i_setModified(IsModified_Storage);
6397 mStorageControllers.backup();
6398
6399 ctrl->i_unshare();
6400
6401 mStorageControllers->remove(ctrl);
6402
6403 /* inform the direct session if any */
6404 alock.release();
6405 i_onStorageControllerChange();
6406
6407 return S_OK;
6408}
6409
6410HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6411 ComPtr<IUSBController> &aController)
6412{
6413 if ( (aType <= USBControllerType_Null)
6414 || (aType >= USBControllerType_Last))
6415 return setError(E_INVALIDARG,
6416 tr("Invalid USB controller type: %d"),
6417 aType);
6418
6419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6420
6421 HRESULT rc = i_checkStateDependency(MutableStateDep);
6422 if (FAILED(rc)) return rc;
6423
6424 /* try to find one with the same type first. */
6425 ComObjPtr<USBController> ctrl;
6426
6427 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6428 if (SUCCEEDED(rc))
6429 return setError(VBOX_E_OBJECT_IN_USE,
6430 tr("USB controller named '%s' already exists"),
6431 aName.c_str());
6432
6433 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6434 ULONG maxInstances;
6435 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6436 if (FAILED(rc))
6437 return rc;
6438
6439 ULONG cInstances = i_getUSBControllerCountByType(aType);
6440 if (cInstances >= maxInstances)
6441 return setError(E_INVALIDARG,
6442 tr("Too many USB controllers of this type"));
6443
6444 ctrl.createObject();
6445
6446 rc = ctrl->init(this, aName, aType);
6447 if (FAILED(rc)) return rc;
6448
6449 i_setModified(IsModified_USB);
6450 mUSBControllers.backup();
6451 mUSBControllers->push_back(ctrl);
6452
6453 ctrl.queryInterfaceTo(aController.asOutParam());
6454
6455 /* inform the direct session if any */
6456 alock.release();
6457 i_onUSBControllerChange();
6458
6459 return S_OK;
6460}
6461
6462HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6463{
6464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6465
6466 ComObjPtr<USBController> ctrl;
6467
6468 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6469 if (SUCCEEDED(rc))
6470 ctrl.queryInterfaceTo(aController.asOutParam());
6471
6472 return rc;
6473}
6474
6475HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6476 ULONG *aControllers)
6477{
6478 if ( (aType <= USBControllerType_Null)
6479 || (aType >= USBControllerType_Last))
6480 return setError(E_INVALIDARG,
6481 tr("Invalid USB controller type: %d"),
6482 aType);
6483
6484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6485
6486 ComObjPtr<USBController> ctrl;
6487
6488 *aControllers = i_getUSBControllerCountByType(aType);
6489
6490 return S_OK;
6491}
6492
6493HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6494{
6495
6496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6497
6498 HRESULT rc = i_checkStateDependency(MutableStateDep);
6499 if (FAILED(rc)) return rc;
6500
6501 ComObjPtr<USBController> ctrl;
6502 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6503 if (FAILED(rc)) return rc;
6504
6505 i_setModified(IsModified_USB);
6506 mUSBControllers.backup();
6507
6508 ctrl->i_unshare();
6509
6510 mUSBControllers->remove(ctrl);
6511
6512 /* inform the direct session if any */
6513 alock.release();
6514 i_onUSBControllerChange();
6515
6516 return S_OK;
6517}
6518
6519HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6520 ULONG *aOriginX,
6521 ULONG *aOriginY,
6522 ULONG *aWidth,
6523 ULONG *aHeight,
6524 BOOL *aEnabled)
6525{
6526 uint32_t u32OriginX= 0;
6527 uint32_t u32OriginY= 0;
6528 uint32_t u32Width = 0;
6529 uint32_t u32Height = 0;
6530 uint16_t u16Flags = 0;
6531
6532 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6533 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6534 if (RT_FAILURE(vrc))
6535 {
6536#ifdef RT_OS_WINDOWS
6537 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6538 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6539 * So just assign fEnable to TRUE again.
6540 * The right fix would be to change GUI API wrappers to make sure that parameters
6541 * are changed only if API succeeds.
6542 */
6543 *aEnabled = TRUE;
6544#endif
6545 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6546 tr("Saved guest size is not available (%Rrc)"),
6547 vrc);
6548 }
6549
6550 *aOriginX = u32OriginX;
6551 *aOriginY = u32OriginY;
6552 *aWidth = u32Width;
6553 *aHeight = u32Height;
6554 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6555
6556 return S_OK;
6557}
6558
6559HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6560 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6561{
6562 if (aScreenId != 0)
6563 return E_NOTIMPL;
6564
6565 if ( aBitmapFormat != BitmapFormat_BGR0
6566 && aBitmapFormat != BitmapFormat_BGRA
6567 && aBitmapFormat != BitmapFormat_RGBA
6568 && aBitmapFormat != BitmapFormat_PNG)
6569 return setError(E_NOTIMPL,
6570 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6571
6572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6573
6574 uint8_t *pu8Data = NULL;
6575 uint32_t cbData = 0;
6576 uint32_t u32Width = 0;
6577 uint32_t u32Height = 0;
6578
6579 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6580
6581 if (RT_FAILURE(vrc))
6582 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6583 tr("Saved thumbnail data is not available (%Rrc)"),
6584 vrc);
6585
6586 HRESULT hr = S_OK;
6587
6588 *aWidth = u32Width;
6589 *aHeight = u32Height;
6590
6591 if (cbData > 0)
6592 {
6593 /* Convert pixels to the format expected by the API caller. */
6594 if (aBitmapFormat == BitmapFormat_BGR0)
6595 {
6596 /* [0] B, [1] G, [2] R, [3] 0. */
6597 aData.resize(cbData);
6598 memcpy(&aData.front(), pu8Data, cbData);
6599 }
6600 else if (aBitmapFormat == BitmapFormat_BGRA)
6601 {
6602 /* [0] B, [1] G, [2] R, [3] A. */
6603 aData.resize(cbData);
6604 for (uint32_t i = 0; i < cbData; i += 4)
6605 {
6606 aData[i] = pu8Data[i];
6607 aData[i + 1] = pu8Data[i + 1];
6608 aData[i + 2] = pu8Data[i + 2];
6609 aData[i + 3] = 0xff;
6610 }
6611 }
6612 else if (aBitmapFormat == BitmapFormat_RGBA)
6613 {
6614 /* [0] R, [1] G, [2] B, [3] A. */
6615 aData.resize(cbData);
6616 for (uint32_t i = 0; i < cbData; i += 4)
6617 {
6618 aData[i] = pu8Data[i + 2];
6619 aData[i + 1] = pu8Data[i + 1];
6620 aData[i + 2] = pu8Data[i];
6621 aData[i + 3] = 0xff;
6622 }
6623 }
6624 else if (aBitmapFormat == BitmapFormat_PNG)
6625 {
6626 uint8_t *pu8PNG = NULL;
6627 uint32_t cbPNG = 0;
6628 uint32_t cxPNG = 0;
6629 uint32_t cyPNG = 0;
6630
6631 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6632
6633 if (RT_SUCCESS(vrc))
6634 {
6635 aData.resize(cbPNG);
6636 if (cbPNG)
6637 memcpy(&aData.front(), pu8PNG, cbPNG);
6638 }
6639 else
6640 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6641 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6642 vrc);
6643
6644 RTMemFree(pu8PNG);
6645 }
6646 }
6647
6648 freeSavedDisplayScreenshot(pu8Data);
6649
6650 return hr;
6651}
6652
6653HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6654 ULONG *aWidth,
6655 ULONG *aHeight,
6656 std::vector<BitmapFormat_T> &aBitmapFormats)
6657{
6658 if (aScreenId != 0)
6659 return E_NOTIMPL;
6660
6661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6662
6663 uint8_t *pu8Data = NULL;
6664 uint32_t cbData = 0;
6665 uint32_t u32Width = 0;
6666 uint32_t u32Height = 0;
6667
6668 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6669
6670 if (RT_FAILURE(vrc))
6671 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6672 tr("Saved screenshot data is not available (%Rrc)"),
6673 vrc);
6674
6675 *aWidth = u32Width;
6676 *aHeight = u32Height;
6677 aBitmapFormats.resize(1);
6678 aBitmapFormats[0] = BitmapFormat_PNG;
6679
6680 freeSavedDisplayScreenshot(pu8Data);
6681
6682 return S_OK;
6683}
6684
6685HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6686 BitmapFormat_T aBitmapFormat,
6687 ULONG *aWidth,
6688 ULONG *aHeight,
6689 std::vector<BYTE> &aData)
6690{
6691 if (aScreenId != 0)
6692 return E_NOTIMPL;
6693
6694 if (aBitmapFormat != BitmapFormat_PNG)
6695 return E_NOTIMPL;
6696
6697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6698
6699 uint8_t *pu8Data = NULL;
6700 uint32_t cbData = 0;
6701 uint32_t u32Width = 0;
6702 uint32_t u32Height = 0;
6703
6704 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6705
6706 if (RT_FAILURE(vrc))
6707 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6708 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6709 vrc);
6710
6711 *aWidth = u32Width;
6712 *aHeight = u32Height;
6713
6714 aData.resize(cbData);
6715 if (cbData)
6716 memcpy(&aData.front(), pu8Data, cbData);
6717
6718 freeSavedDisplayScreenshot(pu8Data);
6719
6720 return S_OK;
6721}
6722
6723HRESULT Machine::hotPlugCPU(ULONG aCpu)
6724{
6725 HRESULT rc = S_OK;
6726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6727
6728 if (!mHWData->mCPUHotPlugEnabled)
6729 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6730
6731 if (aCpu >= mHWData->mCPUCount)
6732 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6733
6734 if (mHWData->mCPUAttached[aCpu])
6735 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6736
6737 alock.release();
6738 rc = i_onCPUChange(aCpu, false);
6739 alock.acquire();
6740 if (FAILED(rc)) return rc;
6741
6742 i_setModified(IsModified_MachineData);
6743 mHWData.backup();
6744 mHWData->mCPUAttached[aCpu] = true;
6745
6746 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6747 if (Global::IsOnline(mData->mMachineState))
6748 i_saveSettings(NULL);
6749
6750 return S_OK;
6751}
6752
6753HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6754{
6755 HRESULT rc = S_OK;
6756
6757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6758
6759 if (!mHWData->mCPUHotPlugEnabled)
6760 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6761
6762 if (aCpu >= SchemaDefs::MaxCPUCount)
6763 return setError(E_INVALIDARG,
6764 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6765 SchemaDefs::MaxCPUCount);
6766
6767 if (!mHWData->mCPUAttached[aCpu])
6768 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6769
6770 /* CPU 0 can't be detached */
6771 if (aCpu == 0)
6772 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6773
6774 alock.release();
6775 rc = i_onCPUChange(aCpu, true);
6776 alock.acquire();
6777 if (FAILED(rc)) return rc;
6778
6779 i_setModified(IsModified_MachineData);
6780 mHWData.backup();
6781 mHWData->mCPUAttached[aCpu] = false;
6782
6783 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6784 if (Global::IsOnline(mData->mMachineState))
6785 i_saveSettings(NULL);
6786
6787 return S_OK;
6788}
6789
6790HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6791{
6792 *aAttached = false;
6793
6794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6795
6796 /* If hotplug is enabled the CPU is always enabled. */
6797 if (!mHWData->mCPUHotPlugEnabled)
6798 {
6799 if (aCpu < mHWData->mCPUCount)
6800 *aAttached = true;
6801 }
6802 else
6803 {
6804 if (aCpu < SchemaDefs::MaxCPUCount)
6805 *aAttached = mHWData->mCPUAttached[aCpu];
6806 }
6807
6808 return S_OK;
6809}
6810
6811HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6812{
6813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6814
6815 Utf8Str log = i_getLogFilename(aIdx);
6816 if (!RTFileExists(log.c_str()))
6817 log.setNull();
6818 aFilename = log;
6819
6820 return S_OK;
6821}
6822
6823HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6824{
6825 if (aSize < 0)
6826 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6827
6828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 HRESULT rc = S_OK;
6831 Utf8Str log = i_getLogFilename(aIdx);
6832
6833 /* do not unnecessarily hold the lock while doing something which does
6834 * not need the lock and potentially takes a long time. */
6835 alock.release();
6836
6837 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6838 * keeps the SOAP reply size under 1M for the webservice (we're using
6839 * base64 encoded strings for binary data for years now, avoiding the
6840 * expansion of each byte array element to approx. 25 bytes of XML. */
6841 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6842 aData.resize(cbData);
6843
6844 RTFILE LogFile;
6845 int vrc = RTFileOpen(&LogFile, log.c_str(),
6846 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6847 if (RT_SUCCESS(vrc))
6848 {
6849 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6850 if (RT_SUCCESS(vrc))
6851 aData.resize(cbData);
6852 else
6853 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6854 tr("Could not read log file '%s' (%Rrc)"),
6855 log.c_str(), vrc);
6856 RTFileClose(LogFile);
6857 }
6858 else
6859 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6860 tr("Could not open log file '%s' (%Rrc)"),
6861 log.c_str(), vrc);
6862
6863 if (FAILED(rc))
6864 aData.resize(0);
6865
6866 return rc;
6867}
6868
6869
6870/**
6871 * Currently this method doesn't attach device to the running VM,
6872 * just makes sure it's plugged on next VM start.
6873 */
6874HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6875{
6876 // lock scope
6877 {
6878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6879
6880 HRESULT rc = i_checkStateDependency(MutableStateDep);
6881 if (FAILED(rc)) return rc;
6882
6883 ChipsetType_T aChipset = ChipsetType_PIIX3;
6884 COMGETTER(ChipsetType)(&aChipset);
6885
6886 if (aChipset != ChipsetType_ICH9)
6887 {
6888 return setError(E_INVALIDARG,
6889 tr("Host PCI attachment only supported with ICH9 chipset"));
6890 }
6891
6892 // check if device with this host PCI address already attached
6893 for (HWData::PCIDeviceAssignmentList::const_iterator
6894 it = mHWData->mPCIDeviceAssignments.begin();
6895 it != mHWData->mPCIDeviceAssignments.end();
6896 ++it)
6897 {
6898 LONG iHostAddress = -1;
6899 ComPtr<PCIDeviceAttachment> pAttach;
6900 pAttach = *it;
6901 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6902 if (iHostAddress == aHostAddress)
6903 return setError(E_INVALIDARG,
6904 tr("Device with host PCI address already attached to this VM"));
6905 }
6906
6907 ComObjPtr<PCIDeviceAttachment> pda;
6908 char name[32];
6909
6910 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6911 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6912 pda.createObject();
6913 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6914 i_setModified(IsModified_MachineData);
6915 mHWData.backup();
6916 mHWData->mPCIDeviceAssignments.push_back(pda);
6917 }
6918
6919 return S_OK;
6920}
6921
6922/**
6923 * Currently this method doesn't detach device from the running VM,
6924 * just makes sure it's not plugged on next VM start.
6925 */
6926HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6927{
6928 ComObjPtr<PCIDeviceAttachment> pAttach;
6929 bool fRemoved = false;
6930 HRESULT rc;
6931
6932 // lock scope
6933 {
6934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6935
6936 rc = i_checkStateDependency(MutableStateDep);
6937 if (FAILED(rc)) return rc;
6938
6939 for (HWData::PCIDeviceAssignmentList::const_iterator
6940 it = mHWData->mPCIDeviceAssignments.begin();
6941 it != mHWData->mPCIDeviceAssignments.end();
6942 ++it)
6943 {
6944 LONG iHostAddress = -1;
6945 pAttach = *it;
6946 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6947 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6948 {
6949 i_setModified(IsModified_MachineData);
6950 mHWData.backup();
6951 mHWData->mPCIDeviceAssignments.remove(pAttach);
6952 fRemoved = true;
6953 break;
6954 }
6955 }
6956 }
6957
6958
6959 /* Fire event outside of the lock */
6960 if (fRemoved)
6961 {
6962 Assert(!pAttach.isNull());
6963 ComPtr<IEventSource> es;
6964 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6965 Assert(SUCCEEDED(rc));
6966 Bstr mid;
6967 rc = this->COMGETTER(Id)(mid.asOutParam());
6968 Assert(SUCCEEDED(rc));
6969 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6970 }
6971
6972 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6973 tr("No host PCI device %08x attached"),
6974 aHostAddress
6975 );
6976}
6977
6978HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6979{
6980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6981
6982 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6983 size_t i = 0;
6984 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6985 it = mHWData->mPCIDeviceAssignments.begin();
6986 it != mHWData->mPCIDeviceAssignments.end();
6987 ++it, ++i)
6988 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6989
6990 return S_OK;
6991}
6992
6993HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6994{
6995 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6996
6997 return S_OK;
6998}
6999
7000HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7001{
7002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7003
7004 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7005
7006 return S_OK;
7007}
7008
7009HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7010{
7011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7012 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7013 if (SUCCEEDED(hrc))
7014 {
7015 hrc = mHWData.backupEx();
7016 if (SUCCEEDED(hrc))
7017 {
7018 i_setModified(IsModified_MachineData);
7019 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7020 }
7021 }
7022 return hrc;
7023}
7024
7025HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7026{
7027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7028 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7029 return S_OK;
7030}
7031
7032HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7033{
7034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7035 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7036 if (SUCCEEDED(hrc))
7037 {
7038 hrc = mHWData.backupEx();
7039 if (SUCCEEDED(hrc))
7040 {
7041 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7042 if (SUCCEEDED(hrc))
7043 i_setModified(IsModified_MachineData);
7044 }
7045 }
7046 return hrc;
7047}
7048
7049HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7050{
7051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7052
7053 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7054
7055 return S_OK;
7056}
7057
7058HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7059{
7060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7061 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7062 if (SUCCEEDED(hrc))
7063 {
7064 hrc = mHWData.backupEx();
7065 if (SUCCEEDED(hrc))
7066 {
7067 i_setModified(IsModified_MachineData);
7068 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7069 }
7070 }
7071 return hrc;
7072}
7073
7074HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7075{
7076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7077
7078 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7079
7080 return S_OK;
7081}
7082
7083HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7084{
7085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7086
7087 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7088 if ( SUCCEEDED(hrc)
7089 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7090 {
7091 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7092 int vrc;
7093
7094 if (aAutostartEnabled)
7095 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7096 else
7097 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7098
7099 if (RT_SUCCESS(vrc))
7100 {
7101 hrc = mHWData.backupEx();
7102 if (SUCCEEDED(hrc))
7103 {
7104 i_setModified(IsModified_MachineData);
7105 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7106 }
7107 }
7108 else if (vrc == VERR_NOT_SUPPORTED)
7109 hrc = setError(VBOX_E_NOT_SUPPORTED,
7110 tr("The VM autostart feature is not supported on this platform"));
7111 else if (vrc == VERR_PATH_NOT_FOUND)
7112 hrc = setError(E_FAIL,
7113 tr("The path to the autostart database is not set"));
7114 else
7115 hrc = setError(E_UNEXPECTED,
7116 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7117 aAutostartEnabled ? "Adding" : "Removing",
7118 mUserData->s.strName.c_str(), vrc);
7119 }
7120 return hrc;
7121}
7122
7123HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7124{
7125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7126
7127 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7128
7129 return S_OK;
7130}
7131
7132HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7133{
7134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7135 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7136 if (SUCCEEDED(hrc))
7137 {
7138 hrc = mHWData.backupEx();
7139 if (SUCCEEDED(hrc))
7140 {
7141 i_setModified(IsModified_MachineData);
7142 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7143 }
7144 }
7145 return hrc;
7146}
7147
7148HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7149{
7150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7151
7152 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7153
7154 return S_OK;
7155}
7156
7157HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7158{
7159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7160 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7161 if ( SUCCEEDED(hrc)
7162 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7163 {
7164 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7165 int vrc;
7166
7167 if (aAutostopType != AutostopType_Disabled)
7168 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7169 else
7170 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7171
7172 if (RT_SUCCESS(vrc))
7173 {
7174 hrc = mHWData.backupEx();
7175 if (SUCCEEDED(hrc))
7176 {
7177 i_setModified(IsModified_MachineData);
7178 mHWData->mAutostart.enmAutostopType = aAutostopType;
7179 }
7180 }
7181 else if (vrc == VERR_NOT_SUPPORTED)
7182 hrc = setError(VBOX_E_NOT_SUPPORTED,
7183 tr("The VM autostop feature is not supported on this platform"));
7184 else if (vrc == VERR_PATH_NOT_FOUND)
7185 hrc = setError(E_FAIL,
7186 tr("The path to the autostart database is not set"));
7187 else
7188 hrc = setError(E_UNEXPECTED,
7189 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7190 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7191 mUserData->s.strName.c_str(), vrc);
7192 }
7193 return hrc;
7194}
7195
7196HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7197{
7198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7199
7200 aDefaultFrontend = mHWData->mDefaultFrontend;
7201
7202 return S_OK;
7203}
7204
7205HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7206{
7207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7208 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7209 if (SUCCEEDED(hrc))
7210 {
7211 hrc = mHWData.backupEx();
7212 if (SUCCEEDED(hrc))
7213 {
7214 i_setModified(IsModified_MachineData);
7215 mHWData->mDefaultFrontend = aDefaultFrontend;
7216 }
7217 }
7218 return hrc;
7219}
7220
7221HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7222{
7223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7224 size_t cbIcon = mUserData->s.ovIcon.size();
7225 aIcon.resize(cbIcon);
7226 if (cbIcon)
7227 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7228 return S_OK;
7229}
7230
7231HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7232{
7233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7234 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7235 if (SUCCEEDED(hrc))
7236 {
7237 i_setModified(IsModified_MachineData);
7238 mUserData.backup();
7239 size_t cbIcon = aIcon.size();
7240 mUserData->s.ovIcon.resize(cbIcon);
7241 if (cbIcon)
7242 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7243 }
7244 return hrc;
7245}
7246
7247HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7248{
7249#ifdef VBOX_WITH_USB
7250 *aUSBProxyAvailable = true;
7251#else
7252 *aUSBProxyAvailable = false;
7253#endif
7254 return S_OK;
7255}
7256
7257HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7258{
7259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7260
7261 aVMProcessPriority = mUserData->s.strVMPriority;
7262
7263 return S_OK;
7264}
7265
7266HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7267{
7268 RT_NOREF(aVMProcessPriority);
7269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7270 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7271 if (SUCCEEDED(hrc))
7272 {
7273 /** @todo r=klaus: currently this is marked as not implemented, as
7274 * the code for setting the priority of the process is not there
7275 * (neither when starting the VM nor at runtime). */
7276 ReturnComNotImplemented();
7277#if 0
7278 hrc = mUserData.backupEx();
7279 if (SUCCEEDED(hrc))
7280 {
7281 i_setModified(IsModified_MachineData);
7282 mUserData->s.strVMPriority = aVMProcessPriority;
7283 }
7284#endif
7285 }
7286 return hrc;
7287}
7288
7289HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7290 ComPtr<IProgress> &aProgress)
7291{
7292 ComObjPtr<Progress> pP;
7293 Progress *ppP = pP;
7294 IProgress *iP = static_cast<IProgress *>(ppP);
7295 IProgress **pProgress = &iP;
7296
7297 IMachine *pTarget = aTarget;
7298
7299 /* Convert the options. */
7300 RTCList<CloneOptions_T> optList;
7301 if (aOptions.size())
7302 for (size_t i = 0; i < aOptions.size(); ++i)
7303 optList.append(aOptions[i]);
7304
7305 if (optList.contains(CloneOptions_Link))
7306 {
7307 if (!i_isSnapshotMachine())
7308 return setError(E_INVALIDARG,
7309 tr("Linked clone can only be created from a snapshot"));
7310 if (aMode != CloneMode_MachineState)
7311 return setError(E_INVALIDARG,
7312 tr("Linked clone can only be created for a single machine state"));
7313 }
7314 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7315
7316 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7317
7318 HRESULT rc = pWorker->start(pProgress);
7319
7320 pP = static_cast<Progress *>(*pProgress);
7321 pP.queryInterfaceTo(aProgress.asOutParam());
7322
7323 return rc;
7324
7325}
7326
7327HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7328 const com::Utf8Str &aType,
7329 ComPtr<IProgress> &aProgress)
7330{
7331 LogFlowThisFuncEnter();
7332
7333 ComObjPtr<Progress> progress;
7334
7335 progress.createObject();
7336
7337 HRESULT rc = S_OK;
7338 Utf8Str targetPath = aTargetPath;
7339 Utf8Str type = aType;
7340
7341 /* Initialize our worker task */
7342 MachineMoveVM* task = NULL;
7343 try
7344 {
7345 task = new MachineMoveVM(this, targetPath, type, progress);
7346 }
7347 catch(...)
7348 {
7349 delete task;
7350 return rc;
7351 }
7352
7353 /*
7354 * task pointer will be owned by the ThreadTask class.
7355 * There is no need to call operator "delete" in the end.
7356 */
7357 rc = task->init();
7358 if (SUCCEEDED(rc))
7359 {
7360 rc = task->createThread();
7361 if (FAILED(rc))
7362 {
7363 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7364 }
7365
7366 /* Return progress to the caller */
7367 progress.queryInterfaceTo(aProgress.asOutParam());
7368 }
7369
7370 LogFlowThisFuncLeave();
7371 return rc;
7372
7373}
7374
7375HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7376{
7377 NOREF(aProgress);
7378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7379
7380 // This check should always fail.
7381 HRESULT rc = i_checkStateDependency(MutableStateDep);
7382 if (FAILED(rc)) return rc;
7383
7384 AssertFailedReturn(E_NOTIMPL);
7385}
7386
7387HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7388{
7389 NOREF(aSavedStateFile);
7390 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7391
7392 // This check should always fail.
7393 HRESULT rc = i_checkStateDependency(MutableStateDep);
7394 if (FAILED(rc)) return rc;
7395
7396 AssertFailedReturn(E_NOTIMPL);
7397}
7398
7399HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7400{
7401 NOREF(aFRemoveFile);
7402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7403
7404 // This check should always fail.
7405 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7406 if (FAILED(rc)) return rc;
7407
7408 AssertFailedReturn(E_NOTIMPL);
7409}
7410
7411// public methods for internal purposes
7412/////////////////////////////////////////////////////////////////////////////
7413
7414/**
7415 * Adds the given IsModified_* flag to the dirty flags of the machine.
7416 * This must be called either during i_loadSettings or under the machine write lock.
7417 * @param fl Flag
7418 * @param fAllowStateModification If state modifications are allowed.
7419 */
7420void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7421{
7422 mData->flModifications |= fl;
7423 if (fAllowStateModification && i_isStateModificationAllowed())
7424 mData->mCurrentStateModified = true;
7425}
7426
7427/**
7428 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7429 * care of the write locking.
7430 *
7431 * @param fModification The flag to add.
7432 * @param fAllowStateModification If state modifications are allowed.
7433 */
7434void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7435{
7436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7437 i_setModified(fModification, fAllowStateModification);
7438}
7439
7440/**
7441 * Saves the registry entry of this machine to the given configuration node.
7442 *
7443 * @param data Machine registry data.
7444 *
7445 * @note locks this object for reading.
7446 */
7447HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7448{
7449 AutoLimitedCaller autoCaller(this);
7450 AssertComRCReturnRC(autoCaller.rc());
7451
7452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7453
7454 data.uuid = mData->mUuid;
7455 data.strSettingsFile = mData->m_strConfigFile;
7456
7457 return S_OK;
7458}
7459
7460/**
7461 * Calculates the absolute path of the given path taking the directory of the
7462 * machine settings file as the current directory.
7463 *
7464 * @param strPath Path to calculate the absolute path for.
7465 * @param aResult Where to put the result (used only on success, can be the
7466 * same Utf8Str instance as passed in @a aPath).
7467 * @return IPRT result.
7468 *
7469 * @note Locks this object for reading.
7470 */
7471int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7472{
7473 AutoCaller autoCaller(this);
7474 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7475
7476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7477
7478 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7479
7480 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7481
7482 strSettingsDir.stripFilename();
7483 char folder[RTPATH_MAX];
7484 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7485 if (RT_SUCCESS(vrc))
7486 aResult = folder;
7487
7488 return vrc;
7489}
7490
7491/**
7492 * Copies strSource to strTarget, making it relative to the machine folder
7493 * if it is a subdirectory thereof, or simply copying it otherwise.
7494 *
7495 * @param strSource Path to evaluate and copy.
7496 * @param strTarget Buffer to receive target path.
7497 *
7498 * @note Locks this object for reading.
7499 */
7500void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7501 Utf8Str &strTarget)
7502{
7503 AutoCaller autoCaller(this);
7504 AssertComRCReturn(autoCaller.rc(), (void)0);
7505
7506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7507
7508 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7509 // use strTarget as a temporary buffer to hold the machine settings dir
7510 strTarget = mData->m_strConfigFileFull;
7511 strTarget.stripFilename();
7512 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7513 {
7514 // is relative: then append what's left
7515 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7516 // for empty paths (only possible for subdirs) use "." to avoid
7517 // triggering default settings for not present config attributes.
7518 if (strTarget.isEmpty())
7519 strTarget = ".";
7520 }
7521 else
7522 // is not relative: then overwrite
7523 strTarget = strSource;
7524}
7525
7526/**
7527 * Returns the full path to the machine's log folder in the
7528 * \a aLogFolder argument.
7529 */
7530void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7531{
7532 AutoCaller autoCaller(this);
7533 AssertComRCReturnVoid(autoCaller.rc());
7534
7535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7536
7537 char szTmp[RTPATH_MAX];
7538 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7539 if (RT_SUCCESS(vrc))
7540 {
7541 if (szTmp[0] && !mUserData.isNull())
7542 {
7543 char szTmp2[RTPATH_MAX];
7544 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7545 if (RT_SUCCESS(vrc))
7546 aLogFolder = Utf8StrFmt("%s%c%s",
7547 szTmp2,
7548 RTPATH_DELIMITER,
7549 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7550 }
7551 else
7552 vrc = VERR_PATH_IS_RELATIVE;
7553 }
7554
7555 if (RT_FAILURE(vrc))
7556 {
7557 // fallback if VBOX_USER_LOGHOME is not set or invalid
7558 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7559 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7560 aLogFolder.append(RTPATH_DELIMITER);
7561 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7562 }
7563}
7564
7565/**
7566 * Returns the full path to the machine's log file for an given index.
7567 */
7568Utf8Str Machine::i_getLogFilename(ULONG idx)
7569{
7570 Utf8Str logFolder;
7571 getLogFolder(logFolder);
7572 Assert(logFolder.length());
7573
7574 Utf8Str log;
7575 if (idx == 0)
7576 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7577#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7578 else if (idx == 1)
7579 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7580 else
7581 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7582#else
7583 else
7584 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7585#endif
7586 return log;
7587}
7588
7589/**
7590 * Returns the full path to the machine's hardened log file.
7591 */
7592Utf8Str Machine::i_getHardeningLogFilename(void)
7593{
7594 Utf8Str strFilename;
7595 getLogFolder(strFilename);
7596 Assert(strFilename.length());
7597 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7598 return strFilename;
7599}
7600
7601
7602/**
7603 * Composes a unique saved state filename based on the current system time. The filename is
7604 * granular to the second so this will work so long as no more than one snapshot is taken on
7605 * a machine per second.
7606 *
7607 * Before version 4.1, we used this formula for saved state files:
7608 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7609 * which no longer works because saved state files can now be shared between the saved state of the
7610 * "saved" machine and an online snapshot, and the following would cause problems:
7611 * 1) save machine
7612 * 2) create online snapshot from that machine state --> reusing saved state file
7613 * 3) save machine again --> filename would be reused, breaking the online snapshot
7614 *
7615 * So instead we now use a timestamp.
7616 *
7617 * @param strStateFilePath
7618 */
7619
7620void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7621{
7622 AutoCaller autoCaller(this);
7623 AssertComRCReturnVoid(autoCaller.rc());
7624
7625 {
7626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7627 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7628 }
7629
7630 RTTIMESPEC ts;
7631 RTTimeNow(&ts);
7632 RTTIME time;
7633 RTTimeExplode(&time, &ts);
7634
7635 strStateFilePath += RTPATH_DELIMITER;
7636 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7637 time.i32Year, time.u8Month, time.u8MonthDay,
7638 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7639}
7640
7641/**
7642 * Returns the full path to the default video capture file.
7643 */
7644void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7645{
7646 AutoCaller autoCaller(this);
7647 AssertComRCReturnVoid(autoCaller.rc());
7648
7649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7650
7651 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7652 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7653 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7654}
7655
7656/**
7657 * Returns whether at least one USB controller is present for the VM.
7658 */
7659bool Machine::i_isUSBControllerPresent()
7660{
7661 AutoCaller autoCaller(this);
7662 AssertComRCReturn(autoCaller.rc(), false);
7663
7664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7665
7666 return (mUSBControllers->size() > 0);
7667}
7668
7669/**
7670 * @note Locks this object for writing, calls the client process
7671 * (inside the lock).
7672 */
7673HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7674 const Utf8Str &strFrontend,
7675 const Utf8Str &strEnvironment,
7676 ProgressProxy *aProgress)
7677{
7678 LogFlowThisFuncEnter();
7679
7680 AssertReturn(aControl, E_FAIL);
7681 AssertReturn(aProgress, E_FAIL);
7682 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7683
7684 AutoCaller autoCaller(this);
7685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7686
7687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7688
7689 if (!mData->mRegistered)
7690 return setError(E_UNEXPECTED,
7691 tr("The machine '%s' is not registered"),
7692 mUserData->s.strName.c_str());
7693
7694 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7695
7696 /* The process started when launching a VM with separate UI/VM processes is always
7697 * the UI process, i.e. needs special handling as it won't claim the session. */
7698 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7699
7700 if (fSeparate)
7701 {
7702 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7703 return setError(VBOX_E_INVALID_OBJECT_STATE,
7704 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7705 mUserData->s.strName.c_str());
7706 }
7707 else
7708 {
7709 if ( mData->mSession.mState == SessionState_Locked
7710 || mData->mSession.mState == SessionState_Spawning
7711 || mData->mSession.mState == SessionState_Unlocking)
7712 return setError(VBOX_E_INVALID_OBJECT_STATE,
7713 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7714 mUserData->s.strName.c_str());
7715
7716 /* may not be busy */
7717 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7718 }
7719
7720 /* get the path to the executable */
7721 char szPath[RTPATH_MAX];
7722 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7723 size_t cchBufLeft = strlen(szPath);
7724 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7725 szPath[cchBufLeft] = 0;
7726 char *pszNamePart = szPath + cchBufLeft;
7727 cchBufLeft = sizeof(szPath) - cchBufLeft;
7728
7729 int vrc = VINF_SUCCESS;
7730 RTPROCESS pid = NIL_RTPROCESS;
7731
7732 RTENV env = RTENV_DEFAULT;
7733
7734 if (!strEnvironment.isEmpty())
7735 {
7736 char *newEnvStr = NULL;
7737
7738 do
7739 {
7740 /* clone the current environment */
7741 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7742 AssertRCBreakStmt(vrc2, vrc = vrc2);
7743
7744 newEnvStr = RTStrDup(strEnvironment.c_str());
7745 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7746
7747 /* put new variables to the environment
7748 * (ignore empty variable names here since RTEnv API
7749 * intentionally doesn't do that) */
7750 char *var = newEnvStr;
7751 for (char *p = newEnvStr; *p; ++p)
7752 {
7753 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7754 {
7755 *p = '\0';
7756 if (*var)
7757 {
7758 char *val = strchr(var, '=');
7759 if (val)
7760 {
7761 *val++ = '\0';
7762 vrc2 = RTEnvSetEx(env, var, val);
7763 }
7764 else
7765 vrc2 = RTEnvUnsetEx(env, var);
7766 if (RT_FAILURE(vrc2))
7767 break;
7768 }
7769 var = p + 1;
7770 }
7771 }
7772 if (RT_SUCCESS(vrc2) && *var)
7773 vrc2 = RTEnvPutEx(env, var);
7774
7775 AssertRCBreakStmt(vrc2, vrc = vrc2);
7776 }
7777 while (0);
7778
7779 if (newEnvStr != NULL)
7780 RTStrFree(newEnvStr);
7781 }
7782
7783 /* Hardening logging */
7784#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7785 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7786 {
7787 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7788 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7789 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7790 {
7791 Utf8Str strStartupLogDir = strHardeningLogFile;
7792 strStartupLogDir.stripFilename();
7793 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7794 file without stripping the file. */
7795 }
7796 strSupHardeningLogArg.append(strHardeningLogFile);
7797
7798 /* Remove legacy log filename to avoid confusion. */
7799 Utf8Str strOldStartupLogFile;
7800 getLogFolder(strOldStartupLogFile);
7801 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7802 RTFileDelete(strOldStartupLogFile.c_str());
7803 }
7804 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7805#else
7806 const char *pszSupHardeningLogArg = NULL;
7807#endif
7808
7809 Utf8Str strCanonicalName;
7810
7811#ifdef VBOX_WITH_QTGUI
7812 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7813 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7814 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7815 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7816 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7817 {
7818 strCanonicalName = "GUI/Qt";
7819# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7820 /* Modify the base path so that we don't need to use ".." below. */
7821 RTPathStripTrailingSlash(szPath);
7822 RTPathStripFilename(szPath);
7823 cchBufLeft = strlen(szPath);
7824 pszNamePart = szPath + cchBufLeft;
7825 cchBufLeft = sizeof(szPath) - cchBufLeft;
7826
7827# define OSX_APP_NAME "VirtualBoxVM"
7828# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7829
7830 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7831 if ( strAppOverride.contains(".")
7832 || strAppOverride.contains("/")
7833 || strAppOverride.contains("\\")
7834 || strAppOverride.contains(":"))
7835 strAppOverride.setNull();
7836 Utf8Str strAppPath;
7837 if (!strAppOverride.isEmpty())
7838 {
7839 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7840 Utf8Str strFullPath(szPath);
7841 strFullPath.append(strAppPath);
7842 /* there is a race, but people using this deserve the failure */
7843 if (!RTFileExists(strFullPath.c_str()))
7844 strAppOverride.setNull();
7845 }
7846 if (strAppOverride.isEmpty())
7847 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7848 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7849 strcpy(pszNamePart, strAppPath.c_str());
7850# else
7851# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7852 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7853# else
7854 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7855# endif
7856 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7857 strcpy(pszNamePart, s_szVirtualBox_exe);
7858# endif
7859
7860 Utf8Str idStr = mData->mUuid.toString();
7861 const char *apszArgs[] =
7862 {
7863 szPath,
7864 "--comment", mUserData->s.strName.c_str(),
7865 "--startvm", idStr.c_str(),
7866 "--no-startvm-errormsgbox",
7867 NULL, /* For "--separate". */
7868 NULL, /* For "--sup-startup-log". */
7869 NULL
7870 };
7871 unsigned iArg = 6;
7872 if (fSeparate)
7873 apszArgs[iArg++] = "--separate";
7874 apszArgs[iArg++] = pszSupHardeningLogArg;
7875
7876 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7877 }
7878#else /* !VBOX_WITH_QTGUI */
7879 if (0)
7880 ;
7881#endif /* VBOX_WITH_QTGUI */
7882
7883 else
7884
7885#ifdef VBOX_WITH_VBOXSDL
7886 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7887 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7888 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7889 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7890 {
7891 strCanonicalName = "GUI/SDL";
7892 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7893 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7894 strcpy(pszNamePart, s_szVBoxSDL_exe);
7895
7896 Utf8Str idStr = mData->mUuid.toString();
7897 const char *apszArgs[] =
7898 {
7899 szPath,
7900 "--comment", mUserData->s.strName.c_str(),
7901 "--startvm", idStr.c_str(),
7902 NULL, /* For "--separate". */
7903 NULL, /* For "--sup-startup-log". */
7904 NULL
7905 };
7906 unsigned iArg = 5;
7907 if (fSeparate)
7908 apszArgs[iArg++] = "--separate";
7909 apszArgs[iArg++] = pszSupHardeningLogArg;
7910
7911 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7912 }
7913#else /* !VBOX_WITH_VBOXSDL */
7914 if (0)
7915 ;
7916#endif /* !VBOX_WITH_VBOXSDL */
7917
7918 else
7919
7920#ifdef VBOX_WITH_HEADLESS
7921 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7922 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7923 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7924 )
7925 {
7926 strCanonicalName = "headless";
7927 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7928 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7929 * and a VM works even if the server has not been installed.
7930 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7931 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7932 * differently in 4.0 and 3.x.
7933 */
7934 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7935 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7936 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7937
7938 Utf8Str idStr = mData->mUuid.toString();
7939 const char *apszArgs[] =
7940 {
7941 szPath,
7942 "--comment", mUserData->s.strName.c_str(),
7943 "--startvm", idStr.c_str(),
7944 "--vrde", "config",
7945 NULL, /* For "--capture". */
7946 NULL, /* For "--sup-startup-log". */
7947 NULL
7948 };
7949 unsigned iArg = 7;
7950 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7951 apszArgs[iArg++] = "--capture";
7952 apszArgs[iArg++] = pszSupHardeningLogArg;
7953
7954# ifdef RT_OS_WINDOWS
7955 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7956# else
7957 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7958# endif
7959 }
7960#else /* !VBOX_WITH_HEADLESS */
7961 if (0)
7962 ;
7963#endif /* !VBOX_WITH_HEADLESS */
7964 else
7965 {
7966 RTEnvDestroy(env);
7967 return setError(E_INVALIDARG,
7968 tr("Invalid frontend name: '%s'"),
7969 strFrontend.c_str());
7970 }
7971
7972 RTEnvDestroy(env);
7973
7974 if (RT_FAILURE(vrc))
7975 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7976 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7977 mUserData->s.strName.c_str(), vrc);
7978
7979 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7980
7981 if (!fSeparate)
7982 {
7983 /*
7984 * Note that we don't release the lock here before calling the client,
7985 * because it doesn't need to call us back if called with a NULL argument.
7986 * Releasing the lock here is dangerous because we didn't prepare the
7987 * launch data yet, but the client we've just started may happen to be
7988 * too fast and call LockMachine() that will fail (because of PID, etc.),
7989 * so that the Machine will never get out of the Spawning session state.
7990 */
7991
7992 /* inform the session that it will be a remote one */
7993 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7994#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7995 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7996#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7997 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7998#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7999 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8000
8001 if (FAILED(rc))
8002 {
8003 /* restore the session state */
8004 mData->mSession.mState = SessionState_Unlocked;
8005 alock.release();
8006 mParent->i_addProcessToReap(pid);
8007 /* The failure may occur w/o any error info (from RPC), so provide one */
8008 return setError(VBOX_E_VM_ERROR,
8009 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8010 }
8011
8012 /* attach launch data to the machine */
8013 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8014 mData->mSession.mRemoteControls.push_back(aControl);
8015 mData->mSession.mProgress = aProgress;
8016 mData->mSession.mPID = pid;
8017 mData->mSession.mState = SessionState_Spawning;
8018 Assert(strCanonicalName.isNotEmpty());
8019 mData->mSession.mName = strCanonicalName;
8020 }
8021 else
8022 {
8023 /* For separate UI process we declare the launch as completed instantly, as the
8024 * actual headless VM start may or may not come. No point in remembering anything
8025 * yet, as what matters for us is when the headless VM gets started. */
8026 aProgress->i_notifyComplete(S_OK);
8027 }
8028
8029 alock.release();
8030 mParent->i_addProcessToReap(pid);
8031
8032 LogFlowThisFuncLeave();
8033 return S_OK;
8034}
8035
8036/**
8037 * Returns @c true if the given session machine instance has an open direct
8038 * session (and optionally also for direct sessions which are closing) and
8039 * returns the session control machine instance if so.
8040 *
8041 * Note that when the method returns @c false, the arguments remain unchanged.
8042 *
8043 * @param aMachine Session machine object.
8044 * @param aControl Direct session control object (optional).
8045 * @param aRequireVM If true then only allow VM sessions.
8046 * @param aAllowClosing If true then additionally a session which is currently
8047 * being closed will also be allowed.
8048 *
8049 * @note locks this object for reading.
8050 */
8051bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8052 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8053 bool aRequireVM /*= false*/,
8054 bool aAllowClosing /*= false*/)
8055{
8056 AutoLimitedCaller autoCaller(this);
8057 AssertComRCReturn(autoCaller.rc(), false);
8058
8059 /* just return false for inaccessible machines */
8060 if (getObjectState().getState() != ObjectState::Ready)
8061 return false;
8062
8063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8064
8065 if ( ( mData->mSession.mState == SessionState_Locked
8066 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8067 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8068 )
8069 {
8070 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8071
8072 aMachine = mData->mSession.mMachine;
8073
8074 if (aControl != NULL)
8075 *aControl = mData->mSession.mDirectControl;
8076
8077 return true;
8078 }
8079
8080 return false;
8081}
8082
8083/**
8084 * Returns @c true if the given machine has an spawning direct session.
8085 *
8086 * @note locks this object for reading.
8087 */
8088bool Machine::i_isSessionSpawning()
8089{
8090 AutoLimitedCaller autoCaller(this);
8091 AssertComRCReturn(autoCaller.rc(), false);
8092
8093 /* just return false for inaccessible machines */
8094 if (getObjectState().getState() != ObjectState::Ready)
8095 return false;
8096
8097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8098
8099 if (mData->mSession.mState == SessionState_Spawning)
8100 return true;
8101
8102 return false;
8103}
8104
8105/**
8106 * Called from the client watcher thread to check for unexpected client process
8107 * death during Session_Spawning state (e.g. before it successfully opened a
8108 * direct session).
8109 *
8110 * On Win32 and on OS/2, this method is called only when we've got the
8111 * direct client's process termination notification, so it always returns @c
8112 * true.
8113 *
8114 * On other platforms, this method returns @c true if the client process is
8115 * terminated and @c false if it's still alive.
8116 *
8117 * @note Locks this object for writing.
8118 */
8119bool Machine::i_checkForSpawnFailure()
8120{
8121 AutoCaller autoCaller(this);
8122 if (!autoCaller.isOk())
8123 {
8124 /* nothing to do */
8125 LogFlowThisFunc(("Already uninitialized!\n"));
8126 return true;
8127 }
8128
8129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8130
8131 if (mData->mSession.mState != SessionState_Spawning)
8132 {
8133 /* nothing to do */
8134 LogFlowThisFunc(("Not spawning any more!\n"));
8135 return true;
8136 }
8137
8138 HRESULT rc = S_OK;
8139
8140 /* PID not yet initialized, skip check. */
8141 if (mData->mSession.mPID == NIL_RTPROCESS)
8142 return false;
8143
8144 RTPROCSTATUS status;
8145 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8146
8147 if (vrc != VERR_PROCESS_RUNNING)
8148 {
8149 Utf8Str strExtraInfo;
8150
8151#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8152 /* If the startup logfile exists and is of non-zero length, tell the
8153 user to look there for more details to encourage them to attach it
8154 when reporting startup issues. */
8155 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8156 uint64_t cbStartupLogFile = 0;
8157 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8158 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8159 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8160#endif
8161
8162 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8163 rc = setError(E_FAIL,
8164 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8165 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8166 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8167 rc = setError(E_FAIL,
8168 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8169 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8170 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8171 rc = setError(E_FAIL,
8172 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8173 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8174 else
8175 rc = setErrorBoth(E_FAIL, vrc,
8176 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8177 i_getName().c_str(), vrc, strExtraInfo.c_str());
8178 }
8179
8180 if (FAILED(rc))
8181 {
8182 /* Close the remote session, remove the remote control from the list
8183 * and reset session state to Closed (@note keep the code in sync with
8184 * the relevant part in LockMachine()). */
8185
8186 Assert(mData->mSession.mRemoteControls.size() == 1);
8187 if (mData->mSession.mRemoteControls.size() == 1)
8188 {
8189 ErrorInfoKeeper eik;
8190 mData->mSession.mRemoteControls.front()->Uninitialize();
8191 }
8192
8193 mData->mSession.mRemoteControls.clear();
8194 mData->mSession.mState = SessionState_Unlocked;
8195
8196 /* finalize the progress after setting the state */
8197 if (!mData->mSession.mProgress.isNull())
8198 {
8199 mData->mSession.mProgress->notifyComplete(rc);
8200 mData->mSession.mProgress.setNull();
8201 }
8202
8203 mData->mSession.mPID = NIL_RTPROCESS;
8204
8205 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8206 return true;
8207 }
8208
8209 return false;
8210}
8211
8212/**
8213 * Checks whether the machine can be registered. If so, commits and saves
8214 * all settings.
8215 *
8216 * @note Must be called from mParent's write lock. Locks this object and
8217 * children for writing.
8218 */
8219HRESULT Machine::i_prepareRegister()
8220{
8221 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8222
8223 AutoLimitedCaller autoCaller(this);
8224 AssertComRCReturnRC(autoCaller.rc());
8225
8226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8227
8228 /* wait for state dependents to drop to zero */
8229 i_ensureNoStateDependencies();
8230
8231 if (!mData->mAccessible)
8232 return setError(VBOX_E_INVALID_OBJECT_STATE,
8233 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8234 mUserData->s.strName.c_str(),
8235 mData->mUuid.toString().c_str());
8236
8237 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8238
8239 if (mData->mRegistered)
8240 return setError(VBOX_E_INVALID_OBJECT_STATE,
8241 tr("The machine '%s' with UUID {%s} is already registered"),
8242 mUserData->s.strName.c_str(),
8243 mData->mUuid.toString().c_str());
8244
8245 HRESULT rc = S_OK;
8246
8247 // Ensure the settings are saved. If we are going to be registered and
8248 // no config file exists yet, create it by calling i_saveSettings() too.
8249 if ( (mData->flModifications)
8250 || (!mData->pMachineConfigFile->fileExists())
8251 )
8252 {
8253 rc = i_saveSettings(NULL);
8254 // no need to check whether VirtualBox.xml needs saving too since
8255 // we can't have a machine XML file rename pending
8256 if (FAILED(rc)) return rc;
8257 }
8258
8259 /* more config checking goes here */
8260
8261 if (SUCCEEDED(rc))
8262 {
8263 /* we may have had implicit modifications we want to fix on success */
8264 i_commit();
8265
8266 mData->mRegistered = true;
8267 }
8268 else
8269 {
8270 /* we may have had implicit modifications we want to cancel on failure*/
8271 i_rollback(false /* aNotify */);
8272 }
8273
8274 return rc;
8275}
8276
8277/**
8278 * Increases the number of objects dependent on the machine state or on the
8279 * registered state. Guarantees that these two states will not change at least
8280 * until #i_releaseStateDependency() is called.
8281 *
8282 * Depending on the @a aDepType value, additional state checks may be made.
8283 * These checks will set extended error info on failure. See
8284 * #i_checkStateDependency() for more info.
8285 *
8286 * If this method returns a failure, the dependency is not added and the caller
8287 * is not allowed to rely on any particular machine state or registration state
8288 * value and may return the failed result code to the upper level.
8289 *
8290 * @param aDepType Dependency type to add.
8291 * @param aState Current machine state (NULL if not interested).
8292 * @param aRegistered Current registered state (NULL if not interested).
8293 *
8294 * @note Locks this object for writing.
8295 */
8296HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8297 MachineState_T *aState /* = NULL */,
8298 BOOL *aRegistered /* = NULL */)
8299{
8300 AutoCaller autoCaller(this);
8301 AssertComRCReturnRC(autoCaller.rc());
8302
8303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8304
8305 HRESULT rc = i_checkStateDependency(aDepType);
8306 if (FAILED(rc)) return rc;
8307
8308 {
8309 if (mData->mMachineStateChangePending != 0)
8310 {
8311 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8312 * drop to zero so don't add more. It may make sense to wait a bit
8313 * and retry before reporting an error (since the pending state
8314 * transition should be really quick) but let's just assert for
8315 * now to see if it ever happens on practice. */
8316
8317 AssertFailed();
8318
8319 return setError(E_ACCESSDENIED,
8320 tr("Machine state change is in progress. Please retry the operation later."));
8321 }
8322
8323 ++mData->mMachineStateDeps;
8324 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8325 }
8326
8327 if (aState)
8328 *aState = mData->mMachineState;
8329 if (aRegistered)
8330 *aRegistered = mData->mRegistered;
8331
8332 return S_OK;
8333}
8334
8335/**
8336 * Decreases the number of objects dependent on the machine state.
8337 * Must always complete the #i_addStateDependency() call after the state
8338 * dependency is no more necessary.
8339 */
8340void Machine::i_releaseStateDependency()
8341{
8342 AutoCaller autoCaller(this);
8343 AssertComRCReturnVoid(autoCaller.rc());
8344
8345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8346
8347 /* releaseStateDependency() w/o addStateDependency()? */
8348 AssertReturnVoid(mData->mMachineStateDeps != 0);
8349 -- mData->mMachineStateDeps;
8350
8351 if (mData->mMachineStateDeps == 0)
8352 {
8353 /* inform i_ensureNoStateDependencies() that there are no more deps */
8354 if (mData->mMachineStateChangePending != 0)
8355 {
8356 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8357 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8358 }
8359 }
8360}
8361
8362Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8363{
8364 /* start with nothing found */
8365 Utf8Str strResult("");
8366
8367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8368
8369 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8370 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8371 // found:
8372 strResult = it->second; // source is a Utf8Str
8373
8374 return strResult;
8375}
8376
8377// protected methods
8378/////////////////////////////////////////////////////////////////////////////
8379
8380/**
8381 * Performs machine state checks based on the @a aDepType value. If a check
8382 * fails, this method will set extended error info, otherwise it will return
8383 * S_OK. It is supposed, that on failure, the caller will immediately return
8384 * the return value of this method to the upper level.
8385 *
8386 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8387 *
8388 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8389 * current state of this machine object allows to change settings of the
8390 * machine (i.e. the machine is not registered, or registered but not running
8391 * and not saved). It is useful to call this method from Machine setters
8392 * before performing any change.
8393 *
8394 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8395 * as for MutableStateDep except that if the machine is saved, S_OK is also
8396 * returned. This is useful in setters which allow changing machine
8397 * properties when it is in the saved state.
8398 *
8399 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8400 * if the current state of this machine object allows to change runtime
8401 * changeable settings of the machine (i.e. the machine is not registered, or
8402 * registered but either running or not running and not saved). It is useful
8403 * to call this method from Machine setters before performing any changes to
8404 * runtime changeable settings.
8405 *
8406 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8407 * the same as for MutableOrRunningStateDep except that if the machine is
8408 * saved, S_OK is also returned. This is useful in setters which allow
8409 * changing runtime and saved state changeable machine properties.
8410 *
8411 * @param aDepType Dependency type to check.
8412 *
8413 * @note Non Machine based classes should use #i_addStateDependency() and
8414 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8415 * template.
8416 *
8417 * @note This method must be called from under this object's read or write
8418 * lock.
8419 */
8420HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8421{
8422 switch (aDepType)
8423 {
8424 case AnyStateDep:
8425 {
8426 break;
8427 }
8428 case MutableStateDep:
8429 {
8430 if ( mData->mRegistered
8431 && ( !i_isSessionMachine()
8432 || ( mData->mMachineState != MachineState_Aborted
8433 && mData->mMachineState != MachineState_Teleported
8434 && mData->mMachineState != MachineState_PoweredOff
8435 )
8436 )
8437 )
8438 return setError(VBOX_E_INVALID_VM_STATE,
8439 tr("The machine is not mutable (state is %s)"),
8440 Global::stringifyMachineState(mData->mMachineState));
8441 break;
8442 }
8443 case MutableOrSavedStateDep:
8444 {
8445 if ( mData->mRegistered
8446 && ( !i_isSessionMachine()
8447 || ( mData->mMachineState != MachineState_Aborted
8448 && mData->mMachineState != MachineState_Teleported
8449 && mData->mMachineState != MachineState_Saved
8450 && mData->mMachineState != MachineState_PoweredOff
8451 )
8452 )
8453 )
8454 return setError(VBOX_E_INVALID_VM_STATE,
8455 tr("The machine is not mutable or saved (state is %s)"),
8456 Global::stringifyMachineState(mData->mMachineState));
8457 break;
8458 }
8459 case MutableOrRunningStateDep:
8460 {
8461 if ( mData->mRegistered
8462 && ( !i_isSessionMachine()
8463 || ( mData->mMachineState != MachineState_Aborted
8464 && mData->mMachineState != MachineState_Teleported
8465 && mData->mMachineState != MachineState_PoweredOff
8466 && !Global::IsOnline(mData->mMachineState)
8467 )
8468 )
8469 )
8470 return setError(VBOX_E_INVALID_VM_STATE,
8471 tr("The machine is not mutable or running (state is %s)"),
8472 Global::stringifyMachineState(mData->mMachineState));
8473 break;
8474 }
8475 case MutableOrSavedOrRunningStateDep:
8476 {
8477 if ( mData->mRegistered
8478 && ( !i_isSessionMachine()
8479 || ( mData->mMachineState != MachineState_Aborted
8480 && mData->mMachineState != MachineState_Teleported
8481 && mData->mMachineState != MachineState_Saved
8482 && mData->mMachineState != MachineState_PoweredOff
8483 && !Global::IsOnline(mData->mMachineState)
8484 )
8485 )
8486 )
8487 return setError(VBOX_E_INVALID_VM_STATE,
8488 tr("The machine is not mutable, saved or running (state is %s)"),
8489 Global::stringifyMachineState(mData->mMachineState));
8490 break;
8491 }
8492 }
8493
8494 return S_OK;
8495}
8496
8497/**
8498 * Helper to initialize all associated child objects and allocate data
8499 * structures.
8500 *
8501 * This method must be called as a part of the object's initialization procedure
8502 * (usually done in the #init() method).
8503 *
8504 * @note Must be called only from #init() or from #i_registeredInit().
8505 */
8506HRESULT Machine::initDataAndChildObjects()
8507{
8508 AutoCaller autoCaller(this);
8509 AssertComRCReturnRC(autoCaller.rc());
8510 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8511 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8512
8513 AssertReturn(!mData->mAccessible, E_FAIL);
8514
8515 /* allocate data structures */
8516 mSSData.allocate();
8517 mUserData.allocate();
8518 mHWData.allocate();
8519 mMediumAttachments.allocate();
8520 mStorageControllers.allocate();
8521 mUSBControllers.allocate();
8522
8523 /* initialize mOSTypeId */
8524 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8525
8526/** @todo r=bird: init() methods never fails, right? Why don't we make them
8527 * return void then! */
8528
8529 /* create associated BIOS settings object */
8530 unconst(mBIOSSettings).createObject();
8531 mBIOSSettings->init(this);
8532
8533 /* create an associated VRDE object (default is disabled) */
8534 unconst(mVRDEServer).createObject();
8535 mVRDEServer->init(this);
8536
8537 /* create associated serial port objects */
8538 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8539 {
8540 unconst(mSerialPorts[slot]).createObject();
8541 mSerialPorts[slot]->init(this, slot);
8542 }
8543
8544 /* create associated parallel port objects */
8545 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8546 {
8547 unconst(mParallelPorts[slot]).createObject();
8548 mParallelPorts[slot]->init(this, slot);
8549 }
8550
8551 /* create the audio adapter object (always present, default is disabled) */
8552 unconst(mAudioAdapter).createObject();
8553 mAudioAdapter->init(this);
8554
8555 /* create the USB device filters object (always present) */
8556 unconst(mUSBDeviceFilters).createObject();
8557 mUSBDeviceFilters->init(this);
8558
8559 /* create associated network adapter objects */
8560 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8561 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8562 {
8563 unconst(mNetworkAdapters[slot]).createObject();
8564 mNetworkAdapters[slot]->init(this, slot);
8565 }
8566
8567 /* create the bandwidth control */
8568 unconst(mBandwidthControl).createObject();
8569 mBandwidthControl->init(this);
8570
8571 return S_OK;
8572}
8573
8574/**
8575 * Helper to uninitialize all associated child objects and to free all data
8576 * structures.
8577 *
8578 * This method must be called as a part of the object's uninitialization
8579 * procedure (usually done in the #uninit() method).
8580 *
8581 * @note Must be called only from #uninit() or from #i_registeredInit().
8582 */
8583void Machine::uninitDataAndChildObjects()
8584{
8585 AutoCaller autoCaller(this);
8586 AssertComRCReturnVoid(autoCaller.rc());
8587 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8588 || getObjectState().getState() == ObjectState::Limited);
8589
8590 /* tell all our other child objects we've been uninitialized */
8591 if (mBandwidthControl)
8592 {
8593 mBandwidthControl->uninit();
8594 unconst(mBandwidthControl).setNull();
8595 }
8596
8597 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8598 {
8599 if (mNetworkAdapters[slot])
8600 {
8601 mNetworkAdapters[slot]->uninit();
8602 unconst(mNetworkAdapters[slot]).setNull();
8603 }
8604 }
8605
8606 if (mUSBDeviceFilters)
8607 {
8608 mUSBDeviceFilters->uninit();
8609 unconst(mUSBDeviceFilters).setNull();
8610 }
8611
8612 if (mAudioAdapter)
8613 {
8614 mAudioAdapter->uninit();
8615 unconst(mAudioAdapter).setNull();
8616 }
8617
8618 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8619 {
8620 if (mParallelPorts[slot])
8621 {
8622 mParallelPorts[slot]->uninit();
8623 unconst(mParallelPorts[slot]).setNull();
8624 }
8625 }
8626
8627 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8628 {
8629 if (mSerialPorts[slot])
8630 {
8631 mSerialPorts[slot]->uninit();
8632 unconst(mSerialPorts[slot]).setNull();
8633 }
8634 }
8635
8636 if (mVRDEServer)
8637 {
8638 mVRDEServer->uninit();
8639 unconst(mVRDEServer).setNull();
8640 }
8641
8642 if (mBIOSSettings)
8643 {
8644 mBIOSSettings->uninit();
8645 unconst(mBIOSSettings).setNull();
8646 }
8647
8648 /* Deassociate media (only when a real Machine or a SnapshotMachine
8649 * instance is uninitialized; SessionMachine instances refer to real
8650 * Machine media). This is necessary for a clean re-initialization of
8651 * the VM after successfully re-checking the accessibility state. Note
8652 * that in case of normal Machine or SnapshotMachine uninitialization (as
8653 * a result of unregistering or deleting the snapshot), outdated media
8654 * attachments will already be uninitialized and deleted, so this
8655 * code will not affect them. */
8656 if ( !mMediumAttachments.isNull()
8657 && !i_isSessionMachine()
8658 )
8659 {
8660 for (MediumAttachmentList::const_iterator
8661 it = mMediumAttachments->begin();
8662 it != mMediumAttachments->end();
8663 ++it)
8664 {
8665 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8666 if (pMedium.isNull())
8667 continue;
8668 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8669 AssertComRC(rc);
8670 }
8671 }
8672
8673 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8674 {
8675 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8676 if (mData->mFirstSnapshot)
8677 {
8678 // snapshots tree is protected by machine write lock; strictly
8679 // this isn't necessary here since we're deleting the entire
8680 // machine, but otherwise we assert in Snapshot::uninit()
8681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8682 mData->mFirstSnapshot->uninit();
8683 mData->mFirstSnapshot.setNull();
8684 }
8685
8686 mData->mCurrentSnapshot.setNull();
8687 }
8688
8689 /* free data structures (the essential mData structure is not freed here
8690 * since it may be still in use) */
8691 mMediumAttachments.free();
8692 mStorageControllers.free();
8693 mUSBControllers.free();
8694 mHWData.free();
8695 mUserData.free();
8696 mSSData.free();
8697}
8698
8699/**
8700 * Returns a pointer to the Machine object for this machine that acts like a
8701 * parent for complex machine data objects such as shared folders, etc.
8702 *
8703 * For primary Machine objects and for SnapshotMachine objects, returns this
8704 * object's pointer itself. For SessionMachine objects, returns the peer
8705 * (primary) machine pointer.
8706 */
8707Machine *Machine::i_getMachine()
8708{
8709 if (i_isSessionMachine())
8710 return (Machine*)mPeer;
8711 return this;
8712}
8713
8714/**
8715 * Makes sure that there are no machine state dependents. If necessary, waits
8716 * for the number of dependents to drop to zero.
8717 *
8718 * Make sure this method is called from under this object's write lock to
8719 * guarantee that no new dependents may be added when this method returns
8720 * control to the caller.
8721 *
8722 * @note Locks this object for writing. The lock will be released while waiting
8723 * (if necessary).
8724 *
8725 * @warning To be used only in methods that change the machine state!
8726 */
8727void Machine::i_ensureNoStateDependencies()
8728{
8729 AssertReturnVoid(isWriteLockOnCurrentThread());
8730
8731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8732
8733 /* Wait for all state dependents if necessary */
8734 if (mData->mMachineStateDeps != 0)
8735 {
8736 /* lazy semaphore creation */
8737 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8738 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8739
8740 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8741 mData->mMachineStateDeps));
8742
8743 ++mData->mMachineStateChangePending;
8744
8745 /* reset the semaphore before waiting, the last dependent will signal
8746 * it */
8747 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8748
8749 alock.release();
8750
8751 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8752
8753 alock.acquire();
8754
8755 -- mData->mMachineStateChangePending;
8756 }
8757}
8758
8759/**
8760 * Changes the machine state and informs callbacks.
8761 *
8762 * This method is not intended to fail so it either returns S_OK or asserts (and
8763 * returns a failure).
8764 *
8765 * @note Locks this object for writing.
8766 */
8767HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8768{
8769 LogFlowThisFuncEnter();
8770 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8771 Assert(aMachineState != MachineState_Null);
8772
8773 AutoCaller autoCaller(this);
8774 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8775
8776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8777
8778 /* wait for state dependents to drop to zero */
8779 i_ensureNoStateDependencies();
8780
8781 MachineState_T const enmOldState = mData->mMachineState;
8782 if (enmOldState != aMachineState)
8783 {
8784 mData->mMachineState = aMachineState;
8785 RTTimeNow(&mData->mLastStateChange);
8786
8787#ifdef VBOX_WITH_DTRACE_R3_MAIN
8788 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8789#endif
8790 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8791 }
8792
8793 LogFlowThisFuncLeave();
8794 return S_OK;
8795}
8796
8797/**
8798 * Searches for a shared folder with the given logical name
8799 * in the collection of shared folders.
8800 *
8801 * @param aName logical name of the shared folder
8802 * @param aSharedFolder where to return the found object
8803 * @param aSetError whether to set the error info if the folder is
8804 * not found
8805 * @return
8806 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8807 *
8808 * @note
8809 * must be called from under the object's lock!
8810 */
8811HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8812 ComObjPtr<SharedFolder> &aSharedFolder,
8813 bool aSetError /* = false */)
8814{
8815 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8816 for (HWData::SharedFolderList::const_iterator
8817 it = mHWData->mSharedFolders.begin();
8818 it != mHWData->mSharedFolders.end();
8819 ++it)
8820 {
8821 SharedFolder *pSF = *it;
8822 AutoCaller autoCaller(pSF);
8823 if (pSF->i_getName() == aName)
8824 {
8825 aSharedFolder = pSF;
8826 rc = S_OK;
8827 break;
8828 }
8829 }
8830
8831 if (aSetError && FAILED(rc))
8832 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8833
8834 return rc;
8835}
8836
8837/**
8838 * Initializes all machine instance data from the given settings structures
8839 * from XML. The exception is the machine UUID which needs special handling
8840 * depending on the caller's use case, so the caller needs to set that herself.
8841 *
8842 * This gets called in several contexts during machine initialization:
8843 *
8844 * -- When machine XML exists on disk already and needs to be loaded into memory,
8845 * for example, from #i_registeredInit() to load all registered machines on
8846 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8847 * attached to the machine should be part of some media registry already.
8848 *
8849 * -- During OVF import, when a machine config has been constructed from an
8850 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8851 * ensure that the media listed as attachments in the config (which have
8852 * been imported from the OVF) receive the correct registry ID.
8853 *
8854 * -- During VM cloning.
8855 *
8856 * @param config Machine settings from XML.
8857 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8858 * for each attached medium in the config.
8859 * @return
8860 */
8861HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8862 const Guid *puuidRegistry)
8863{
8864 // copy name, description, OS type, teleporter, UTC etc.
8865 mUserData->s = config.machineUserData;
8866
8867 // look up the object by Id to check it is valid
8868 ComObjPtr<GuestOSType> pGuestOSType;
8869 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8870 if (!pGuestOSType.isNull())
8871 mUserData->s.strOsType = pGuestOSType->i_id();
8872
8873 // stateFile (optional)
8874 if (config.strStateFile.isEmpty())
8875 mSSData->strStateFilePath.setNull();
8876 else
8877 {
8878 Utf8Str stateFilePathFull(config.strStateFile);
8879 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8880 if (RT_FAILURE(vrc))
8881 return setErrorBoth(E_FAIL, vrc,
8882 tr("Invalid saved state file path '%s' (%Rrc)"),
8883 config.strStateFile.c_str(),
8884 vrc);
8885 mSSData->strStateFilePath = stateFilePathFull;
8886 }
8887
8888 // snapshot folder needs special processing so set it again
8889 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8890 if (FAILED(rc)) return rc;
8891
8892 /* Copy the extra data items (config may or may not be the same as
8893 * mData->pMachineConfigFile) if necessary. When loading the XML files
8894 * from disk they are the same, but not for OVF import. */
8895 if (mData->pMachineConfigFile != &config)
8896 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8897
8898 /* currentStateModified (optional, default is true) */
8899 mData->mCurrentStateModified = config.fCurrentStateModified;
8900
8901 mData->mLastStateChange = config.timeLastStateChange;
8902
8903 /*
8904 * note: all mUserData members must be assigned prior this point because
8905 * we need to commit changes in order to let mUserData be shared by all
8906 * snapshot machine instances.
8907 */
8908 mUserData.commitCopy();
8909
8910 // machine registry, if present (must be loaded before snapshots)
8911 if (config.canHaveOwnMediaRegistry())
8912 {
8913 // determine machine folder
8914 Utf8Str strMachineFolder = i_getSettingsFileFull();
8915 strMachineFolder.stripFilename();
8916 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8917 config.mediaRegistry,
8918 strMachineFolder);
8919 if (FAILED(rc)) return rc;
8920 }
8921
8922 /* Snapshot node (optional) */
8923 size_t cRootSnapshots;
8924 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8925 {
8926 // there must be only one root snapshot
8927 Assert(cRootSnapshots == 1);
8928
8929 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8930
8931 rc = i_loadSnapshot(snap,
8932 config.uuidCurrentSnapshot,
8933 NULL); // no parent == first snapshot
8934 if (FAILED(rc)) return rc;
8935 }
8936
8937 // hardware data
8938 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8939 if (FAILED(rc)) return rc;
8940
8941 /*
8942 * NOTE: the assignment below must be the last thing to do,
8943 * otherwise it will be not possible to change the settings
8944 * somewhere in the code above because all setters will be
8945 * blocked by i_checkStateDependency(MutableStateDep).
8946 */
8947
8948 /* set the machine state to Aborted or Saved when appropriate */
8949 if (config.fAborted)
8950 {
8951 mSSData->strStateFilePath.setNull();
8952
8953 /* no need to use i_setMachineState() during init() */
8954 mData->mMachineState = MachineState_Aborted;
8955 }
8956 else if (!mSSData->strStateFilePath.isEmpty())
8957 {
8958 /* no need to use i_setMachineState() during init() */
8959 mData->mMachineState = MachineState_Saved;
8960 }
8961
8962 // after loading settings, we are no longer different from the XML on disk
8963 mData->flModifications = 0;
8964
8965 return S_OK;
8966}
8967
8968/**
8969 * Recursively loads all snapshots starting from the given.
8970 *
8971 * @param data snapshot settings.
8972 * @param aCurSnapshotId Current snapshot ID from the settings file.
8973 * @param aParentSnapshot Parent snapshot.
8974 */
8975HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8976 const Guid &aCurSnapshotId,
8977 Snapshot *aParentSnapshot)
8978{
8979 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8980 AssertReturn(!i_isSessionMachine(), E_FAIL);
8981
8982 HRESULT rc = S_OK;
8983
8984 Utf8Str strStateFile;
8985 if (!data.strStateFile.isEmpty())
8986 {
8987 /* optional */
8988 strStateFile = data.strStateFile;
8989 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8990 if (RT_FAILURE(vrc))
8991 return setErrorBoth(E_FAIL, vrc,
8992 tr("Invalid saved state file path '%s' (%Rrc)"),
8993 strStateFile.c_str(),
8994 vrc);
8995 }
8996
8997 /* create a snapshot machine object */
8998 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8999 pSnapshotMachine.createObject();
9000 rc = pSnapshotMachine->initFromSettings(this,
9001 data.hardware,
9002 &data.debugging,
9003 &data.autostart,
9004 data.uuid.ref(),
9005 strStateFile);
9006 if (FAILED(rc)) return rc;
9007
9008 /* create a snapshot object */
9009 ComObjPtr<Snapshot> pSnapshot;
9010 pSnapshot.createObject();
9011 /* initialize the snapshot */
9012 rc = pSnapshot->init(mParent, // VirtualBox object
9013 data.uuid,
9014 data.strName,
9015 data.strDescription,
9016 data.timestamp,
9017 pSnapshotMachine,
9018 aParentSnapshot);
9019 if (FAILED(rc)) return rc;
9020
9021 /* memorize the first snapshot if necessary */
9022 if (!mData->mFirstSnapshot)
9023 mData->mFirstSnapshot = pSnapshot;
9024
9025 /* memorize the current snapshot when appropriate */
9026 if ( !mData->mCurrentSnapshot
9027 && pSnapshot->i_getId() == aCurSnapshotId
9028 )
9029 mData->mCurrentSnapshot = pSnapshot;
9030
9031 // now create the children
9032 for (settings::SnapshotsList::const_iterator
9033 it = data.llChildSnapshots.begin();
9034 it != data.llChildSnapshots.end();
9035 ++it)
9036 {
9037 const settings::Snapshot &childData = *it;
9038 // recurse
9039 rc = i_loadSnapshot(childData,
9040 aCurSnapshotId,
9041 pSnapshot); // parent = the one we created above
9042 if (FAILED(rc)) return rc;
9043 }
9044
9045 return rc;
9046}
9047
9048/**
9049 * Loads settings into mHWData.
9050 *
9051 * @param puuidRegistry Registry ID.
9052 * @param puuidSnapshot Snapshot ID
9053 * @param data Reference to the hardware settings.
9054 * @param pDbg Pointer to the debugging settings.
9055 * @param pAutostart Pointer to the autostart settings.
9056 */
9057HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9058 const Guid *puuidSnapshot,
9059 const settings::Hardware &data,
9060 const settings::Debugging *pDbg,
9061 const settings::Autostart *pAutostart)
9062{
9063 AssertReturn(!i_isSessionMachine(), E_FAIL);
9064
9065 HRESULT rc = S_OK;
9066
9067 try
9068 {
9069 ComObjPtr<GuestOSType> pGuestOSType;
9070 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9071
9072 /* The hardware version attribute (optional). */
9073 mHWData->mHWVersion = data.strVersion;
9074 mHWData->mHardwareUUID = data.uuid;
9075
9076 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9077 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9078 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9079 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9080 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9081 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9082 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9083 mHWData->mPAEEnabled = data.fPAE;
9084 mHWData->mLongMode = data.enmLongMode;
9085 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9086 mHWData->mAPIC = data.fAPIC;
9087 mHWData->mX2APIC = data.fX2APIC;
9088 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9089 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9090 mHWData->mSpecCtrl = data.fSpecCtrl;
9091 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9092 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9093 mHWData->mCPUCount = data.cCPUs;
9094 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9095 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9096 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9097 mHWData->mCpuProfile = data.strCpuProfile;
9098
9099 // cpu
9100 if (mHWData->mCPUHotPlugEnabled)
9101 {
9102 for (settings::CpuList::const_iterator
9103 it = data.llCpus.begin();
9104 it != data.llCpus.end();
9105 ++it)
9106 {
9107 const settings::Cpu &cpu = *it;
9108
9109 mHWData->mCPUAttached[cpu.ulId] = true;
9110 }
9111 }
9112
9113 // cpuid leafs
9114 for (settings::CpuIdLeafsList::const_iterator
9115 it = data.llCpuIdLeafs.begin();
9116 it != data.llCpuIdLeafs.end();
9117 ++it)
9118 {
9119 const settings::CpuIdLeaf &rLeaf= *it;
9120 if ( rLeaf.idx < UINT32_C(0x20)
9121 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9122 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9123 mHWData->mCpuIdLeafList.push_back(rLeaf);
9124 /* else: just ignore */
9125 }
9126
9127 mHWData->mMemorySize = data.ulMemorySizeMB;
9128 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9129
9130 // boot order
9131 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9132 {
9133 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9134 if (it == data.mapBootOrder.end())
9135 mHWData->mBootOrder[i] = DeviceType_Null;
9136 else
9137 mHWData->mBootOrder[i] = it->second;
9138 }
9139
9140 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9141 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9142 mHWData->mMonitorCount = data.cMonitors;
9143 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9144 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9145 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9146 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9147 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9148 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9149 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9150 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9151 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9152 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9153 if (!data.strVideoCaptureFile.isEmpty())
9154 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9155 else
9156 mHWData->mVideoCaptureFile.setNull();
9157 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9158 mHWData->mFirmwareType = data.firmwareType;
9159 mHWData->mPointingHIDType = data.pointingHIDType;
9160 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9161 mHWData->mChipsetType = data.chipsetType;
9162 mHWData->mParavirtProvider = data.paravirtProvider;
9163 mHWData->mParavirtDebug = data.strParavirtDebug;
9164 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9165 mHWData->mHPETEnabled = data.fHPETEnabled;
9166
9167 /* VRDEServer */
9168 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9169 if (FAILED(rc)) return rc;
9170
9171 /* BIOS */
9172 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9173 if (FAILED(rc)) return rc;
9174
9175 // Bandwidth control (must come before network adapters)
9176 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9177 if (FAILED(rc)) return rc;
9178
9179 /* Shared folders */
9180 for (settings::USBControllerList::const_iterator
9181 it = data.usbSettings.llUSBControllers.begin();
9182 it != data.usbSettings.llUSBControllers.end();
9183 ++it)
9184 {
9185 const settings::USBController &settingsCtrl = *it;
9186 ComObjPtr<USBController> newCtrl;
9187
9188 newCtrl.createObject();
9189 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9190 mUSBControllers->push_back(newCtrl);
9191 }
9192
9193 /* USB device filters */
9194 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9195 if (FAILED(rc)) return rc;
9196
9197 // network adapters (establish array size first and apply defaults, to
9198 // ensure reading the same settings as we saved, since the list skips
9199 // adapters having defaults)
9200 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9201 size_t oldCount = mNetworkAdapters.size();
9202 if (newCount > oldCount)
9203 {
9204 mNetworkAdapters.resize(newCount);
9205 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9206 {
9207 unconst(mNetworkAdapters[slot]).createObject();
9208 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9209 }
9210 }
9211 else if (newCount < oldCount)
9212 mNetworkAdapters.resize(newCount);
9213 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9214 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9215 for (settings::NetworkAdaptersList::const_iterator
9216 it = data.llNetworkAdapters.begin();
9217 it != data.llNetworkAdapters.end();
9218 ++it)
9219 {
9220 const settings::NetworkAdapter &nic = *it;
9221
9222 /* slot uniqueness is guaranteed by XML Schema */
9223 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9224 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9225 if (FAILED(rc)) return rc;
9226 }
9227
9228 // serial ports (establish defaults first, to ensure reading the same
9229 // settings as we saved, since the list skips ports having defaults)
9230 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9231 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9232 for (settings::SerialPortsList::const_iterator
9233 it = data.llSerialPorts.begin();
9234 it != data.llSerialPorts.end();
9235 ++it)
9236 {
9237 const settings::SerialPort &s = *it;
9238
9239 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9240 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9241 if (FAILED(rc)) return rc;
9242 }
9243
9244 // parallel ports (establish defaults first, to ensure reading the same
9245 // settings as we saved, since the list skips ports having defaults)
9246 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9247 mParallelPorts[i]->i_applyDefaults();
9248 for (settings::ParallelPortsList::const_iterator
9249 it = data.llParallelPorts.begin();
9250 it != data.llParallelPorts.end();
9251 ++it)
9252 {
9253 const settings::ParallelPort &p = *it;
9254
9255 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9256 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9257 if (FAILED(rc)) return rc;
9258 }
9259
9260 /* AudioAdapter */
9261 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9262 if (FAILED(rc)) return rc;
9263
9264 /* storage controllers */
9265 rc = i_loadStorageControllers(data.storage,
9266 puuidRegistry,
9267 puuidSnapshot);
9268 if (FAILED(rc)) return rc;
9269
9270 /* Shared folders */
9271 for (settings::SharedFoldersList::const_iterator
9272 it = data.llSharedFolders.begin();
9273 it != data.llSharedFolders.end();
9274 ++it)
9275 {
9276 const settings::SharedFolder &sf = *it;
9277
9278 ComObjPtr<SharedFolder> sharedFolder;
9279 /* Check for double entries. Not allowed! */
9280 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9281 if (SUCCEEDED(rc))
9282 return setError(VBOX_E_OBJECT_IN_USE,
9283 tr("Shared folder named '%s' already exists"),
9284 sf.strName.c_str());
9285
9286 /* Create the new shared folder. Don't break on error. This will be
9287 * reported when the machine starts. */
9288 sharedFolder.createObject();
9289 rc = sharedFolder->init(i_getMachine(),
9290 sf.strName,
9291 sf.strHostPath,
9292 RT_BOOL(sf.fWritable),
9293 RT_BOOL(sf.fAutoMount),
9294 false /* fFailOnError */);
9295 if (FAILED(rc)) return rc;
9296 mHWData->mSharedFolders.push_back(sharedFolder);
9297 }
9298
9299 // Clipboard
9300 mHWData->mClipboardMode = data.clipboardMode;
9301
9302 // drag'n'drop
9303 mHWData->mDnDMode = data.dndMode;
9304
9305 // guest settings
9306 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9307
9308 // IO settings
9309 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9310 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9311
9312 // Host PCI devices
9313 for (settings::HostPCIDeviceAttachmentList::const_iterator
9314 it = data.pciAttachments.begin();
9315 it != data.pciAttachments.end();
9316 ++it)
9317 {
9318 const settings::HostPCIDeviceAttachment &hpda = *it;
9319 ComObjPtr<PCIDeviceAttachment> pda;
9320
9321 pda.createObject();
9322 pda->i_loadSettings(this, hpda);
9323 mHWData->mPCIDeviceAssignments.push_back(pda);
9324 }
9325
9326 /*
9327 * (The following isn't really real hardware, but it lives in HWData
9328 * for reasons of convenience.)
9329 */
9330
9331#ifdef VBOX_WITH_GUEST_PROPS
9332 /* Guest properties (optional) */
9333
9334 /* Only load transient guest properties for configs which have saved
9335 * state, because there shouldn't be any for powered off VMs. The same
9336 * logic applies for snapshots, as offline snapshots shouldn't have
9337 * any such properties. They confuse the code in various places.
9338 * Note: can't rely on the machine state, as it isn't set yet. */
9339 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9340 /* apologies for the hacky unconst() usage, but this needs hacking
9341 * actually inconsistent settings into consistency, otherwise there
9342 * will be some corner cases where the inconsistency survives
9343 * surprisingly long without getting fixed, especially for snapshots
9344 * as there are no config changes. */
9345 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9346 for (settings::GuestPropertiesList::iterator
9347 it = llGuestProperties.begin();
9348 it != llGuestProperties.end();
9349 /*nothing*/)
9350 {
9351 const settings::GuestProperty &prop = *it;
9352 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9353 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9354 if ( fSkipTransientGuestProperties
9355 && ( fFlags & GUEST_PROP_F_TRANSIENT
9356 || fFlags & GUEST_PROP_F_TRANSRESET))
9357 {
9358 it = llGuestProperties.erase(it);
9359 continue;
9360 }
9361 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9362 mHWData->mGuestProperties[prop.strName] = property;
9363 ++it;
9364 }
9365#endif /* VBOX_WITH_GUEST_PROPS defined */
9366
9367 rc = i_loadDebugging(pDbg);
9368 if (FAILED(rc))
9369 return rc;
9370
9371 mHWData->mAutostart = *pAutostart;
9372
9373 /* default frontend */
9374 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9375 }
9376 catch (std::bad_alloc &)
9377 {
9378 return E_OUTOFMEMORY;
9379 }
9380
9381 AssertComRC(rc);
9382 return rc;
9383}
9384
9385/**
9386 * Called from i_loadHardware() to load the debugging settings of the
9387 * machine.
9388 *
9389 * @param pDbg Pointer to the settings.
9390 */
9391HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9392{
9393 mHWData->mDebugging = *pDbg;
9394 /* no more processing currently required, this will probably change. */
9395 return S_OK;
9396}
9397
9398/**
9399 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9400 *
9401 * @param data storage settings.
9402 * @param puuidRegistry media registry ID to set media to or NULL;
9403 * see Machine::i_loadMachineDataFromSettings()
9404 * @param puuidSnapshot snapshot ID
9405 * @return
9406 */
9407HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9408 const Guid *puuidRegistry,
9409 const Guid *puuidSnapshot)
9410{
9411 AssertReturn(!i_isSessionMachine(), E_FAIL);
9412
9413 HRESULT rc = S_OK;
9414
9415 for (settings::StorageControllersList::const_iterator
9416 it = data.llStorageControllers.begin();
9417 it != data.llStorageControllers.end();
9418 ++it)
9419 {
9420 const settings::StorageController &ctlData = *it;
9421
9422 ComObjPtr<StorageController> pCtl;
9423 /* Try to find one with the name first. */
9424 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9425 if (SUCCEEDED(rc))
9426 return setError(VBOX_E_OBJECT_IN_USE,
9427 tr("Storage controller named '%s' already exists"),
9428 ctlData.strName.c_str());
9429
9430 pCtl.createObject();
9431 rc = pCtl->init(this,
9432 ctlData.strName,
9433 ctlData.storageBus,
9434 ctlData.ulInstance,
9435 ctlData.fBootable);
9436 if (FAILED(rc)) return rc;
9437
9438 mStorageControllers->push_back(pCtl);
9439
9440 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9441 if (FAILED(rc)) return rc;
9442
9443 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9444 if (FAILED(rc)) return rc;
9445
9446 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9447 if (FAILED(rc)) return rc;
9448
9449 /* Load the attached devices now. */
9450 rc = i_loadStorageDevices(pCtl,
9451 ctlData,
9452 puuidRegistry,
9453 puuidSnapshot);
9454 if (FAILED(rc)) return rc;
9455 }
9456
9457 return S_OK;
9458}
9459
9460/**
9461 * Called from i_loadStorageControllers for a controller's devices.
9462 *
9463 * @param aStorageController
9464 * @param data
9465 * @param puuidRegistry media registry ID to set media to or NULL; see
9466 * Machine::i_loadMachineDataFromSettings()
9467 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9468 * @return
9469 */
9470HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9471 const settings::StorageController &data,
9472 const Guid *puuidRegistry,
9473 const Guid *puuidSnapshot)
9474{
9475 HRESULT rc = S_OK;
9476
9477 /* paranoia: detect duplicate attachments */
9478 for (settings::AttachedDevicesList::const_iterator
9479 it = data.llAttachedDevices.begin();
9480 it != data.llAttachedDevices.end();
9481 ++it)
9482 {
9483 const settings::AttachedDevice &ad = *it;
9484
9485 for (settings::AttachedDevicesList::const_iterator it2 = it;
9486 it2 != data.llAttachedDevices.end();
9487 ++it2)
9488 {
9489 if (it == it2)
9490 continue;
9491
9492 const settings::AttachedDevice &ad2 = *it2;
9493
9494 if ( ad.lPort == ad2.lPort
9495 && ad.lDevice == ad2.lDevice)
9496 {
9497 return setError(E_FAIL,
9498 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9499 aStorageController->i_getName().c_str(),
9500 ad.lPort,
9501 ad.lDevice,
9502 mUserData->s.strName.c_str());
9503 }
9504 }
9505 }
9506
9507 for (settings::AttachedDevicesList::const_iterator
9508 it = data.llAttachedDevices.begin();
9509 it != data.llAttachedDevices.end();
9510 ++it)
9511 {
9512 const settings::AttachedDevice &dev = *it;
9513 ComObjPtr<Medium> medium;
9514
9515 switch (dev.deviceType)
9516 {
9517 case DeviceType_Floppy:
9518 case DeviceType_DVD:
9519 if (dev.strHostDriveSrc.isNotEmpty())
9520 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9521 false /* fRefresh */, medium);
9522 else
9523 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9524 dev.uuid,
9525 false /* fRefresh */,
9526 false /* aSetError */,
9527 medium);
9528 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9529 // This is not an error. The host drive or UUID might have vanished, so just go
9530 // ahead without this removeable medium attachment
9531 rc = S_OK;
9532 break;
9533
9534 case DeviceType_HardDisk:
9535 {
9536 /* find a hard disk by UUID */
9537 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9538 if (FAILED(rc))
9539 {
9540 if (i_isSnapshotMachine())
9541 {
9542 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9543 // so the user knows that the bad disk is in a snapshot somewhere
9544 com::ErrorInfo info;
9545 return setError(E_FAIL,
9546 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9547 puuidSnapshot->raw(),
9548 info.getText().raw());
9549 }
9550 else
9551 return rc;
9552 }
9553
9554 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9555
9556 if (medium->i_getType() == MediumType_Immutable)
9557 {
9558 if (i_isSnapshotMachine())
9559 return setError(E_FAIL,
9560 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9561 "of the virtual machine '%s' ('%s')"),
9562 medium->i_getLocationFull().c_str(),
9563 dev.uuid.raw(),
9564 puuidSnapshot->raw(),
9565 mUserData->s.strName.c_str(),
9566 mData->m_strConfigFileFull.c_str());
9567
9568 return setError(E_FAIL,
9569 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9570 medium->i_getLocationFull().c_str(),
9571 dev.uuid.raw(),
9572 mUserData->s.strName.c_str(),
9573 mData->m_strConfigFileFull.c_str());
9574 }
9575
9576 if (medium->i_getType() == MediumType_MultiAttach)
9577 {
9578 if (i_isSnapshotMachine())
9579 return setError(E_FAIL,
9580 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9581 "of the virtual machine '%s' ('%s')"),
9582 medium->i_getLocationFull().c_str(),
9583 dev.uuid.raw(),
9584 puuidSnapshot->raw(),
9585 mUserData->s.strName.c_str(),
9586 mData->m_strConfigFileFull.c_str());
9587
9588 return setError(E_FAIL,
9589 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9590 medium->i_getLocationFull().c_str(),
9591 dev.uuid.raw(),
9592 mUserData->s.strName.c_str(),
9593 mData->m_strConfigFileFull.c_str());
9594 }
9595
9596 if ( !i_isSnapshotMachine()
9597 && medium->i_getChildren().size() != 0
9598 )
9599 return setError(E_FAIL,
9600 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9601 "because it has %d differencing child hard disks"),
9602 medium->i_getLocationFull().c_str(),
9603 dev.uuid.raw(),
9604 mUserData->s.strName.c_str(),
9605 mData->m_strConfigFileFull.c_str(),
9606 medium->i_getChildren().size());
9607
9608 if (i_findAttachment(*mMediumAttachments.data(),
9609 medium))
9610 return setError(E_FAIL,
9611 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9612 medium->i_getLocationFull().c_str(),
9613 dev.uuid.raw(),
9614 mUserData->s.strName.c_str(),
9615 mData->m_strConfigFileFull.c_str());
9616
9617 break;
9618 }
9619
9620 default:
9621 return setError(E_FAIL,
9622 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9623 medium->i_getLocationFull().c_str(),
9624 mUserData->s.strName.c_str(),
9625 mData->m_strConfigFileFull.c_str());
9626 }
9627
9628 if (FAILED(rc))
9629 break;
9630
9631 /* Bandwidth groups are loaded at this point. */
9632 ComObjPtr<BandwidthGroup> pBwGroup;
9633
9634 if (!dev.strBwGroup.isEmpty())
9635 {
9636 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9637 if (FAILED(rc))
9638 return setError(E_FAIL,
9639 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9640 medium->i_getLocationFull().c_str(),
9641 dev.strBwGroup.c_str(),
9642 mUserData->s.strName.c_str(),
9643 mData->m_strConfigFileFull.c_str());
9644 pBwGroup->i_reference();
9645 }
9646
9647 const Utf8Str controllerName = aStorageController->i_getName();
9648 ComObjPtr<MediumAttachment> pAttachment;
9649 pAttachment.createObject();
9650 rc = pAttachment->init(this,
9651 medium,
9652 controllerName,
9653 dev.lPort,
9654 dev.lDevice,
9655 dev.deviceType,
9656 false,
9657 dev.fPassThrough,
9658 dev.fTempEject,
9659 dev.fNonRotational,
9660 dev.fDiscard,
9661 dev.fHotPluggable,
9662 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9663 if (FAILED(rc)) break;
9664
9665 /* associate the medium with this machine and snapshot */
9666 if (!medium.isNull())
9667 {
9668 AutoCaller medCaller(medium);
9669 if (FAILED(medCaller.rc())) return medCaller.rc();
9670 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9671
9672 if (i_isSnapshotMachine())
9673 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9674 else
9675 rc = medium->i_addBackReference(mData->mUuid);
9676 /* If the medium->addBackReference fails it sets an appropriate
9677 * error message, so no need to do any guesswork here. */
9678
9679 if (puuidRegistry)
9680 // caller wants registry ID to be set on all attached media (OVF import case)
9681 medium->i_addRegistry(*puuidRegistry);
9682 }
9683
9684 if (FAILED(rc))
9685 break;
9686
9687 /* back up mMediumAttachments to let registeredInit() properly rollback
9688 * on failure (= limited accessibility) */
9689 i_setModified(IsModified_Storage);
9690 mMediumAttachments.backup();
9691 mMediumAttachments->push_back(pAttachment);
9692 }
9693
9694 return rc;
9695}
9696
9697/**
9698 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9699 *
9700 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9701 * @param aSnapshot where to return the found snapshot
9702 * @param aSetError true to set extended error info on failure
9703 */
9704HRESULT Machine::i_findSnapshotById(const Guid &aId,
9705 ComObjPtr<Snapshot> &aSnapshot,
9706 bool aSetError /* = false */)
9707{
9708 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9709
9710 if (!mData->mFirstSnapshot)
9711 {
9712 if (aSetError)
9713 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9714 return E_FAIL;
9715 }
9716
9717 if (aId.isZero())
9718 aSnapshot = mData->mFirstSnapshot;
9719 else
9720 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9721
9722 if (!aSnapshot)
9723 {
9724 if (aSetError)
9725 return setError(E_FAIL,
9726 tr("Could not find a snapshot with UUID {%s}"),
9727 aId.toString().c_str());
9728 return E_FAIL;
9729 }
9730
9731 return S_OK;
9732}
9733
9734/**
9735 * Returns the snapshot with the given name or fails of no such snapshot.
9736 *
9737 * @param strName snapshot name to find
9738 * @param aSnapshot where to return the found snapshot
9739 * @param aSetError true to set extended error info on failure
9740 */
9741HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9742 ComObjPtr<Snapshot> &aSnapshot,
9743 bool aSetError /* = false */)
9744{
9745 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9746
9747 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9748
9749 if (!mData->mFirstSnapshot)
9750 {
9751 if (aSetError)
9752 return setError(VBOX_E_OBJECT_NOT_FOUND,
9753 tr("This machine does not have any snapshots"));
9754 return VBOX_E_OBJECT_NOT_FOUND;
9755 }
9756
9757 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9758
9759 if (!aSnapshot)
9760 {
9761 if (aSetError)
9762 return setError(VBOX_E_OBJECT_NOT_FOUND,
9763 tr("Could not find a snapshot named '%s'"), strName.c_str());
9764 return VBOX_E_OBJECT_NOT_FOUND;
9765 }
9766
9767 return S_OK;
9768}
9769
9770/**
9771 * Returns a storage controller object with the given name.
9772 *
9773 * @param aName storage controller name to find
9774 * @param aStorageController where to return the found storage controller
9775 * @param aSetError true to set extended error info on failure
9776 */
9777HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9778 ComObjPtr<StorageController> &aStorageController,
9779 bool aSetError /* = false */)
9780{
9781 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9782
9783 for (StorageControllerList::const_iterator
9784 it = mStorageControllers->begin();
9785 it != mStorageControllers->end();
9786 ++it)
9787 {
9788 if ((*it)->i_getName() == aName)
9789 {
9790 aStorageController = (*it);
9791 return S_OK;
9792 }
9793 }
9794
9795 if (aSetError)
9796 return setError(VBOX_E_OBJECT_NOT_FOUND,
9797 tr("Could not find a storage controller named '%s'"),
9798 aName.c_str());
9799 return VBOX_E_OBJECT_NOT_FOUND;
9800}
9801
9802/**
9803 * Returns a USB controller object with the given name.
9804 *
9805 * @param aName USB controller name to find
9806 * @param aUSBController where to return the found USB controller
9807 * @param aSetError true to set extended error info on failure
9808 */
9809HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9810 ComObjPtr<USBController> &aUSBController,
9811 bool aSetError /* = false */)
9812{
9813 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9814
9815 for (USBControllerList::const_iterator
9816 it = mUSBControllers->begin();
9817 it != mUSBControllers->end();
9818 ++it)
9819 {
9820 if ((*it)->i_getName() == aName)
9821 {
9822 aUSBController = (*it);
9823 return S_OK;
9824 }
9825 }
9826
9827 if (aSetError)
9828 return setError(VBOX_E_OBJECT_NOT_FOUND,
9829 tr("Could not find a storage controller named '%s'"),
9830 aName.c_str());
9831 return VBOX_E_OBJECT_NOT_FOUND;
9832}
9833
9834/**
9835 * Returns the number of USB controller instance of the given type.
9836 *
9837 * @param enmType USB controller type.
9838 */
9839ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9840{
9841 ULONG cCtrls = 0;
9842
9843 for (USBControllerList::const_iterator
9844 it = mUSBControllers->begin();
9845 it != mUSBControllers->end();
9846 ++it)
9847 {
9848 if ((*it)->i_getControllerType() == enmType)
9849 cCtrls++;
9850 }
9851
9852 return cCtrls;
9853}
9854
9855HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9856 MediumAttachmentList &atts)
9857{
9858 AutoCaller autoCaller(this);
9859 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9860
9861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9862
9863 for (MediumAttachmentList::const_iterator
9864 it = mMediumAttachments->begin();
9865 it != mMediumAttachments->end();
9866 ++it)
9867 {
9868 const ComObjPtr<MediumAttachment> &pAtt = *it;
9869 // should never happen, but deal with NULL pointers in the list.
9870 AssertContinue(!pAtt.isNull());
9871
9872 // getControllerName() needs caller+read lock
9873 AutoCaller autoAttCaller(pAtt);
9874 if (FAILED(autoAttCaller.rc()))
9875 {
9876 atts.clear();
9877 return autoAttCaller.rc();
9878 }
9879 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9880
9881 if (pAtt->i_getControllerName() == aName)
9882 atts.push_back(pAtt);
9883 }
9884
9885 return S_OK;
9886}
9887
9888
9889/**
9890 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9891 * file if the machine name was changed and about creating a new settings file
9892 * if this is a new machine.
9893 *
9894 * @note Must be never called directly but only from #saveSettings().
9895 */
9896HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9897{
9898 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9899
9900 HRESULT rc = S_OK;
9901
9902 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9903
9904 /// @todo need to handle primary group change, too
9905
9906 /* attempt to rename the settings file if machine name is changed */
9907 if ( mUserData->s.fNameSync
9908 && mUserData.isBackedUp()
9909 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9910 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9911 )
9912 {
9913 bool dirRenamed = false;
9914 bool fileRenamed = false;
9915
9916 Utf8Str configFile, newConfigFile;
9917 Utf8Str configFilePrev, newConfigFilePrev;
9918 Utf8Str configDir, newConfigDir;
9919
9920 do
9921 {
9922 int vrc = VINF_SUCCESS;
9923
9924 Utf8Str name = mUserData.backedUpData()->s.strName;
9925 Utf8Str newName = mUserData->s.strName;
9926 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9927 if (group == "/")
9928 group.setNull();
9929 Utf8Str newGroup = mUserData->s.llGroups.front();
9930 if (newGroup == "/")
9931 newGroup.setNull();
9932
9933 configFile = mData->m_strConfigFileFull;
9934
9935 /* first, rename the directory if it matches the group and machine name */
9936 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9937 group.c_str(), RTPATH_DELIMITER, name.c_str());
9938 /** @todo hack, make somehow use of ComposeMachineFilename */
9939 if (mUserData->s.fDirectoryIncludesUUID)
9940 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9941 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9942 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9943 /** @todo hack, make somehow use of ComposeMachineFilename */
9944 if (mUserData->s.fDirectoryIncludesUUID)
9945 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9946 configDir = configFile;
9947 configDir.stripFilename();
9948 newConfigDir = configDir;
9949 if ( configDir.length() >= groupPlusName.length()
9950 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9951 groupPlusName.c_str()))
9952 {
9953 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9954 Utf8Str newConfigBaseDir(newConfigDir);
9955 newConfigDir.append(newGroupPlusName);
9956 /* consistency: use \ if appropriate on the platform */
9957 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9958 /* new dir and old dir cannot be equal here because of 'if'
9959 * above and because name != newName */
9960 Assert(configDir != newConfigDir);
9961 if (!fSettingsFileIsNew)
9962 {
9963 /* perform real rename only if the machine is not new */
9964 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9965 if ( vrc == VERR_FILE_NOT_FOUND
9966 || vrc == VERR_PATH_NOT_FOUND)
9967 {
9968 /* create the parent directory, then retry renaming */
9969 Utf8Str parent(newConfigDir);
9970 parent.stripFilename();
9971 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9972 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9973 }
9974 if (RT_FAILURE(vrc))
9975 {
9976 rc = setErrorBoth(E_FAIL, vrc,
9977 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9978 configDir.c_str(),
9979 newConfigDir.c_str(),
9980 vrc);
9981 break;
9982 }
9983 /* delete subdirectories which are no longer needed */
9984 Utf8Str dir(configDir);
9985 dir.stripFilename();
9986 while (dir != newConfigBaseDir && dir != ".")
9987 {
9988 vrc = RTDirRemove(dir.c_str());
9989 if (RT_FAILURE(vrc))
9990 break;
9991 dir.stripFilename();
9992 }
9993 dirRenamed = true;
9994 }
9995 }
9996
9997 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9998 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9999
10000 /* then try to rename the settings file itself */
10001 if (newConfigFile != configFile)
10002 {
10003 /* get the path to old settings file in renamed directory */
10004 configFile = Utf8StrFmt("%s%c%s",
10005 newConfigDir.c_str(),
10006 RTPATH_DELIMITER,
10007 RTPathFilename(configFile.c_str()));
10008 if (!fSettingsFileIsNew)
10009 {
10010 /* perform real rename only if the machine is not new */
10011 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10012 if (RT_FAILURE(vrc))
10013 {
10014 rc = setErrorBoth(E_FAIL, vrc,
10015 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10016 configFile.c_str(),
10017 newConfigFile.c_str(),
10018 vrc);
10019 break;
10020 }
10021 fileRenamed = true;
10022 configFilePrev = configFile;
10023 configFilePrev += "-prev";
10024 newConfigFilePrev = newConfigFile;
10025 newConfigFilePrev += "-prev";
10026 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10027 }
10028 }
10029
10030 // update m_strConfigFileFull amd mConfigFile
10031 mData->m_strConfigFileFull = newConfigFile;
10032 // compute the relative path too
10033 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10034
10035 // store the old and new so that VirtualBox::i_saveSettings() can update
10036 // the media registry
10037 if ( mData->mRegistered
10038 && (configDir != newConfigDir || configFile != newConfigFile))
10039 {
10040 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10041
10042 if (pfNeedsGlobalSaveSettings)
10043 *pfNeedsGlobalSaveSettings = true;
10044 }
10045
10046 // in the saved state file path, replace the old directory with the new directory
10047 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10048 {
10049 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10050 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10051 }
10052
10053 // and do the same thing for the saved state file paths of all the online snapshots
10054 if (mData->mFirstSnapshot)
10055 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10056 newConfigDir.c_str());
10057 }
10058 while (0);
10059
10060 if (FAILED(rc))
10061 {
10062 /* silently try to rename everything back */
10063 if (fileRenamed)
10064 {
10065 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10066 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10067 }
10068 if (dirRenamed)
10069 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10070 }
10071
10072 if (FAILED(rc)) return rc;
10073 }
10074
10075 if (fSettingsFileIsNew)
10076 {
10077 /* create a virgin config file */
10078 int vrc = VINF_SUCCESS;
10079
10080 /* ensure the settings directory exists */
10081 Utf8Str path(mData->m_strConfigFileFull);
10082 path.stripFilename();
10083 if (!RTDirExists(path.c_str()))
10084 {
10085 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10086 if (RT_FAILURE(vrc))
10087 {
10088 return setErrorBoth(E_FAIL, vrc,
10089 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10090 path.c_str(),
10091 vrc);
10092 }
10093 }
10094
10095 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10096 path = Utf8Str(mData->m_strConfigFileFull);
10097 RTFILE f = NIL_RTFILE;
10098 vrc = RTFileOpen(&f, path.c_str(),
10099 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10100 if (RT_FAILURE(vrc))
10101 return setErrorBoth(E_FAIL, vrc,
10102 tr("Could not create the settings file '%s' (%Rrc)"),
10103 path.c_str(),
10104 vrc);
10105 RTFileClose(f);
10106 }
10107
10108 return rc;
10109}
10110
10111/**
10112 * Saves and commits machine data, user data and hardware data.
10113 *
10114 * Note that on failure, the data remains uncommitted.
10115 *
10116 * @a aFlags may combine the following flags:
10117 *
10118 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10119 * Used when saving settings after an operation that makes them 100%
10120 * correspond to the settings from the current snapshot.
10121 * - SaveS_Force: settings will be saved without doing a deep compare of the
10122 * settings structures. This is used when this is called because snapshots
10123 * have changed to avoid the overhead of the deep compare.
10124 *
10125 * @note Must be called from under this object's write lock. Locks children for
10126 * writing.
10127 *
10128 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10129 * initialized to false and that will be set to true by this function if
10130 * the caller must invoke VirtualBox::i_saveSettings() because the global
10131 * settings have changed. This will happen if a machine rename has been
10132 * saved and the global machine and media registries will therefore need
10133 * updating.
10134 * @param aFlags Flags.
10135 */
10136HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10137 int aFlags /*= 0*/)
10138{
10139 LogFlowThisFuncEnter();
10140
10141 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10142
10143 /* make sure child objects are unable to modify the settings while we are
10144 * saving them */
10145 i_ensureNoStateDependencies();
10146
10147 AssertReturn(!i_isSnapshotMachine(),
10148 E_FAIL);
10149
10150 HRESULT rc = S_OK;
10151 bool fNeedsWrite = false;
10152
10153 /* First, prepare to save settings. It will care about renaming the
10154 * settings directory and file if the machine name was changed and about
10155 * creating a new settings file if this is a new machine. */
10156 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10157 if (FAILED(rc)) return rc;
10158
10159 // keep a pointer to the current settings structures
10160 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10161 settings::MachineConfigFile *pNewConfig = NULL;
10162
10163 try
10164 {
10165 // make a fresh one to have everyone write stuff into
10166 pNewConfig = new settings::MachineConfigFile(NULL);
10167 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10168
10169 // now go and copy all the settings data from COM to the settings structures
10170 // (this calls i_saveSettings() on all the COM objects in the machine)
10171 i_copyMachineDataToSettings(*pNewConfig);
10172
10173 if (aFlags & SaveS_ResetCurStateModified)
10174 {
10175 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10176 mData->mCurrentStateModified = FALSE;
10177 fNeedsWrite = true; // always, no need to compare
10178 }
10179 else if (aFlags & SaveS_Force)
10180 {
10181 fNeedsWrite = true; // always, no need to compare
10182 }
10183 else
10184 {
10185 if (!mData->mCurrentStateModified)
10186 {
10187 // do a deep compare of the settings that we just saved with the settings
10188 // previously stored in the config file; this invokes MachineConfigFile::operator==
10189 // which does a deep compare of all the settings, which is expensive but less expensive
10190 // than writing out XML in vain
10191 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10192
10193 // could still be modified if any settings changed
10194 mData->mCurrentStateModified = fAnySettingsChanged;
10195
10196 fNeedsWrite = fAnySettingsChanged;
10197 }
10198 else
10199 fNeedsWrite = true;
10200 }
10201
10202 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10203
10204 if (fNeedsWrite)
10205 // now spit it all out!
10206 pNewConfig->write(mData->m_strConfigFileFull);
10207
10208 mData->pMachineConfigFile = pNewConfig;
10209 delete pOldConfig;
10210 i_commit();
10211
10212 // after saving settings, we are no longer different from the XML on disk
10213 mData->flModifications = 0;
10214 }
10215 catch (HRESULT err)
10216 {
10217 // we assume that error info is set by the thrower
10218 rc = err;
10219
10220 // restore old config
10221 delete pNewConfig;
10222 mData->pMachineConfigFile = pOldConfig;
10223 }
10224 catch (...)
10225 {
10226 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10227 }
10228
10229 if (fNeedsWrite)
10230 {
10231 /* Fire the data change event, even on failure (since we've already
10232 * committed all data). This is done only for SessionMachines because
10233 * mutable Machine instances are always not registered (i.e. private
10234 * to the client process that creates them) and thus don't need to
10235 * inform callbacks. */
10236 if (i_isSessionMachine())
10237 mParent->i_onMachineDataChange(mData->mUuid);
10238 }
10239
10240 LogFlowThisFunc(("rc=%08X\n", rc));
10241 LogFlowThisFuncLeave();
10242 return rc;
10243}
10244
10245/**
10246 * Implementation for saving the machine settings into the given
10247 * settings::MachineConfigFile instance. This copies machine extradata
10248 * from the previous machine config file in the instance data, if any.
10249 *
10250 * This gets called from two locations:
10251 *
10252 * -- Machine::i_saveSettings(), during the regular XML writing;
10253 *
10254 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10255 * exported to OVF and we write the VirtualBox proprietary XML
10256 * into a <vbox:Machine> tag.
10257 *
10258 * This routine fills all the fields in there, including snapshots, *except*
10259 * for the following:
10260 *
10261 * -- fCurrentStateModified. There is some special logic associated with that.
10262 *
10263 * The caller can then call MachineConfigFile::write() or do something else
10264 * with it.
10265 *
10266 * Caller must hold the machine lock!
10267 *
10268 * This throws XML errors and HRESULT, so the caller must have a catch block!
10269 */
10270void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10271{
10272 // deep copy extradata, being extra careful with self assignment (the STL
10273 // map assignment on Mac OS X clang based Xcode isn't checking)
10274 if (&config != mData->pMachineConfigFile)
10275 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10276
10277 config.uuid = mData->mUuid;
10278
10279 // copy name, description, OS type, teleport, UTC etc.
10280 config.machineUserData = mUserData->s;
10281
10282 if ( mData->mMachineState == MachineState_Saved
10283 || mData->mMachineState == MachineState_Restoring
10284 // when doing certain snapshot operations we may or may not have
10285 // a saved state in the current state, so keep everything as is
10286 || ( ( mData->mMachineState == MachineState_Snapshotting
10287 || mData->mMachineState == MachineState_DeletingSnapshot
10288 || mData->mMachineState == MachineState_RestoringSnapshot)
10289 && (!mSSData->strStateFilePath.isEmpty())
10290 )
10291 )
10292 {
10293 Assert(!mSSData->strStateFilePath.isEmpty());
10294 /* try to make the file name relative to the settings file dir */
10295 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10296 }
10297 else
10298 {
10299 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10300 config.strStateFile.setNull();
10301 }
10302
10303 if (mData->mCurrentSnapshot)
10304 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10305 else
10306 config.uuidCurrentSnapshot.clear();
10307
10308 config.timeLastStateChange = mData->mLastStateChange;
10309 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10310 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10311
10312 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10313 if (FAILED(rc)) throw rc;
10314
10315 // save machine's media registry if this is VirtualBox 4.0 or later
10316 if (config.canHaveOwnMediaRegistry())
10317 {
10318 // determine machine folder
10319 Utf8Str strMachineFolder = i_getSettingsFileFull();
10320 strMachineFolder.stripFilename();
10321 mParent->i_saveMediaRegistry(config.mediaRegistry,
10322 i_getId(), // only media with registry ID == machine UUID
10323 strMachineFolder);
10324 // this throws HRESULT
10325 }
10326
10327 // save snapshots
10328 rc = i_saveAllSnapshots(config);
10329 if (FAILED(rc)) throw rc;
10330}
10331
10332/**
10333 * Saves all snapshots of the machine into the given machine config file. Called
10334 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10335 * @param config
10336 * @return
10337 */
10338HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10339{
10340 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10341
10342 HRESULT rc = S_OK;
10343
10344 try
10345 {
10346 config.llFirstSnapshot.clear();
10347
10348 if (mData->mFirstSnapshot)
10349 {
10350 // the settings use a list for "the first snapshot"
10351 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10352
10353 // get reference to the snapshot on the list and work on that
10354 // element straight in the list to avoid excessive copying later
10355 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10356 if (FAILED(rc)) throw rc;
10357 }
10358
10359// if (mType == IsSessionMachine)
10360// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10361
10362 }
10363 catch (HRESULT err)
10364 {
10365 /* we assume that error info is set by the thrower */
10366 rc = err;
10367 }
10368 catch (...)
10369 {
10370 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10371 }
10372
10373 return rc;
10374}
10375
10376/**
10377 * Saves the VM hardware configuration. It is assumed that the
10378 * given node is empty.
10379 *
10380 * @param data Reference to the settings object for the hardware config.
10381 * @param pDbg Pointer to the settings object for the debugging config
10382 * which happens to live in mHWData.
10383 * @param pAutostart Pointer to the settings object for the autostart config
10384 * which happens to live in mHWData.
10385 */
10386HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10387 settings::Autostart *pAutostart)
10388{
10389 HRESULT rc = S_OK;
10390
10391 try
10392 {
10393 /* The hardware version attribute (optional).
10394 Automatically upgrade from 1 to current default hardware version
10395 when there is no saved state. (ugly!) */
10396 if ( mHWData->mHWVersion == "1"
10397 && mSSData->strStateFilePath.isEmpty()
10398 )
10399 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10400
10401 data.strVersion = mHWData->mHWVersion;
10402 data.uuid = mHWData->mHardwareUUID;
10403
10404 // CPU
10405 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10406 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10407 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10408 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10409 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10410 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10411 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10412 data.fPAE = !!mHWData->mPAEEnabled;
10413 data.enmLongMode = mHWData->mLongMode;
10414 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10415 data.fAPIC = !!mHWData->mAPIC;
10416 data.fX2APIC = !!mHWData->mX2APIC;
10417 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10418 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10419 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10420 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10421 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10422 data.cCPUs = mHWData->mCPUCount;
10423 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10424 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10425 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10426 data.strCpuProfile = mHWData->mCpuProfile;
10427
10428 data.llCpus.clear();
10429 if (data.fCpuHotPlug)
10430 {
10431 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10432 {
10433 if (mHWData->mCPUAttached[idx])
10434 {
10435 settings::Cpu cpu;
10436 cpu.ulId = idx;
10437 data.llCpus.push_back(cpu);
10438 }
10439 }
10440 }
10441
10442 /* Standard and Extended CPUID leafs. */
10443 data.llCpuIdLeafs.clear();
10444 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10445
10446 // memory
10447 data.ulMemorySizeMB = mHWData->mMemorySize;
10448 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10449
10450 // firmware
10451 data.firmwareType = mHWData->mFirmwareType;
10452
10453 // HID
10454 data.pointingHIDType = mHWData->mPointingHIDType;
10455 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10456
10457 // chipset
10458 data.chipsetType = mHWData->mChipsetType;
10459
10460 // paravirt
10461 data.paravirtProvider = mHWData->mParavirtProvider;
10462 data.strParavirtDebug = mHWData->mParavirtDebug;
10463
10464 // emulated USB card reader
10465 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10466
10467 // HPET
10468 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10469
10470 // boot order
10471 data.mapBootOrder.clear();
10472 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10473 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10474
10475 // display
10476 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10477 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10478 data.cMonitors = mHWData->mMonitorCount;
10479 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10480 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10481 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10482 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10483 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10484 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10485 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10486 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10487 {
10488 if (mHWData->maVideoCaptureScreens[i])
10489 ASMBitSet(&data.u64VideoCaptureScreens, i);
10490 else
10491 ASMBitClear(&data.u64VideoCaptureScreens, i);
10492 }
10493 /* store relative video capture file if possible */
10494 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10495 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10496
10497 /* VRDEServer settings (optional) */
10498 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10499 if (FAILED(rc)) throw rc;
10500
10501 /* BIOS (required) */
10502 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10503 if (FAILED(rc)) throw rc;
10504
10505 /* USB Controller (required) */
10506 data.usbSettings.llUSBControllers.clear();
10507 for (USBControllerList::const_iterator
10508 it = mUSBControllers->begin();
10509 it != mUSBControllers->end();
10510 ++it)
10511 {
10512 ComObjPtr<USBController> ctrl = *it;
10513 settings::USBController settingsCtrl;
10514
10515 settingsCtrl.strName = ctrl->i_getName();
10516 settingsCtrl.enmType = ctrl->i_getControllerType();
10517
10518 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10519 }
10520
10521 /* USB device filters (required) */
10522 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10523 if (FAILED(rc)) throw rc;
10524
10525 /* Network adapters (required) */
10526 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10527 data.llNetworkAdapters.clear();
10528 /* Write out only the nominal number of network adapters for this
10529 * chipset type. Since Machine::commit() hasn't been called there
10530 * may be extra NIC settings in the vector. */
10531 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10532 {
10533 settings::NetworkAdapter nic;
10534 nic.ulSlot = (uint32_t)slot;
10535 /* paranoia check... must not be NULL, but must not crash either. */
10536 if (mNetworkAdapters[slot])
10537 {
10538 if (mNetworkAdapters[slot]->i_hasDefaults())
10539 continue;
10540
10541 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10542 if (FAILED(rc)) throw rc;
10543
10544 data.llNetworkAdapters.push_back(nic);
10545 }
10546 }
10547
10548 /* Serial ports */
10549 data.llSerialPorts.clear();
10550 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10551 {
10552 if (mSerialPorts[slot]->i_hasDefaults())
10553 continue;
10554
10555 settings::SerialPort s;
10556 s.ulSlot = slot;
10557 rc = mSerialPorts[slot]->i_saveSettings(s);
10558 if (FAILED(rc)) return rc;
10559
10560 data.llSerialPorts.push_back(s);
10561 }
10562
10563 /* Parallel ports */
10564 data.llParallelPorts.clear();
10565 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10566 {
10567 if (mParallelPorts[slot]->i_hasDefaults())
10568 continue;
10569
10570 settings::ParallelPort p;
10571 p.ulSlot = slot;
10572 rc = mParallelPorts[slot]->i_saveSettings(p);
10573 if (FAILED(rc)) return rc;
10574
10575 data.llParallelPorts.push_back(p);
10576 }
10577
10578 /* Audio adapter */
10579 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10580 if (FAILED(rc)) return rc;
10581
10582 rc = i_saveStorageControllers(data.storage);
10583 if (FAILED(rc)) return rc;
10584
10585 /* Shared folders */
10586 data.llSharedFolders.clear();
10587 for (HWData::SharedFolderList::const_iterator
10588 it = mHWData->mSharedFolders.begin();
10589 it != mHWData->mSharedFolders.end();
10590 ++it)
10591 {
10592 SharedFolder *pSF = *it;
10593 AutoCaller sfCaller(pSF);
10594 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10595 settings::SharedFolder sf;
10596 sf.strName = pSF->i_getName();
10597 sf.strHostPath = pSF->i_getHostPath();
10598 sf.fWritable = !!pSF->i_isWritable();
10599 sf.fAutoMount = !!pSF->i_isAutoMounted();
10600
10601 data.llSharedFolders.push_back(sf);
10602 }
10603
10604 // clipboard
10605 data.clipboardMode = mHWData->mClipboardMode;
10606
10607 // drag'n'drop
10608 data.dndMode = mHWData->mDnDMode;
10609
10610 /* Guest */
10611 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10612
10613 // IO settings
10614 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10615 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10616
10617 /* BandwidthControl (required) */
10618 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10619 if (FAILED(rc)) throw rc;
10620
10621 /* Host PCI devices */
10622 data.pciAttachments.clear();
10623 for (HWData::PCIDeviceAssignmentList::const_iterator
10624 it = mHWData->mPCIDeviceAssignments.begin();
10625 it != mHWData->mPCIDeviceAssignments.end();
10626 ++it)
10627 {
10628 ComObjPtr<PCIDeviceAttachment> pda = *it;
10629 settings::HostPCIDeviceAttachment hpda;
10630
10631 rc = pda->i_saveSettings(hpda);
10632 if (FAILED(rc)) throw rc;
10633
10634 data.pciAttachments.push_back(hpda);
10635 }
10636
10637 // guest properties
10638 data.llGuestProperties.clear();
10639#ifdef VBOX_WITH_GUEST_PROPS
10640 for (HWData::GuestPropertyMap::const_iterator
10641 it = mHWData->mGuestProperties.begin();
10642 it != mHWData->mGuestProperties.end();
10643 ++it)
10644 {
10645 HWData::GuestProperty property = it->second;
10646
10647 /* Remove transient guest properties at shutdown unless we
10648 * are saving state. Note that restoring snapshot intentionally
10649 * keeps them, they will be removed if appropriate once the final
10650 * machine state is set (as crashes etc. need to work). */
10651 if ( ( mData->mMachineState == MachineState_PoweredOff
10652 || mData->mMachineState == MachineState_Aborted
10653 || mData->mMachineState == MachineState_Teleported)
10654 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10655 continue;
10656 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10657 prop.strName = it->first;
10658 prop.strValue = property.strValue;
10659 prop.timestamp = property.mTimestamp;
10660 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10661 GuestPropWriteFlags(property.mFlags, szFlags);
10662 prop.strFlags = szFlags;
10663
10664 data.llGuestProperties.push_back(prop);
10665 }
10666
10667 /* I presume this doesn't require a backup(). */
10668 mData->mGuestPropertiesModified = FALSE;
10669#endif /* VBOX_WITH_GUEST_PROPS defined */
10670
10671 *pDbg = mHWData->mDebugging;
10672 *pAutostart = mHWData->mAutostart;
10673
10674 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10675 }
10676 catch (std::bad_alloc &)
10677 {
10678 return E_OUTOFMEMORY;
10679 }
10680
10681 AssertComRC(rc);
10682 return rc;
10683}
10684
10685/**
10686 * Saves the storage controller configuration.
10687 *
10688 * @param data storage settings.
10689 */
10690HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10691{
10692 data.llStorageControllers.clear();
10693
10694 for (StorageControllerList::const_iterator
10695 it = mStorageControllers->begin();
10696 it != mStorageControllers->end();
10697 ++it)
10698 {
10699 HRESULT rc;
10700 ComObjPtr<StorageController> pCtl = *it;
10701
10702 settings::StorageController ctl;
10703 ctl.strName = pCtl->i_getName();
10704 ctl.controllerType = pCtl->i_getControllerType();
10705 ctl.storageBus = pCtl->i_getStorageBus();
10706 ctl.ulInstance = pCtl->i_getInstance();
10707 ctl.fBootable = pCtl->i_getBootable();
10708
10709 /* Save the port count. */
10710 ULONG portCount;
10711 rc = pCtl->COMGETTER(PortCount)(&portCount);
10712 ComAssertComRCRet(rc, rc);
10713 ctl.ulPortCount = portCount;
10714
10715 /* Save fUseHostIOCache */
10716 BOOL fUseHostIOCache;
10717 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10718 ComAssertComRCRet(rc, rc);
10719 ctl.fUseHostIOCache = !!fUseHostIOCache;
10720
10721 /* save the devices now. */
10722 rc = i_saveStorageDevices(pCtl, ctl);
10723 ComAssertComRCRet(rc, rc);
10724
10725 data.llStorageControllers.push_back(ctl);
10726 }
10727
10728 return S_OK;
10729}
10730
10731/**
10732 * Saves the hard disk configuration.
10733 */
10734HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10735 settings::StorageController &data)
10736{
10737 MediumAttachmentList atts;
10738
10739 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10740 if (FAILED(rc)) return rc;
10741
10742 data.llAttachedDevices.clear();
10743 for (MediumAttachmentList::const_iterator
10744 it = atts.begin();
10745 it != atts.end();
10746 ++it)
10747 {
10748 settings::AttachedDevice dev;
10749 IMediumAttachment *iA = *it;
10750 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10751 Medium *pMedium = pAttach->i_getMedium();
10752
10753 dev.deviceType = pAttach->i_getType();
10754 dev.lPort = pAttach->i_getPort();
10755 dev.lDevice = pAttach->i_getDevice();
10756 dev.fPassThrough = pAttach->i_getPassthrough();
10757 dev.fHotPluggable = pAttach->i_getHotPluggable();
10758 if (pMedium)
10759 {
10760 if (pMedium->i_isHostDrive())
10761 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10762 else
10763 dev.uuid = pMedium->i_getId();
10764 dev.fTempEject = pAttach->i_getTempEject();
10765 dev.fNonRotational = pAttach->i_getNonRotational();
10766 dev.fDiscard = pAttach->i_getDiscard();
10767 }
10768
10769 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10770
10771 data.llAttachedDevices.push_back(dev);
10772 }
10773
10774 return S_OK;
10775}
10776
10777/**
10778 * Saves machine state settings as defined by aFlags
10779 * (SaveSTS_* values).
10780 *
10781 * @param aFlags Combination of SaveSTS_* flags.
10782 *
10783 * @note Locks objects for writing.
10784 */
10785HRESULT Machine::i_saveStateSettings(int aFlags)
10786{
10787 if (aFlags == 0)
10788 return S_OK;
10789
10790 AutoCaller autoCaller(this);
10791 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10792
10793 /* This object's write lock is also necessary to serialize file access
10794 * (prevent concurrent reads and writes) */
10795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10796
10797 HRESULT rc = S_OK;
10798
10799 Assert(mData->pMachineConfigFile);
10800
10801 try
10802 {
10803 if (aFlags & SaveSTS_CurStateModified)
10804 mData->pMachineConfigFile->fCurrentStateModified = true;
10805
10806 if (aFlags & SaveSTS_StateFilePath)
10807 {
10808 if (!mSSData->strStateFilePath.isEmpty())
10809 /* try to make the file name relative to the settings file dir */
10810 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10811 else
10812 mData->pMachineConfigFile->strStateFile.setNull();
10813 }
10814
10815 if (aFlags & SaveSTS_StateTimeStamp)
10816 {
10817 Assert( mData->mMachineState != MachineState_Aborted
10818 || mSSData->strStateFilePath.isEmpty());
10819
10820 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10821
10822 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10823/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10824 }
10825
10826 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10827 }
10828 catch (...)
10829 {
10830 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10831 }
10832
10833 return rc;
10834}
10835
10836/**
10837 * Ensures that the given medium is added to a media registry. If this machine
10838 * was created with 4.0 or later, then the machine registry is used. Otherwise
10839 * the global VirtualBox media registry is used.
10840 *
10841 * Caller must NOT hold machine lock, media tree or any medium locks!
10842 *
10843 * @param pMedium
10844 */
10845void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10846{
10847 /* Paranoia checks: do not hold machine or media tree locks. */
10848 AssertReturnVoid(!isWriteLockOnCurrentThread());
10849 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10850
10851 ComObjPtr<Medium> pBase;
10852 {
10853 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10854 pBase = pMedium->i_getBase();
10855 }
10856
10857 /* Paranoia checks: do not hold medium locks. */
10858 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10859 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10860
10861 // decide which medium registry to use now that the medium is attached:
10862 Guid uuid;
10863 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10864 if (fCanHaveOwnMediaRegistry)
10865 // machine XML is VirtualBox 4.0 or higher:
10866 uuid = i_getId(); // machine UUID
10867 else
10868 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10869
10870 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10871 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10872 if (pMedium->i_addRegistry(uuid))
10873 mParent->i_markRegistryModified(uuid);
10874
10875 /* For more complex hard disk structures it can happen that the base
10876 * medium isn't yet associated with any medium registry. Do that now. */
10877 if (pMedium != pBase)
10878 {
10879 /* Tree lock needed by Medium::addRegistry when recursing. */
10880 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10881 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10882 {
10883 treeLock.release();
10884 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10885 treeLock.acquire();
10886 }
10887 if (pBase->i_addRegistryRecursive(uuid))
10888 {
10889 treeLock.release();
10890 mParent->i_markRegistryModified(uuid);
10891 }
10892 }
10893}
10894
10895/**
10896 * Creates differencing hard disks for all normal hard disks attached to this
10897 * machine and a new set of attachments to refer to created disks.
10898 *
10899 * Used when taking a snapshot or when deleting the current state. Gets called
10900 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10901 *
10902 * This method assumes that mMediumAttachments contains the original hard disk
10903 * attachments it needs to create diffs for. On success, these attachments will
10904 * be replaced with the created diffs.
10905 *
10906 * Attachments with non-normal hard disks are left as is.
10907 *
10908 * If @a aOnline is @c false then the original hard disks that require implicit
10909 * diffs will be locked for reading. Otherwise it is assumed that they are
10910 * already locked for writing (when the VM was started). Note that in the latter
10911 * case it is responsibility of the caller to lock the newly created diffs for
10912 * writing if this method succeeds.
10913 *
10914 * @param aProgress Progress object to run (must contain at least as
10915 * many operations left as the number of hard disks
10916 * attached).
10917 * @param aWeight Weight of this operation.
10918 * @param aOnline Whether the VM was online prior to this operation.
10919 *
10920 * @note The progress object is not marked as completed, neither on success nor
10921 * on failure. This is a responsibility of the caller.
10922 *
10923 * @note Locks this object and the media tree for writing.
10924 */
10925HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10926 ULONG aWeight,
10927 bool aOnline)
10928{
10929 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10930
10931 AutoCaller autoCaller(this);
10932 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10933
10934 AutoMultiWriteLock2 alock(this->lockHandle(),
10935 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10936
10937 /* must be in a protective state because we release the lock below */
10938 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10939 || mData->mMachineState == MachineState_OnlineSnapshotting
10940 || mData->mMachineState == MachineState_LiveSnapshotting
10941 || mData->mMachineState == MachineState_RestoringSnapshot
10942 || mData->mMachineState == MachineState_DeletingSnapshot
10943 , E_FAIL);
10944
10945 HRESULT rc = S_OK;
10946
10947 // use appropriate locked media map (online or offline)
10948 MediumLockListMap lockedMediaOffline;
10949 MediumLockListMap *lockedMediaMap;
10950 if (aOnline)
10951 lockedMediaMap = &mData->mSession.mLockedMedia;
10952 else
10953 lockedMediaMap = &lockedMediaOffline;
10954
10955 try
10956 {
10957 if (!aOnline)
10958 {
10959 /* lock all attached hard disks early to detect "in use"
10960 * situations before creating actual diffs */
10961 for (MediumAttachmentList::const_iterator
10962 it = mMediumAttachments->begin();
10963 it != mMediumAttachments->end();
10964 ++it)
10965 {
10966 MediumAttachment *pAtt = *it;
10967 if (pAtt->i_getType() == DeviceType_HardDisk)
10968 {
10969 Medium *pMedium = pAtt->i_getMedium();
10970 Assert(pMedium);
10971
10972 MediumLockList *pMediumLockList(new MediumLockList());
10973 alock.release();
10974 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10975 NULL /* pToLockWrite */,
10976 false /* fMediumLockWriteAll */,
10977 NULL,
10978 *pMediumLockList);
10979 alock.acquire();
10980 if (FAILED(rc))
10981 {
10982 delete pMediumLockList;
10983 throw rc;
10984 }
10985 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10986 if (FAILED(rc))
10987 {
10988 throw setError(rc,
10989 tr("Collecting locking information for all attached media failed"));
10990 }
10991 }
10992 }
10993
10994 /* Now lock all media. If this fails, nothing is locked. */
10995 alock.release();
10996 rc = lockedMediaMap->Lock();
10997 alock.acquire();
10998 if (FAILED(rc))
10999 {
11000 throw setError(rc,
11001 tr("Locking of attached media failed"));
11002 }
11003 }
11004
11005 /* remember the current list (note that we don't use backup() since
11006 * mMediumAttachments may be already backed up) */
11007 MediumAttachmentList atts = *mMediumAttachments.data();
11008
11009 /* start from scratch */
11010 mMediumAttachments->clear();
11011
11012 /* go through remembered attachments and create diffs for normal hard
11013 * disks and attach them */
11014 for (MediumAttachmentList::const_iterator
11015 it = atts.begin();
11016 it != atts.end();
11017 ++it)
11018 {
11019 MediumAttachment *pAtt = *it;
11020
11021 DeviceType_T devType = pAtt->i_getType();
11022 Medium *pMedium = pAtt->i_getMedium();
11023
11024 if ( devType != DeviceType_HardDisk
11025 || pMedium == NULL
11026 || pMedium->i_getType() != MediumType_Normal)
11027 {
11028 /* copy the attachment as is */
11029
11030 /** @todo the progress object created in SessionMachine::TakeSnaphot
11031 * only expects operations for hard disks. Later other
11032 * device types need to show up in the progress as well. */
11033 if (devType == DeviceType_HardDisk)
11034 {
11035 if (pMedium == NULL)
11036 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11037 aWeight); // weight
11038 else
11039 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11040 pMedium->i_getBase()->i_getName().c_str()).raw(),
11041 aWeight); // weight
11042 }
11043
11044 mMediumAttachments->push_back(pAtt);
11045 continue;
11046 }
11047
11048 /* need a diff */
11049 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11050 pMedium->i_getBase()->i_getName().c_str()).raw(),
11051 aWeight); // weight
11052
11053 Utf8Str strFullSnapshotFolder;
11054 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11055
11056 ComObjPtr<Medium> diff;
11057 diff.createObject();
11058 // store the diff in the same registry as the parent
11059 // (this cannot fail here because we can't create implicit diffs for
11060 // unregistered images)
11061 Guid uuidRegistryParent;
11062 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11063 Assert(fInRegistry); NOREF(fInRegistry);
11064 rc = diff->init(mParent,
11065 pMedium->i_getPreferredDiffFormat(),
11066 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11067 uuidRegistryParent,
11068 DeviceType_HardDisk);
11069 if (FAILED(rc)) throw rc;
11070
11071 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11072 * the push_back? Looks like we're going to release medium with the
11073 * wrong kind of lock (general issue with if we fail anywhere at all)
11074 * and an orphaned VDI in the snapshots folder. */
11075
11076 /* update the appropriate lock list */
11077 MediumLockList *pMediumLockList;
11078 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11079 AssertComRCThrowRC(rc);
11080 if (aOnline)
11081 {
11082 alock.release();
11083 /* The currently attached medium will be read-only, change
11084 * the lock type to read. */
11085 rc = pMediumLockList->Update(pMedium, false);
11086 alock.acquire();
11087 AssertComRCThrowRC(rc);
11088 }
11089
11090 /* release the locks before the potentially lengthy operation */
11091 alock.release();
11092 rc = pMedium->i_createDiffStorage(diff,
11093 pMedium->i_getPreferredDiffVariant(),
11094 pMediumLockList,
11095 NULL /* aProgress */,
11096 true /* aWait */);
11097 alock.acquire();
11098 if (FAILED(rc)) throw rc;
11099
11100 /* actual lock list update is done in Machine::i_commitMedia */
11101
11102 rc = diff->i_addBackReference(mData->mUuid);
11103 AssertComRCThrowRC(rc);
11104
11105 /* add a new attachment */
11106 ComObjPtr<MediumAttachment> attachment;
11107 attachment.createObject();
11108 rc = attachment->init(this,
11109 diff,
11110 pAtt->i_getControllerName(),
11111 pAtt->i_getPort(),
11112 pAtt->i_getDevice(),
11113 DeviceType_HardDisk,
11114 true /* aImplicit */,
11115 false /* aPassthrough */,
11116 false /* aTempEject */,
11117 pAtt->i_getNonRotational(),
11118 pAtt->i_getDiscard(),
11119 pAtt->i_getHotPluggable(),
11120 pAtt->i_getBandwidthGroup());
11121 if (FAILED(rc)) throw rc;
11122
11123 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11124 AssertComRCThrowRC(rc);
11125 mMediumAttachments->push_back(attachment);
11126 }
11127 }
11128 catch (HRESULT aRC) { rc = aRC; }
11129
11130 /* unlock all hard disks we locked when there is no VM */
11131 if (!aOnline)
11132 {
11133 ErrorInfoKeeper eik;
11134
11135 HRESULT rc1 = lockedMediaMap->Clear();
11136 AssertComRC(rc1);
11137 }
11138
11139 return rc;
11140}
11141
11142/**
11143 * Deletes implicit differencing hard disks created either by
11144 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11145 * mMediumAttachments.
11146 *
11147 * Note that to delete hard disks created by #attachDevice() this method is
11148 * called from #i_rollbackMedia() when the changes are rolled back.
11149 *
11150 * @note Locks this object and the media tree for writing.
11151 */
11152HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11153{
11154 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11155
11156 AutoCaller autoCaller(this);
11157 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11158
11159 AutoMultiWriteLock2 alock(this->lockHandle(),
11160 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11161
11162 /* We absolutely must have backed up state. */
11163 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11164
11165 /* Check if there are any implicitly created diff images. */
11166 bool fImplicitDiffs = false;
11167 for (MediumAttachmentList::const_iterator
11168 it = mMediumAttachments->begin();
11169 it != mMediumAttachments->end();
11170 ++it)
11171 {
11172 const ComObjPtr<MediumAttachment> &pAtt = *it;
11173 if (pAtt->i_isImplicit())
11174 {
11175 fImplicitDiffs = true;
11176 break;
11177 }
11178 }
11179 /* If there is nothing to do, leave early. This saves lots of image locking
11180 * effort. It also avoids a MachineStateChanged event without real reason.
11181 * This is important e.g. when loading a VM config, because there should be
11182 * no events. Otherwise API clients can become thoroughly confused for
11183 * inaccessible VMs (the code for loading VM configs uses this method for
11184 * cleanup if the config makes no sense), as they take such events as an
11185 * indication that the VM is alive, and they would force the VM config to
11186 * be reread, leading to an endless loop. */
11187 if (!fImplicitDiffs)
11188 return S_OK;
11189
11190 HRESULT rc = S_OK;
11191 MachineState_T oldState = mData->mMachineState;
11192
11193 /* will release the lock before the potentially lengthy operation,
11194 * so protect with the special state (unless already protected) */
11195 if ( oldState != MachineState_Snapshotting
11196 && oldState != MachineState_OnlineSnapshotting
11197 && oldState != MachineState_LiveSnapshotting
11198 && oldState != MachineState_RestoringSnapshot
11199 && oldState != MachineState_DeletingSnapshot
11200 && oldState != MachineState_DeletingSnapshotOnline
11201 && oldState != MachineState_DeletingSnapshotPaused
11202 )
11203 i_setMachineState(MachineState_SettingUp);
11204
11205 // use appropriate locked media map (online or offline)
11206 MediumLockListMap lockedMediaOffline;
11207 MediumLockListMap *lockedMediaMap;
11208 if (aOnline)
11209 lockedMediaMap = &mData->mSession.mLockedMedia;
11210 else
11211 lockedMediaMap = &lockedMediaOffline;
11212
11213 try
11214 {
11215 if (!aOnline)
11216 {
11217 /* lock all attached hard disks early to detect "in use"
11218 * situations before deleting actual diffs */
11219 for (MediumAttachmentList::const_iterator
11220 it = mMediumAttachments->begin();
11221 it != mMediumAttachments->end();
11222 ++it)
11223 {
11224 MediumAttachment *pAtt = *it;
11225 if (pAtt->i_getType() == DeviceType_HardDisk)
11226 {
11227 Medium *pMedium = pAtt->i_getMedium();
11228 Assert(pMedium);
11229
11230 MediumLockList *pMediumLockList(new MediumLockList());
11231 alock.release();
11232 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11233 NULL /* pToLockWrite */,
11234 false /* fMediumLockWriteAll */,
11235 NULL,
11236 *pMediumLockList);
11237 alock.acquire();
11238
11239 if (FAILED(rc))
11240 {
11241 delete pMediumLockList;
11242 throw rc;
11243 }
11244
11245 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11246 if (FAILED(rc))
11247 throw rc;
11248 }
11249 }
11250
11251 if (FAILED(rc))
11252 throw rc;
11253 } // end of offline
11254
11255 /* Lock lists are now up to date and include implicitly created media */
11256
11257 /* Go through remembered attachments and delete all implicitly created
11258 * diffs and fix up the attachment information */
11259 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11260 MediumAttachmentList implicitAtts;
11261 for (MediumAttachmentList::const_iterator
11262 it = mMediumAttachments->begin();
11263 it != mMediumAttachments->end();
11264 ++it)
11265 {
11266 ComObjPtr<MediumAttachment> pAtt = *it;
11267 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11268 if (pMedium.isNull())
11269 continue;
11270
11271 // Implicit attachments go on the list for deletion and back references are removed.
11272 if (pAtt->i_isImplicit())
11273 {
11274 /* Deassociate and mark for deletion */
11275 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11276 rc = pMedium->i_removeBackReference(mData->mUuid);
11277 if (FAILED(rc))
11278 throw rc;
11279 implicitAtts.push_back(pAtt);
11280 continue;
11281 }
11282
11283 /* Was this medium attached before? */
11284 if (!i_findAttachment(oldAtts, pMedium))
11285 {
11286 /* no: de-associate */
11287 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11288 rc = pMedium->i_removeBackReference(mData->mUuid);
11289 if (FAILED(rc))
11290 throw rc;
11291 continue;
11292 }
11293 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11294 }
11295
11296 /* If there are implicit attachments to delete, throw away the lock
11297 * map contents (which will unlock all media) since the medium
11298 * attachments will be rolled back. Below we need to completely
11299 * recreate the lock map anyway since it is infinitely complex to
11300 * do this incrementally (would need reconstructing each attachment
11301 * change, which would be extremely hairy). */
11302 if (implicitAtts.size() != 0)
11303 {
11304 ErrorInfoKeeper eik;
11305
11306 HRESULT rc1 = lockedMediaMap->Clear();
11307 AssertComRC(rc1);
11308 }
11309
11310 /* rollback hard disk changes */
11311 mMediumAttachments.rollback();
11312
11313 MultiResult mrc(S_OK);
11314
11315 // Delete unused implicit diffs.
11316 if (implicitAtts.size() != 0)
11317 {
11318 alock.release();
11319
11320 for (MediumAttachmentList::const_iterator
11321 it = implicitAtts.begin();
11322 it != implicitAtts.end();
11323 ++it)
11324 {
11325 // Remove medium associated with this attachment.
11326 ComObjPtr<MediumAttachment> pAtt = *it;
11327 Assert(pAtt);
11328 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11329 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11330 Assert(pMedium);
11331
11332 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11333 // continue on delete failure, just collect error messages
11334 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11335 pMedium->i_getLocationFull().c_str() ));
11336 mrc = rc;
11337 }
11338 // Clear the list of deleted implicit attachments now, while not
11339 // holding the lock, as it will ultimately trigger Medium::uninit()
11340 // calls which assume that the media tree lock isn't held.
11341 implicitAtts.clear();
11342
11343 alock.acquire();
11344
11345 /* if there is a VM recreate media lock map as mentioned above,
11346 * otherwise it is a waste of time and we leave things unlocked */
11347 if (aOnline)
11348 {
11349 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11350 /* must never be NULL, but better safe than sorry */
11351 if (!pMachine.isNull())
11352 {
11353 alock.release();
11354 rc = mData->mSession.mMachine->i_lockMedia();
11355 alock.acquire();
11356 if (FAILED(rc))
11357 throw rc;
11358 }
11359 }
11360 }
11361 }
11362 catch (HRESULT aRC) {rc = aRC;}
11363
11364 if (mData->mMachineState == MachineState_SettingUp)
11365 i_setMachineState(oldState);
11366
11367 /* unlock all hard disks we locked when there is no VM */
11368 if (!aOnline)
11369 {
11370 ErrorInfoKeeper eik;
11371
11372 HRESULT rc1 = lockedMediaMap->Clear();
11373 AssertComRC(rc1);
11374 }
11375
11376 return rc;
11377}
11378
11379
11380/**
11381 * Looks through the given list of media attachments for one with the given parameters
11382 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11383 * can be searched as well if needed.
11384 *
11385 * @param ll
11386 * @param aControllerName
11387 * @param aControllerPort
11388 * @param aDevice
11389 * @return
11390 */
11391MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11392 const Utf8Str &aControllerName,
11393 LONG aControllerPort,
11394 LONG aDevice)
11395{
11396 for (MediumAttachmentList::const_iterator
11397 it = ll.begin();
11398 it != ll.end();
11399 ++it)
11400 {
11401 MediumAttachment *pAttach = *it;
11402 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11403 return pAttach;
11404 }
11405
11406 return NULL;
11407}
11408
11409/**
11410 * Looks through the given list of media attachments for one with the given parameters
11411 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11412 * can be searched as well if needed.
11413 *
11414 * @param ll
11415 * @param pMedium
11416 * @return
11417 */
11418MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11419 ComObjPtr<Medium> pMedium)
11420{
11421 for (MediumAttachmentList::const_iterator
11422 it = ll.begin();
11423 it != ll.end();
11424 ++it)
11425 {
11426 MediumAttachment *pAttach = *it;
11427 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11428 if (pMediumThis == pMedium)
11429 return pAttach;
11430 }
11431
11432 return NULL;
11433}
11434
11435/**
11436 * Looks through the given list of media attachments for one with the given parameters
11437 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11438 * can be searched as well if needed.
11439 *
11440 * @param ll
11441 * @param id
11442 * @return
11443 */
11444MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11445 Guid &id)
11446{
11447 for (MediumAttachmentList::const_iterator
11448 it = ll.begin();
11449 it != ll.end();
11450 ++it)
11451 {
11452 MediumAttachment *pAttach = *it;
11453 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11454 if (pMediumThis->i_getId() == id)
11455 return pAttach;
11456 }
11457
11458 return NULL;
11459}
11460
11461/**
11462 * Main implementation for Machine::DetachDevice. This also gets called
11463 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11464 *
11465 * @param pAttach Medium attachment to detach.
11466 * @param writeLock Machine write lock which the caller must have locked once.
11467 * This may be released temporarily in here.
11468 * @param pSnapshot If NULL, then the detachment is for the current machine.
11469 * Otherwise this is for a SnapshotMachine, and this must be
11470 * its snapshot.
11471 * @return
11472 */
11473HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11474 AutoWriteLock &writeLock,
11475 Snapshot *pSnapshot)
11476{
11477 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11478 DeviceType_T mediumType = pAttach->i_getType();
11479
11480 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11481
11482 if (pAttach->i_isImplicit())
11483 {
11484 /* attempt to implicitly delete the implicitly created diff */
11485
11486 /// @todo move the implicit flag from MediumAttachment to Medium
11487 /// and forbid any hard disk operation when it is implicit. Or maybe
11488 /// a special media state for it to make it even more simple.
11489
11490 Assert(mMediumAttachments.isBackedUp());
11491
11492 /* will release the lock before the potentially lengthy operation, so
11493 * protect with the special state */
11494 MachineState_T oldState = mData->mMachineState;
11495 i_setMachineState(MachineState_SettingUp);
11496
11497 writeLock.release();
11498
11499 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11500 true /*aWait*/);
11501
11502 writeLock.acquire();
11503
11504 i_setMachineState(oldState);
11505
11506 if (FAILED(rc)) return rc;
11507 }
11508
11509 i_setModified(IsModified_Storage);
11510 mMediumAttachments.backup();
11511 mMediumAttachments->remove(pAttach);
11512
11513 if (!oldmedium.isNull())
11514 {
11515 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11516 if (pSnapshot)
11517 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11518 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11519 else if (mediumType != DeviceType_HardDisk)
11520 oldmedium->i_removeBackReference(mData->mUuid);
11521 }
11522
11523 return S_OK;
11524}
11525
11526/**
11527 * Goes thru all media of the given list and
11528 *
11529 * 1) calls i_detachDevice() on each of them for this machine and
11530 * 2) adds all Medium objects found in the process to the given list,
11531 * depending on cleanupMode.
11532 *
11533 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11534 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11535 * media to the list.
11536 *
11537 * This gets called from Machine::Unregister, both for the actual Machine and
11538 * the SnapshotMachine objects that might be found in the snapshots.
11539 *
11540 * Requires caller and locking. The machine lock must be passed in because it
11541 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11542 *
11543 * @param writeLock Machine lock from top-level caller; this gets passed to
11544 * i_detachDevice.
11545 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11546 * object if called for a SnapshotMachine.
11547 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11548 * added to llMedia; if Full, then all media get added;
11549 * otherwise no media get added.
11550 * @param llMedia Caller's list to receive Medium objects which got detached so
11551 * caller can close() them, depending on cleanupMode.
11552 * @return
11553 */
11554HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11555 Snapshot *pSnapshot,
11556 CleanupMode_T cleanupMode,
11557 MediaList &llMedia)
11558{
11559 Assert(isWriteLockOnCurrentThread());
11560
11561 HRESULT rc;
11562
11563 // make a temporary list because i_detachDevice invalidates iterators into
11564 // mMediumAttachments
11565 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11566
11567 for (MediumAttachmentList::iterator
11568 it = llAttachments2.begin();
11569 it != llAttachments2.end();
11570 ++it)
11571 {
11572 ComObjPtr<MediumAttachment> &pAttach = *it;
11573 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11574
11575 if (!pMedium.isNull())
11576 {
11577 AutoCaller mac(pMedium);
11578 if (FAILED(mac.rc())) return mac.rc();
11579 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11580 DeviceType_T devType = pMedium->i_getDeviceType();
11581 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11582 && devType == DeviceType_HardDisk)
11583 || (cleanupMode == CleanupMode_Full)
11584 )
11585 {
11586 llMedia.push_back(pMedium);
11587 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11588 /* Not allowed to keep this lock as below we need the parent
11589 * medium lock, and the lock order is parent to child. */
11590 lock.release();
11591 /*
11592 * Search for medias which are not attached to any machine, but
11593 * in the chain to an attached disk. Mediums are only consided
11594 * if they are:
11595 * - have only one child
11596 * - no references to any machines
11597 * - are of normal medium type
11598 */
11599 while (!pParent.isNull())
11600 {
11601 AutoCaller mac1(pParent);
11602 if (FAILED(mac1.rc())) return mac1.rc();
11603 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11604 if (pParent->i_getChildren().size() == 1)
11605 {
11606 if ( pParent->i_getMachineBackRefCount() == 0
11607 && pParent->i_getType() == MediumType_Normal
11608 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11609 llMedia.push_back(pParent);
11610 }
11611 else
11612 break;
11613 pParent = pParent->i_getParent();
11614 }
11615 }
11616 }
11617
11618 // real machine: then we need to use the proper method
11619 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11620
11621 if (FAILED(rc))
11622 return rc;
11623 }
11624
11625 return S_OK;
11626}
11627
11628/**
11629 * Perform deferred hard disk detachments.
11630 *
11631 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11632 * changed (not backed up).
11633 *
11634 * If @a aOnline is @c true then this method will also unlock the old hard
11635 * disks for which the new implicit diffs were created and will lock these new
11636 * diffs for writing.
11637 *
11638 * @param aOnline Whether the VM was online prior to this operation.
11639 *
11640 * @note Locks this object for writing!
11641 */
11642void Machine::i_commitMedia(bool aOnline /*= false*/)
11643{
11644 AutoCaller autoCaller(this);
11645 AssertComRCReturnVoid(autoCaller.rc());
11646
11647 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11648
11649 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11650
11651 HRESULT rc = S_OK;
11652
11653 /* no attach/detach operations -- nothing to do */
11654 if (!mMediumAttachments.isBackedUp())
11655 return;
11656
11657 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11658 bool fMediaNeedsLocking = false;
11659
11660 /* enumerate new attachments */
11661 for (MediumAttachmentList::const_iterator
11662 it = mMediumAttachments->begin();
11663 it != mMediumAttachments->end();
11664 ++it)
11665 {
11666 MediumAttachment *pAttach = *it;
11667
11668 pAttach->i_commit();
11669
11670 Medium *pMedium = pAttach->i_getMedium();
11671 bool fImplicit = pAttach->i_isImplicit();
11672
11673 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11674 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11675 fImplicit));
11676
11677 /** @todo convert all this Machine-based voodoo to MediumAttachment
11678 * based commit logic. */
11679 if (fImplicit)
11680 {
11681 /* convert implicit attachment to normal */
11682 pAttach->i_setImplicit(false);
11683
11684 if ( aOnline
11685 && pMedium
11686 && pAttach->i_getType() == DeviceType_HardDisk
11687 )
11688 {
11689 /* update the appropriate lock list */
11690 MediumLockList *pMediumLockList;
11691 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11692 AssertComRC(rc);
11693 if (pMediumLockList)
11694 {
11695 /* unlock if there's a need to change the locking */
11696 if (!fMediaNeedsLocking)
11697 {
11698 rc = mData->mSession.mLockedMedia.Unlock();
11699 AssertComRC(rc);
11700 fMediaNeedsLocking = true;
11701 }
11702 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11703 AssertComRC(rc);
11704 rc = pMediumLockList->Append(pMedium, true);
11705 AssertComRC(rc);
11706 }
11707 }
11708
11709 continue;
11710 }
11711
11712 if (pMedium)
11713 {
11714 /* was this medium attached before? */
11715 for (MediumAttachmentList::iterator
11716 oldIt = oldAtts.begin();
11717 oldIt != oldAtts.end();
11718 ++oldIt)
11719 {
11720 MediumAttachment *pOldAttach = *oldIt;
11721 if (pOldAttach->i_getMedium() == pMedium)
11722 {
11723 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11724
11725 /* yes: remove from old to avoid de-association */
11726 oldAtts.erase(oldIt);
11727 break;
11728 }
11729 }
11730 }
11731 }
11732
11733 /* enumerate remaining old attachments and de-associate from the
11734 * current machine state */
11735 for (MediumAttachmentList::const_iterator
11736 it = oldAtts.begin();
11737 it != oldAtts.end();
11738 ++it)
11739 {
11740 MediumAttachment *pAttach = *it;
11741 Medium *pMedium = pAttach->i_getMedium();
11742
11743 /* Detach only hard disks, since DVD/floppy media is detached
11744 * instantly in MountMedium. */
11745 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11746 {
11747 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11748
11749 /* now de-associate from the current machine state */
11750 rc = pMedium->i_removeBackReference(mData->mUuid);
11751 AssertComRC(rc);
11752
11753 if (aOnline)
11754 {
11755 /* unlock since medium is not used anymore */
11756 MediumLockList *pMediumLockList;
11757 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11758 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11759 {
11760 /* this happens for online snapshots, there the attachment
11761 * is changing, but only to a diff image created under
11762 * the old one, so there is no separate lock list */
11763 Assert(!pMediumLockList);
11764 }
11765 else
11766 {
11767 AssertComRC(rc);
11768 if (pMediumLockList)
11769 {
11770 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11771 AssertComRC(rc);
11772 }
11773 }
11774 }
11775 }
11776 }
11777
11778 /* take media locks again so that the locking state is consistent */
11779 if (fMediaNeedsLocking)
11780 {
11781 Assert(aOnline);
11782 rc = mData->mSession.mLockedMedia.Lock();
11783 AssertComRC(rc);
11784 }
11785
11786 /* commit the hard disk changes */
11787 mMediumAttachments.commit();
11788
11789 if (i_isSessionMachine())
11790 {
11791 /*
11792 * Update the parent machine to point to the new owner.
11793 * This is necessary because the stored parent will point to the
11794 * session machine otherwise and cause crashes or errors later
11795 * when the session machine gets invalid.
11796 */
11797 /** @todo Change the MediumAttachment class to behave like any other
11798 * class in this regard by creating peer MediumAttachment
11799 * objects for session machines and share the data with the peer
11800 * machine.
11801 */
11802 for (MediumAttachmentList::const_iterator
11803 it = mMediumAttachments->begin();
11804 it != mMediumAttachments->end();
11805 ++it)
11806 (*it)->i_updateParentMachine(mPeer);
11807
11808 /* attach new data to the primary machine and reshare it */
11809 mPeer->mMediumAttachments.attach(mMediumAttachments);
11810 }
11811
11812 return;
11813}
11814
11815/**
11816 * Perform deferred deletion of implicitly created diffs.
11817 *
11818 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11819 * changed (not backed up).
11820 *
11821 * @note Locks this object for writing!
11822 */
11823void Machine::i_rollbackMedia()
11824{
11825 AutoCaller autoCaller(this);
11826 AssertComRCReturnVoid(autoCaller.rc());
11827
11828 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11829 LogFlowThisFunc(("Entering rollbackMedia\n"));
11830
11831 HRESULT rc = S_OK;
11832
11833 /* no attach/detach operations -- nothing to do */
11834 if (!mMediumAttachments.isBackedUp())
11835 return;
11836
11837 /* enumerate new attachments */
11838 for (MediumAttachmentList::const_iterator
11839 it = mMediumAttachments->begin();
11840 it != mMediumAttachments->end();
11841 ++it)
11842 {
11843 MediumAttachment *pAttach = *it;
11844 /* Fix up the backrefs for DVD/floppy media. */
11845 if (pAttach->i_getType() != DeviceType_HardDisk)
11846 {
11847 Medium *pMedium = pAttach->i_getMedium();
11848 if (pMedium)
11849 {
11850 rc = pMedium->i_removeBackReference(mData->mUuid);
11851 AssertComRC(rc);
11852 }
11853 }
11854
11855 (*it)->i_rollback();
11856
11857 pAttach = *it;
11858 /* Fix up the backrefs for DVD/floppy media. */
11859 if (pAttach->i_getType() != DeviceType_HardDisk)
11860 {
11861 Medium *pMedium = pAttach->i_getMedium();
11862 if (pMedium)
11863 {
11864 rc = pMedium->i_addBackReference(mData->mUuid);
11865 AssertComRC(rc);
11866 }
11867 }
11868 }
11869
11870 /** @todo convert all this Machine-based voodoo to MediumAttachment
11871 * based rollback logic. */
11872 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11873
11874 return;
11875}
11876
11877/**
11878 * Returns true if the settings file is located in the directory named exactly
11879 * as the machine; this means, among other things, that the machine directory
11880 * should be auto-renamed.
11881 *
11882 * @param aSettingsDir if not NULL, the full machine settings file directory
11883 * name will be assigned there.
11884 *
11885 * @note Doesn't lock anything.
11886 * @note Not thread safe (must be called from this object's lock).
11887 */
11888bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11889{
11890 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11891 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11892 if (aSettingsDir)
11893 *aSettingsDir = strMachineDirName;
11894 strMachineDirName.stripPath(); // vmname
11895 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11896 strConfigFileOnly.stripPath() // vmname.vbox
11897 .stripSuffix(); // vmname
11898 /** @todo hack, make somehow use of ComposeMachineFilename */
11899 if (mUserData->s.fDirectoryIncludesUUID)
11900 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11901
11902 AssertReturn(!strMachineDirName.isEmpty(), false);
11903 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11904
11905 return strMachineDirName == strConfigFileOnly;
11906}
11907
11908/**
11909 * Discards all changes to machine settings.
11910 *
11911 * @param aNotify Whether to notify the direct session about changes or not.
11912 *
11913 * @note Locks objects for writing!
11914 */
11915void Machine::i_rollback(bool aNotify)
11916{
11917 AutoCaller autoCaller(this);
11918 AssertComRCReturn(autoCaller.rc(), (void)0);
11919
11920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11921
11922 if (!mStorageControllers.isNull())
11923 {
11924 if (mStorageControllers.isBackedUp())
11925 {
11926 /* unitialize all new devices (absent in the backed up list). */
11927 StorageControllerList *backedList = mStorageControllers.backedUpData();
11928 for (StorageControllerList::const_iterator
11929 it = mStorageControllers->begin();
11930 it != mStorageControllers->end();
11931 ++it)
11932 {
11933 if ( std::find(backedList->begin(), backedList->end(), *it)
11934 == backedList->end()
11935 )
11936 {
11937 (*it)->uninit();
11938 }
11939 }
11940
11941 /* restore the list */
11942 mStorageControllers.rollback();
11943 }
11944
11945 /* rollback any changes to devices after restoring the list */
11946 if (mData->flModifications & IsModified_Storage)
11947 {
11948 for (StorageControllerList::const_iterator
11949 it = mStorageControllers->begin();
11950 it != mStorageControllers->end();
11951 ++it)
11952 {
11953 (*it)->i_rollback();
11954 }
11955 }
11956 }
11957
11958 if (!mUSBControllers.isNull())
11959 {
11960 if (mUSBControllers.isBackedUp())
11961 {
11962 /* unitialize all new devices (absent in the backed up list). */
11963 USBControllerList *backedList = mUSBControllers.backedUpData();
11964 for (USBControllerList::const_iterator
11965 it = mUSBControllers->begin();
11966 it != mUSBControllers->end();
11967 ++it)
11968 {
11969 if ( std::find(backedList->begin(), backedList->end(), *it)
11970 == backedList->end()
11971 )
11972 {
11973 (*it)->uninit();
11974 }
11975 }
11976
11977 /* restore the list */
11978 mUSBControllers.rollback();
11979 }
11980
11981 /* rollback any changes to devices after restoring the list */
11982 if (mData->flModifications & IsModified_USB)
11983 {
11984 for (USBControllerList::const_iterator
11985 it = mUSBControllers->begin();
11986 it != mUSBControllers->end();
11987 ++it)
11988 {
11989 (*it)->i_rollback();
11990 }
11991 }
11992 }
11993
11994 mUserData.rollback();
11995
11996 mHWData.rollback();
11997
11998 if (mData->flModifications & IsModified_Storage)
11999 i_rollbackMedia();
12000
12001 if (mBIOSSettings)
12002 mBIOSSettings->i_rollback();
12003
12004 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12005 mVRDEServer->i_rollback();
12006
12007 if (mAudioAdapter)
12008 mAudioAdapter->i_rollback();
12009
12010 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12011 mUSBDeviceFilters->i_rollback();
12012
12013 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12014 mBandwidthControl->i_rollback();
12015
12016 if (!mHWData.isNull())
12017 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12018 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12019 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12020 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12021
12022 if (mData->flModifications & IsModified_NetworkAdapters)
12023 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12024 if ( mNetworkAdapters[slot]
12025 && mNetworkAdapters[slot]->i_isModified())
12026 {
12027 mNetworkAdapters[slot]->i_rollback();
12028 networkAdapters[slot] = mNetworkAdapters[slot];
12029 }
12030
12031 if (mData->flModifications & IsModified_SerialPorts)
12032 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12033 if ( mSerialPorts[slot]
12034 && mSerialPorts[slot]->i_isModified())
12035 {
12036 mSerialPorts[slot]->i_rollback();
12037 serialPorts[slot] = mSerialPorts[slot];
12038 }
12039
12040 if (mData->flModifications & IsModified_ParallelPorts)
12041 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12042 if ( mParallelPorts[slot]
12043 && mParallelPorts[slot]->i_isModified())
12044 {
12045 mParallelPorts[slot]->i_rollback();
12046 parallelPorts[slot] = mParallelPorts[slot];
12047 }
12048
12049 if (aNotify)
12050 {
12051 /* inform the direct session about changes */
12052
12053 ComObjPtr<Machine> that = this;
12054 uint32_t flModifications = mData->flModifications;
12055 alock.release();
12056
12057 if (flModifications & IsModified_SharedFolders)
12058 that->i_onSharedFolderChange();
12059
12060 if (flModifications & IsModified_VRDEServer)
12061 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12062 if (flModifications & IsModified_USB)
12063 that->i_onUSBControllerChange();
12064
12065 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12066 if (networkAdapters[slot])
12067 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12068 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12069 if (serialPorts[slot])
12070 that->i_onSerialPortChange(serialPorts[slot]);
12071 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12072 if (parallelPorts[slot])
12073 that->i_onParallelPortChange(parallelPorts[slot]);
12074
12075 if (flModifications & IsModified_Storage)
12076 that->i_onStorageControllerChange();
12077
12078#if 0
12079 if (flModifications & IsModified_BandwidthControl)
12080 that->onBandwidthControlChange();
12081#endif
12082 }
12083}
12084
12085/**
12086 * Commits all the changes to machine settings.
12087 *
12088 * Note that this operation is supposed to never fail.
12089 *
12090 * @note Locks this object and children for writing.
12091 */
12092void Machine::i_commit()
12093{
12094 AutoCaller autoCaller(this);
12095 AssertComRCReturnVoid(autoCaller.rc());
12096
12097 AutoCaller peerCaller(mPeer);
12098 AssertComRCReturnVoid(peerCaller.rc());
12099
12100 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12101
12102 /*
12103 * use safe commit to ensure Snapshot machines (that share mUserData)
12104 * will still refer to a valid memory location
12105 */
12106 mUserData.commitCopy();
12107
12108 mHWData.commit();
12109
12110 if (mMediumAttachments.isBackedUp())
12111 i_commitMedia(Global::IsOnline(mData->mMachineState));
12112
12113 mBIOSSettings->i_commit();
12114 mVRDEServer->i_commit();
12115 mAudioAdapter->i_commit();
12116 mUSBDeviceFilters->i_commit();
12117 mBandwidthControl->i_commit();
12118
12119 /* Since mNetworkAdapters is a list which might have been changed (resized)
12120 * without using the Backupable<> template we need to handle the copying
12121 * of the list entries manually, including the creation of peers for the
12122 * new objects. */
12123 bool commitNetworkAdapters = false;
12124 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12125 if (mPeer)
12126 {
12127 /* commit everything, even the ones which will go away */
12128 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12129 mNetworkAdapters[slot]->i_commit();
12130 /* copy over the new entries, creating a peer and uninit the original */
12131 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12132 for (size_t slot = 0; slot < newSize; slot++)
12133 {
12134 /* look if this adapter has a peer device */
12135 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12136 if (!peer)
12137 {
12138 /* no peer means the adapter is a newly created one;
12139 * create a peer owning data this data share it with */
12140 peer.createObject();
12141 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12142 }
12143 mPeer->mNetworkAdapters[slot] = peer;
12144 }
12145 /* uninit any no longer needed network adapters */
12146 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12147 mNetworkAdapters[slot]->uninit();
12148 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12149 {
12150 if (mPeer->mNetworkAdapters[slot])
12151 mPeer->mNetworkAdapters[slot]->uninit();
12152 }
12153 /* Keep the original network adapter count until this point, so that
12154 * discarding a chipset type change will not lose settings. */
12155 mNetworkAdapters.resize(newSize);
12156 mPeer->mNetworkAdapters.resize(newSize);
12157 }
12158 else
12159 {
12160 /* we have no peer (our parent is the newly created machine);
12161 * just commit changes to the network adapters */
12162 commitNetworkAdapters = true;
12163 }
12164 if (commitNetworkAdapters)
12165 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12166 mNetworkAdapters[slot]->i_commit();
12167
12168 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12169 mSerialPorts[slot]->i_commit();
12170 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12171 mParallelPorts[slot]->i_commit();
12172
12173 bool commitStorageControllers = false;
12174
12175 if (mStorageControllers.isBackedUp())
12176 {
12177 mStorageControllers.commit();
12178
12179 if (mPeer)
12180 {
12181 /* Commit all changes to new controllers (this will reshare data with
12182 * peers for those who have peers) */
12183 StorageControllerList *newList = new StorageControllerList();
12184 for (StorageControllerList::const_iterator
12185 it = mStorageControllers->begin();
12186 it != mStorageControllers->end();
12187 ++it)
12188 {
12189 (*it)->i_commit();
12190
12191 /* look if this controller has a peer device */
12192 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12193 if (!peer)
12194 {
12195 /* no peer means the device is a newly created one;
12196 * create a peer owning data this device share it with */
12197 peer.createObject();
12198 peer->init(mPeer, *it, true /* aReshare */);
12199 }
12200 else
12201 {
12202 /* remove peer from the old list */
12203 mPeer->mStorageControllers->remove(peer);
12204 }
12205 /* and add it to the new list */
12206 newList->push_back(peer);
12207 }
12208
12209 /* uninit old peer's controllers that are left */
12210 for (StorageControllerList::const_iterator
12211 it = mPeer->mStorageControllers->begin();
12212 it != mPeer->mStorageControllers->end();
12213 ++it)
12214 {
12215 (*it)->uninit();
12216 }
12217
12218 /* attach new list of controllers to our peer */
12219 mPeer->mStorageControllers.attach(newList);
12220 }
12221 else
12222 {
12223 /* we have no peer (our parent is the newly created machine);
12224 * just commit changes to devices */
12225 commitStorageControllers = true;
12226 }
12227 }
12228 else
12229 {
12230 /* the list of controllers itself is not changed,
12231 * just commit changes to controllers themselves */
12232 commitStorageControllers = true;
12233 }
12234
12235 if (commitStorageControllers)
12236 {
12237 for (StorageControllerList::const_iterator
12238 it = mStorageControllers->begin();
12239 it != mStorageControllers->end();
12240 ++it)
12241 {
12242 (*it)->i_commit();
12243 }
12244 }
12245
12246 bool commitUSBControllers = false;
12247
12248 if (mUSBControllers.isBackedUp())
12249 {
12250 mUSBControllers.commit();
12251
12252 if (mPeer)
12253 {
12254 /* Commit all changes to new controllers (this will reshare data with
12255 * peers for those who have peers) */
12256 USBControllerList *newList = new USBControllerList();
12257 for (USBControllerList::const_iterator
12258 it = mUSBControllers->begin();
12259 it != mUSBControllers->end();
12260 ++it)
12261 {
12262 (*it)->i_commit();
12263
12264 /* look if this controller has a peer device */
12265 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12266 if (!peer)
12267 {
12268 /* no peer means the device is a newly created one;
12269 * create a peer owning data this device share it with */
12270 peer.createObject();
12271 peer->init(mPeer, *it, true /* aReshare */);
12272 }
12273 else
12274 {
12275 /* remove peer from the old list */
12276 mPeer->mUSBControllers->remove(peer);
12277 }
12278 /* and add it to the new list */
12279 newList->push_back(peer);
12280 }
12281
12282 /* uninit old peer's controllers that are left */
12283 for (USBControllerList::const_iterator
12284 it = mPeer->mUSBControllers->begin();
12285 it != mPeer->mUSBControllers->end();
12286 ++it)
12287 {
12288 (*it)->uninit();
12289 }
12290
12291 /* attach new list of controllers to our peer */
12292 mPeer->mUSBControllers.attach(newList);
12293 }
12294 else
12295 {
12296 /* we have no peer (our parent is the newly created machine);
12297 * just commit changes to devices */
12298 commitUSBControllers = true;
12299 }
12300 }
12301 else
12302 {
12303 /* the list of controllers itself is not changed,
12304 * just commit changes to controllers themselves */
12305 commitUSBControllers = true;
12306 }
12307
12308 if (commitUSBControllers)
12309 {
12310 for (USBControllerList::const_iterator
12311 it = mUSBControllers->begin();
12312 it != mUSBControllers->end();
12313 ++it)
12314 {
12315 (*it)->i_commit();
12316 }
12317 }
12318
12319 if (i_isSessionMachine())
12320 {
12321 /* attach new data to the primary machine and reshare it */
12322 mPeer->mUserData.attach(mUserData);
12323 mPeer->mHWData.attach(mHWData);
12324 /* mmMediumAttachments is reshared by fixupMedia */
12325 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12326 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12327 }
12328}
12329
12330/**
12331 * Copies all the hardware data from the given machine.
12332 *
12333 * Currently, only called when the VM is being restored from a snapshot. In
12334 * particular, this implies that the VM is not running during this method's
12335 * call.
12336 *
12337 * @note This method must be called from under this object's lock.
12338 *
12339 * @note This method doesn't call #i_commit(), so all data remains backed up and
12340 * unsaved.
12341 */
12342void Machine::i_copyFrom(Machine *aThat)
12343{
12344 AssertReturnVoid(!i_isSnapshotMachine());
12345 AssertReturnVoid(aThat->i_isSnapshotMachine());
12346
12347 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12348
12349 mHWData.assignCopy(aThat->mHWData);
12350
12351 // create copies of all shared folders (mHWData after attaching a copy
12352 // contains just references to original objects)
12353 for (HWData::SharedFolderList::iterator
12354 it = mHWData->mSharedFolders.begin();
12355 it != mHWData->mSharedFolders.end();
12356 ++it)
12357 {
12358 ComObjPtr<SharedFolder> folder;
12359 folder.createObject();
12360 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12361 AssertComRC(rc);
12362 *it = folder;
12363 }
12364
12365 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12366 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12367 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12368 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12369 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12370
12371 /* create private copies of all controllers */
12372 mStorageControllers.backup();
12373 mStorageControllers->clear();
12374 for (StorageControllerList::const_iterator
12375 it = aThat->mStorageControllers->begin();
12376 it != aThat->mStorageControllers->end();
12377 ++it)
12378 {
12379 ComObjPtr<StorageController> ctrl;
12380 ctrl.createObject();
12381 ctrl->initCopy(this, *it);
12382 mStorageControllers->push_back(ctrl);
12383 }
12384
12385 /* create private copies of all USB controllers */
12386 mUSBControllers.backup();
12387 mUSBControllers->clear();
12388 for (USBControllerList::const_iterator
12389 it = aThat->mUSBControllers->begin();
12390 it != aThat->mUSBControllers->end();
12391 ++it)
12392 {
12393 ComObjPtr<USBController> ctrl;
12394 ctrl.createObject();
12395 ctrl->initCopy(this, *it);
12396 mUSBControllers->push_back(ctrl);
12397 }
12398
12399 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12400 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12401 {
12402 if (mNetworkAdapters[slot].isNotNull())
12403 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12404 else
12405 {
12406 unconst(mNetworkAdapters[slot]).createObject();
12407 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12408 }
12409 }
12410 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12411 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12412 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12413 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12414}
12415
12416/**
12417 * Returns whether the given storage controller is hotplug capable.
12418 *
12419 * @returns true if the controller supports hotplugging
12420 * false otherwise.
12421 * @param enmCtrlType The controller type to check for.
12422 */
12423bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12424{
12425 ComPtr<ISystemProperties> systemProperties;
12426 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12427 if (FAILED(rc))
12428 return false;
12429
12430 BOOL aHotplugCapable = FALSE;
12431 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12432
12433 return RT_BOOL(aHotplugCapable);
12434}
12435
12436#ifdef VBOX_WITH_RESOURCE_USAGE_API
12437
12438void Machine::i_getDiskList(MediaList &list)
12439{
12440 for (MediumAttachmentList::const_iterator
12441 it = mMediumAttachments->begin();
12442 it != mMediumAttachments->end();
12443 ++it)
12444 {
12445 MediumAttachment *pAttach = *it;
12446 /* just in case */
12447 AssertContinue(pAttach);
12448
12449 AutoCaller localAutoCallerA(pAttach);
12450 if (FAILED(localAutoCallerA.rc())) continue;
12451
12452 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12453
12454 if (pAttach->i_getType() == DeviceType_HardDisk)
12455 list.push_back(pAttach->i_getMedium());
12456 }
12457}
12458
12459void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12460{
12461 AssertReturnVoid(isWriteLockOnCurrentThread());
12462 AssertPtrReturnVoid(aCollector);
12463
12464 pm::CollectorHAL *hal = aCollector->getHAL();
12465 /* Create sub metrics */
12466 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12467 "Percentage of processor time spent in user mode by the VM process.");
12468 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12469 "Percentage of processor time spent in kernel mode by the VM process.");
12470 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12471 "Size of resident portion of VM process in memory.");
12472 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12473 "Actual size of all VM disks combined.");
12474 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12475 "Network receive rate.");
12476 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12477 "Network transmit rate.");
12478 /* Create and register base metrics */
12479 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12480 cpuLoadUser, cpuLoadKernel);
12481 aCollector->registerBaseMetric(cpuLoad);
12482 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12483 ramUsageUsed);
12484 aCollector->registerBaseMetric(ramUsage);
12485 MediaList disks;
12486 i_getDiskList(disks);
12487 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12488 diskUsageUsed);
12489 aCollector->registerBaseMetric(diskUsage);
12490
12491 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12492 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12493 new pm::AggregateAvg()));
12494 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12495 new pm::AggregateMin()));
12496 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12497 new pm::AggregateMax()));
12498 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12499 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12500 new pm::AggregateAvg()));
12501 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12502 new pm::AggregateMin()));
12503 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12504 new pm::AggregateMax()));
12505
12506 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12507 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12508 new pm::AggregateAvg()));
12509 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12510 new pm::AggregateMin()));
12511 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12512 new pm::AggregateMax()));
12513
12514 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12515 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12516 new pm::AggregateAvg()));
12517 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12518 new pm::AggregateMin()));
12519 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12520 new pm::AggregateMax()));
12521
12522
12523 /* Guest metrics collector */
12524 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12525 aCollector->registerGuest(mCollectorGuest);
12526 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12527
12528 /* Create sub metrics */
12529 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12530 "Percentage of processor time spent in user mode as seen by the guest.");
12531 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12532 "Percentage of processor time spent in kernel mode as seen by the guest.");
12533 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12534 "Percentage of processor time spent idling as seen by the guest.");
12535
12536 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12537 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12538 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12539 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12540 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12541 pm::SubMetric *guestMemCache = new pm::SubMetric(
12542 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12543
12544 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12545 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12546
12547 /* Create and register base metrics */
12548 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12549 machineNetRx, machineNetTx);
12550 aCollector->registerBaseMetric(machineNetRate);
12551
12552 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12553 guestLoadUser, guestLoadKernel, guestLoadIdle);
12554 aCollector->registerBaseMetric(guestCpuLoad);
12555
12556 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12557 guestMemTotal, guestMemFree,
12558 guestMemBalloon, guestMemShared,
12559 guestMemCache, guestPagedTotal);
12560 aCollector->registerBaseMetric(guestCpuMem);
12561
12562 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12563 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12564 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12565 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12566
12567 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12568 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12569 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12570 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12571
12572 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12573 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12574 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12575 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12576
12577 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12578 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12579 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12580 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12581
12582 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12583 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12584 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12585 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12586
12587 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12588 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12589 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12590 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12591
12592 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12593 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12594 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12595 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12596
12597 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12598 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12599 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12600 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12601
12602 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12603 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12606
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12608 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12611
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12613 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12616}
12617
12618void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12619{
12620 AssertReturnVoid(isWriteLockOnCurrentThread());
12621
12622 if (aCollector)
12623 {
12624 aCollector->unregisterMetricsFor(aMachine);
12625 aCollector->unregisterBaseMetricsFor(aMachine);
12626 }
12627}
12628
12629#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12630
12631
12632////////////////////////////////////////////////////////////////////////////////
12633
12634DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12635
12636HRESULT SessionMachine::FinalConstruct()
12637{
12638 LogFlowThisFunc(("\n"));
12639
12640 mClientToken = NULL;
12641
12642 return BaseFinalConstruct();
12643}
12644
12645void SessionMachine::FinalRelease()
12646{
12647 LogFlowThisFunc(("\n"));
12648
12649 Assert(!mClientToken);
12650 /* paranoia, should not hang around any more */
12651 if (mClientToken)
12652 {
12653 delete mClientToken;
12654 mClientToken = NULL;
12655 }
12656
12657 uninit(Uninit::Unexpected);
12658
12659 BaseFinalRelease();
12660}
12661
12662/**
12663 * @note Must be called only by Machine::LockMachine() from its own write lock.
12664 */
12665HRESULT SessionMachine::init(Machine *aMachine)
12666{
12667 LogFlowThisFuncEnter();
12668 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12669
12670 AssertReturn(aMachine, E_INVALIDARG);
12671
12672 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12673
12674 /* Enclose the state transition NotReady->InInit->Ready */
12675 AutoInitSpan autoInitSpan(this);
12676 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12677
12678 HRESULT rc = S_OK;
12679
12680 RT_ZERO(mAuthLibCtx);
12681
12682 /* create the machine client token */
12683 try
12684 {
12685 mClientToken = new ClientToken(aMachine, this);
12686 if (!mClientToken->isReady())
12687 {
12688 delete mClientToken;
12689 mClientToken = NULL;
12690 rc = E_FAIL;
12691 }
12692 }
12693 catch (std::bad_alloc &)
12694 {
12695 rc = E_OUTOFMEMORY;
12696 }
12697 if (FAILED(rc))
12698 return rc;
12699
12700 /* memorize the peer Machine */
12701 unconst(mPeer) = aMachine;
12702 /* share the parent pointer */
12703 unconst(mParent) = aMachine->mParent;
12704
12705 /* take the pointers to data to share */
12706 mData.share(aMachine->mData);
12707 mSSData.share(aMachine->mSSData);
12708
12709 mUserData.share(aMachine->mUserData);
12710 mHWData.share(aMachine->mHWData);
12711 mMediumAttachments.share(aMachine->mMediumAttachments);
12712
12713 mStorageControllers.allocate();
12714 for (StorageControllerList::const_iterator
12715 it = aMachine->mStorageControllers->begin();
12716 it != aMachine->mStorageControllers->end();
12717 ++it)
12718 {
12719 ComObjPtr<StorageController> ctl;
12720 ctl.createObject();
12721 ctl->init(this, *it);
12722 mStorageControllers->push_back(ctl);
12723 }
12724
12725 mUSBControllers.allocate();
12726 for (USBControllerList::const_iterator
12727 it = aMachine->mUSBControllers->begin();
12728 it != aMachine->mUSBControllers->end();
12729 ++it)
12730 {
12731 ComObjPtr<USBController> ctl;
12732 ctl.createObject();
12733 ctl->init(this, *it);
12734 mUSBControllers->push_back(ctl);
12735 }
12736
12737 unconst(mBIOSSettings).createObject();
12738 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12739 /* create another VRDEServer object that will be mutable */
12740 unconst(mVRDEServer).createObject();
12741 mVRDEServer->init(this, aMachine->mVRDEServer);
12742 /* create another audio adapter object that will be mutable */
12743 unconst(mAudioAdapter).createObject();
12744 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12745 /* create a list of serial ports that will be mutable */
12746 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12747 {
12748 unconst(mSerialPorts[slot]).createObject();
12749 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12750 }
12751 /* create a list of parallel ports that will be mutable */
12752 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12753 {
12754 unconst(mParallelPorts[slot]).createObject();
12755 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12756 }
12757
12758 /* create another USB device filters object that will be mutable */
12759 unconst(mUSBDeviceFilters).createObject();
12760 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12761
12762 /* create a list of network adapters that will be mutable */
12763 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12764 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12765 {
12766 unconst(mNetworkAdapters[slot]).createObject();
12767 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12768 }
12769
12770 /* create another bandwidth control object that will be mutable */
12771 unconst(mBandwidthControl).createObject();
12772 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12773
12774 /* default is to delete saved state on Saved -> PoweredOff transition */
12775 mRemoveSavedState = true;
12776
12777 /* Confirm a successful initialization when it's the case */
12778 autoInitSpan.setSucceeded();
12779
12780 miNATNetworksStarted = 0;
12781
12782 LogFlowThisFuncLeave();
12783 return rc;
12784}
12785
12786/**
12787 * Uninitializes this session object. If the reason is other than
12788 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12789 * or the client watcher code.
12790 *
12791 * @param aReason uninitialization reason
12792 *
12793 * @note Locks mParent + this object for writing.
12794 */
12795void SessionMachine::uninit(Uninit::Reason aReason)
12796{
12797 LogFlowThisFuncEnter();
12798 LogFlowThisFunc(("reason=%d\n", aReason));
12799
12800 /*
12801 * Strongly reference ourselves to prevent this object deletion after
12802 * mData->mSession.mMachine.setNull() below (which can release the last
12803 * reference and call the destructor). Important: this must be done before
12804 * accessing any members (and before AutoUninitSpan that does it as well).
12805 * This self reference will be released as the very last step on return.
12806 */
12807 ComObjPtr<SessionMachine> selfRef;
12808 if (aReason != Uninit::Unexpected)
12809 selfRef = this;
12810
12811 /* Enclose the state transition Ready->InUninit->NotReady */
12812 AutoUninitSpan autoUninitSpan(this);
12813 if (autoUninitSpan.uninitDone())
12814 {
12815 LogFlowThisFunc(("Already uninitialized\n"));
12816 LogFlowThisFuncLeave();
12817 return;
12818 }
12819
12820 if (autoUninitSpan.initFailed())
12821 {
12822 /* We've been called by init() because it's failed. It's not really
12823 * necessary (nor it's safe) to perform the regular uninit sequence
12824 * below, the following is enough.
12825 */
12826 LogFlowThisFunc(("Initialization failed.\n"));
12827 /* destroy the machine client token */
12828 if (mClientToken)
12829 {
12830 delete mClientToken;
12831 mClientToken = NULL;
12832 }
12833 uninitDataAndChildObjects();
12834 mData.free();
12835 unconst(mParent) = NULL;
12836 unconst(mPeer) = NULL;
12837 LogFlowThisFuncLeave();
12838 return;
12839 }
12840
12841 MachineState_T lastState;
12842 {
12843 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12844 lastState = mData->mMachineState;
12845 }
12846 NOREF(lastState);
12847
12848#ifdef VBOX_WITH_USB
12849 // release all captured USB devices, but do this before requesting the locks below
12850 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12851 {
12852 /* Console::captureUSBDevices() is called in the VM process only after
12853 * setting the machine state to Starting or Restoring.
12854 * Console::detachAllUSBDevices() will be called upon successful
12855 * termination. So, we need to release USB devices only if there was
12856 * an abnormal termination of a running VM.
12857 *
12858 * This is identical to SessionMachine::DetachAllUSBDevices except
12859 * for the aAbnormal argument. */
12860 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12861 AssertComRC(rc);
12862 NOREF(rc);
12863
12864 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12865 if (service)
12866 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12867 }
12868#endif /* VBOX_WITH_USB */
12869
12870 // we need to lock this object in uninit() because the lock is shared
12871 // with mPeer (as well as data we modify below). mParent lock is needed
12872 // by several calls to it.
12873 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12874
12875#ifdef VBOX_WITH_RESOURCE_USAGE_API
12876 /*
12877 * It is safe to call Machine::i_unregisterMetrics() here because
12878 * PerformanceCollector::samplerCallback no longer accesses guest methods
12879 * holding the lock.
12880 */
12881 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12882 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12883 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12884 if (mCollectorGuest)
12885 {
12886 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12887 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12888 mCollectorGuest = NULL;
12889 }
12890#endif
12891
12892 if (aReason == Uninit::Abnormal)
12893 {
12894 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12895
12896 /* reset the state to Aborted */
12897 if (mData->mMachineState != MachineState_Aborted)
12898 i_setMachineState(MachineState_Aborted);
12899 }
12900
12901 // any machine settings modified?
12902 if (mData->flModifications)
12903 {
12904 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12905 i_rollback(false /* aNotify */);
12906 }
12907
12908 mData->mSession.mPID = NIL_RTPROCESS;
12909
12910 if (aReason == Uninit::Unexpected)
12911 {
12912 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12913 * client watcher thread to update the set of machines that have open
12914 * sessions. */
12915 mParent->i_updateClientWatcher();
12916 }
12917
12918 /* uninitialize all remote controls */
12919 if (mData->mSession.mRemoteControls.size())
12920 {
12921 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12922 mData->mSession.mRemoteControls.size()));
12923
12924 /* Always restart a the beginning, since the iterator is invalidated
12925 * by using erase(). */
12926 for (Data::Session::RemoteControlList::iterator
12927 it = mData->mSession.mRemoteControls.begin();
12928 it != mData->mSession.mRemoteControls.end();
12929 it = mData->mSession.mRemoteControls.begin())
12930 {
12931 ComPtr<IInternalSessionControl> pControl = *it;
12932 mData->mSession.mRemoteControls.erase(it);
12933 multilock.release();
12934 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12935 HRESULT rc = pControl->Uninitialize();
12936 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12937 if (FAILED(rc))
12938 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12939 multilock.acquire();
12940 }
12941 mData->mSession.mRemoteControls.clear();
12942 }
12943
12944 /* Remove all references to the NAT network service. The service will stop
12945 * if all references (also from other VMs) are removed. */
12946 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12947 {
12948 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12949 {
12950 BOOL enabled;
12951 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12952 if ( FAILED(hrc)
12953 || !enabled)
12954 continue;
12955
12956 NetworkAttachmentType_T type;
12957 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12958 if ( SUCCEEDED(hrc)
12959 && type == NetworkAttachmentType_NATNetwork)
12960 {
12961 Bstr name;
12962 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12963 if (SUCCEEDED(hrc))
12964 {
12965 multilock.release();
12966 Utf8Str strName(name);
12967 LogRel(("VM '%s' stops using NAT network '%s'\n",
12968 mUserData->s.strName.c_str(), strName.c_str()));
12969 mParent->i_natNetworkRefDec(strName);
12970 multilock.acquire();
12971 }
12972 }
12973 }
12974 }
12975
12976 /*
12977 * An expected uninitialization can come only from #i_checkForDeath().
12978 * Otherwise it means that something's gone really wrong (for example,
12979 * the Session implementation has released the VirtualBox reference
12980 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12981 * etc). However, it's also possible, that the client releases the IPC
12982 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12983 * but the VirtualBox release event comes first to the server process.
12984 * This case is practically possible, so we should not assert on an
12985 * unexpected uninit, just log a warning.
12986 */
12987
12988 if (aReason == Uninit::Unexpected)
12989 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12990
12991 if (aReason != Uninit::Normal)
12992 {
12993 mData->mSession.mDirectControl.setNull();
12994 }
12995 else
12996 {
12997 /* this must be null here (see #OnSessionEnd()) */
12998 Assert(mData->mSession.mDirectControl.isNull());
12999 Assert(mData->mSession.mState == SessionState_Unlocking);
13000 Assert(!mData->mSession.mProgress.isNull());
13001 }
13002 if (mData->mSession.mProgress)
13003 {
13004 if (aReason == Uninit::Normal)
13005 mData->mSession.mProgress->i_notifyComplete(S_OK);
13006 else
13007 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13008 COM_IIDOF(ISession),
13009 getComponentName(),
13010 tr("The VM session was aborted"));
13011 mData->mSession.mProgress.setNull();
13012 }
13013
13014 if (mConsoleTaskData.mProgress)
13015 {
13016 Assert(aReason == Uninit::Abnormal);
13017 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13018 COM_IIDOF(ISession),
13019 getComponentName(),
13020 tr("The VM session was aborted"));
13021 mConsoleTaskData.mProgress.setNull();
13022 }
13023
13024 /* remove the association between the peer machine and this session machine */
13025 Assert( (SessionMachine*)mData->mSession.mMachine == this
13026 || aReason == Uninit::Unexpected);
13027
13028 /* reset the rest of session data */
13029 mData->mSession.mLockType = LockType_Null;
13030 mData->mSession.mMachine.setNull();
13031 mData->mSession.mState = SessionState_Unlocked;
13032 mData->mSession.mName.setNull();
13033
13034 /* destroy the machine client token before leaving the exclusive lock */
13035 if (mClientToken)
13036 {
13037 delete mClientToken;
13038 mClientToken = NULL;
13039 }
13040
13041 /* fire an event */
13042 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13043
13044 uninitDataAndChildObjects();
13045
13046 /* free the essential data structure last */
13047 mData.free();
13048
13049 /* release the exclusive lock before setting the below two to NULL */
13050 multilock.release();
13051
13052 unconst(mParent) = NULL;
13053 unconst(mPeer) = NULL;
13054
13055 AuthLibUnload(&mAuthLibCtx);
13056
13057 LogFlowThisFuncLeave();
13058}
13059
13060// util::Lockable interface
13061////////////////////////////////////////////////////////////////////////////////
13062
13063/**
13064 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13065 * with the primary Machine instance (mPeer).
13066 */
13067RWLockHandle *SessionMachine::lockHandle() const
13068{
13069 AssertReturn(mPeer != NULL, NULL);
13070 return mPeer->lockHandle();
13071}
13072
13073// IInternalMachineControl methods
13074////////////////////////////////////////////////////////////////////////////////
13075
13076/**
13077 * Passes collected guest statistics to performance collector object
13078 */
13079HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13080 ULONG aCpuKernel, ULONG aCpuIdle,
13081 ULONG aMemTotal, ULONG aMemFree,
13082 ULONG aMemBalloon, ULONG aMemShared,
13083 ULONG aMemCache, ULONG aPageTotal,
13084 ULONG aAllocVMM, ULONG aFreeVMM,
13085 ULONG aBalloonedVMM, ULONG aSharedVMM,
13086 ULONG aVmNetRx, ULONG aVmNetTx)
13087{
13088#ifdef VBOX_WITH_RESOURCE_USAGE_API
13089 if (mCollectorGuest)
13090 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13091 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13092 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13093 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13094
13095 return S_OK;
13096#else
13097 NOREF(aValidStats);
13098 NOREF(aCpuUser);
13099 NOREF(aCpuKernel);
13100 NOREF(aCpuIdle);
13101 NOREF(aMemTotal);
13102 NOREF(aMemFree);
13103 NOREF(aMemBalloon);
13104 NOREF(aMemShared);
13105 NOREF(aMemCache);
13106 NOREF(aPageTotal);
13107 NOREF(aAllocVMM);
13108 NOREF(aFreeVMM);
13109 NOREF(aBalloonedVMM);
13110 NOREF(aSharedVMM);
13111 NOREF(aVmNetRx);
13112 NOREF(aVmNetTx);
13113 return E_NOTIMPL;
13114#endif
13115}
13116
13117////////////////////////////////////////////////////////////////////////////////
13118//
13119// SessionMachine task records
13120//
13121////////////////////////////////////////////////////////////////////////////////
13122
13123/**
13124 * Task record for saving the machine state.
13125 */
13126class SessionMachine::SaveStateTask
13127 : public Machine::Task
13128{
13129public:
13130 SaveStateTask(SessionMachine *m,
13131 Progress *p,
13132 const Utf8Str &t,
13133 Reason_T enmReason,
13134 const Utf8Str &strStateFilePath)
13135 : Task(m, p, t),
13136 m_enmReason(enmReason),
13137 m_strStateFilePath(strStateFilePath)
13138 {}
13139
13140private:
13141 void handler()
13142 {
13143 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13144 }
13145
13146 Reason_T m_enmReason;
13147 Utf8Str m_strStateFilePath;
13148
13149 friend class SessionMachine;
13150};
13151
13152/**
13153 * Task thread implementation for SessionMachine::SaveState(), called from
13154 * SessionMachine::taskHandler().
13155 *
13156 * @note Locks this object for writing.
13157 *
13158 * @param task
13159 * @return
13160 */
13161void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13162{
13163 LogFlowThisFuncEnter();
13164
13165 AutoCaller autoCaller(this);
13166 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13167 if (FAILED(autoCaller.rc()))
13168 {
13169 /* we might have been uninitialized because the session was accidentally
13170 * closed by the client, so don't assert */
13171 HRESULT rc = setError(E_FAIL,
13172 tr("The session has been accidentally closed"));
13173 task.m_pProgress->i_notifyComplete(rc);
13174 LogFlowThisFuncLeave();
13175 return;
13176 }
13177
13178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13179
13180 HRESULT rc = S_OK;
13181
13182 try
13183 {
13184 ComPtr<IInternalSessionControl> directControl;
13185 if (mData->mSession.mLockType == LockType_VM)
13186 directControl = mData->mSession.mDirectControl;
13187 if (directControl.isNull())
13188 throw setError(VBOX_E_INVALID_VM_STATE,
13189 tr("Trying to save state without a running VM"));
13190 alock.release();
13191 BOOL fSuspendedBySave;
13192 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13193 Assert(!fSuspendedBySave);
13194 alock.acquire();
13195
13196 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13197 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13198 throw E_FAIL);
13199
13200 if (SUCCEEDED(rc))
13201 {
13202 mSSData->strStateFilePath = task.m_strStateFilePath;
13203
13204 /* save all VM settings */
13205 rc = i_saveSettings(NULL);
13206 // no need to check whether VirtualBox.xml needs saving also since
13207 // we can't have a name change pending at this point
13208 }
13209 else
13210 {
13211 // On failure, set the state to the state we had at the beginning.
13212 i_setMachineState(task.m_machineStateBackup);
13213 i_updateMachineStateOnClient();
13214
13215 // Delete the saved state file (might have been already created).
13216 // No need to check whether this is shared with a snapshot here
13217 // because we certainly created a fresh saved state file here.
13218 RTFileDelete(task.m_strStateFilePath.c_str());
13219 }
13220 }
13221 catch (HRESULT aRC) { rc = aRC; }
13222
13223 task.m_pProgress->i_notifyComplete(rc);
13224
13225 LogFlowThisFuncLeave();
13226}
13227
13228/**
13229 * @note Locks this object for writing.
13230 */
13231HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13232{
13233 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13234}
13235
13236HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13237{
13238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13239
13240 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13241 if (FAILED(rc)) return rc;
13242
13243 if ( mData->mMachineState != MachineState_Running
13244 && mData->mMachineState != MachineState_Paused
13245 )
13246 return setError(VBOX_E_INVALID_VM_STATE,
13247 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13248 Global::stringifyMachineState(mData->mMachineState));
13249
13250 ComObjPtr<Progress> pProgress;
13251 pProgress.createObject();
13252 rc = pProgress->init(i_getVirtualBox(),
13253 static_cast<IMachine *>(this) /* aInitiator */,
13254 tr("Saving the execution state of the virtual machine"),
13255 FALSE /* aCancelable */);
13256 if (FAILED(rc))
13257 return rc;
13258
13259 Utf8Str strStateFilePath;
13260 i_composeSavedStateFilename(strStateFilePath);
13261
13262 /* create and start the task on a separate thread (note that it will not
13263 * start working until we release alock) */
13264 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13265 rc = pTask->createThread();
13266 if (FAILED(rc))
13267 return rc;
13268
13269 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13270 i_setMachineState(MachineState_Saving);
13271 i_updateMachineStateOnClient();
13272
13273 pProgress.queryInterfaceTo(aProgress.asOutParam());
13274
13275 return S_OK;
13276}
13277
13278/**
13279 * @note Locks this object for writing.
13280 */
13281HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13282{
13283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13284
13285 HRESULT rc = i_checkStateDependency(MutableStateDep);
13286 if (FAILED(rc)) return rc;
13287
13288 if ( mData->mMachineState != MachineState_PoweredOff
13289 && mData->mMachineState != MachineState_Teleported
13290 && mData->mMachineState != MachineState_Aborted
13291 )
13292 return setError(VBOX_E_INVALID_VM_STATE,
13293 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13294 Global::stringifyMachineState(mData->mMachineState));
13295
13296 com::Utf8Str stateFilePathFull;
13297 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13298 if (RT_FAILURE(vrc))
13299 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13300 tr("Invalid saved state file path '%s' (%Rrc)"),
13301 aSavedStateFile.c_str(),
13302 vrc);
13303
13304 mSSData->strStateFilePath = stateFilePathFull;
13305
13306 /* The below i_setMachineState() will detect the state transition and will
13307 * update the settings file */
13308
13309 return i_setMachineState(MachineState_Saved);
13310}
13311
13312/**
13313 * @note Locks this object for writing.
13314 */
13315HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13316{
13317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13318
13319 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13320 if (FAILED(rc)) return rc;
13321
13322 if (mData->mMachineState != MachineState_Saved)
13323 return setError(VBOX_E_INVALID_VM_STATE,
13324 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13325 Global::stringifyMachineState(mData->mMachineState));
13326
13327 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13328
13329 /*
13330 * Saved -> PoweredOff transition will be detected in the SessionMachine
13331 * and properly handled.
13332 */
13333 rc = i_setMachineState(MachineState_PoweredOff);
13334 return rc;
13335}
13336
13337
13338/**
13339 * @note Locks the same as #i_setMachineState() does.
13340 */
13341HRESULT SessionMachine::updateState(MachineState_T aState)
13342{
13343 return i_setMachineState(aState);
13344}
13345
13346/**
13347 * @note Locks this object for writing.
13348 */
13349HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13350{
13351 IProgress *pProgress(aProgress);
13352
13353 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13354
13355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13356
13357 if (mData->mSession.mState != SessionState_Locked)
13358 return VBOX_E_INVALID_OBJECT_STATE;
13359
13360 if (!mData->mSession.mProgress.isNull())
13361 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13362
13363 /* If we didn't reference the NAT network service yet, add a reference to
13364 * force a start */
13365 if (miNATNetworksStarted < 1)
13366 {
13367 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13368 {
13369 BOOL enabled;
13370 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13371 if ( FAILED(hrc)
13372 || !enabled)
13373 continue;
13374
13375 NetworkAttachmentType_T type;
13376 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13377 if ( SUCCEEDED(hrc)
13378 && type == NetworkAttachmentType_NATNetwork)
13379 {
13380 Bstr name;
13381 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13382 if (SUCCEEDED(hrc))
13383 {
13384 Utf8Str strName(name);
13385 LogRel(("VM '%s' starts using NAT network '%s'\n",
13386 mUserData->s.strName.c_str(), strName.c_str()));
13387 mPeer->lockHandle()->unlockWrite();
13388 mParent->i_natNetworkRefInc(strName);
13389#ifdef RT_LOCK_STRICT
13390 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13391#else
13392 mPeer->lockHandle()->lockWrite();
13393#endif
13394 }
13395 }
13396 }
13397 miNATNetworksStarted++;
13398 }
13399
13400 LogFlowThisFunc(("returns S_OK.\n"));
13401 return S_OK;
13402}
13403
13404/**
13405 * @note Locks this object for writing.
13406 */
13407HRESULT SessionMachine::endPowerUp(LONG aResult)
13408{
13409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13410
13411 if (mData->mSession.mState != SessionState_Locked)
13412 return VBOX_E_INVALID_OBJECT_STATE;
13413
13414 /* Finalize the LaunchVMProcess progress object. */
13415 if (mData->mSession.mProgress)
13416 {
13417 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13418 mData->mSession.mProgress.setNull();
13419 }
13420
13421 if (SUCCEEDED((HRESULT)aResult))
13422 {
13423#ifdef VBOX_WITH_RESOURCE_USAGE_API
13424 /* The VM has been powered up successfully, so it makes sense
13425 * now to offer the performance metrics for a running machine
13426 * object. Doing it earlier wouldn't be safe. */
13427 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13428 mData->mSession.mPID);
13429#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13430 }
13431
13432 return S_OK;
13433}
13434
13435/**
13436 * @note Locks this object for writing.
13437 */
13438HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13439{
13440 LogFlowThisFuncEnter();
13441
13442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13443
13444 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13445 E_FAIL);
13446
13447 /* create a progress object to track operation completion */
13448 ComObjPtr<Progress> pProgress;
13449 pProgress.createObject();
13450 pProgress->init(i_getVirtualBox(),
13451 static_cast<IMachine *>(this) /* aInitiator */,
13452 tr("Stopping the virtual machine"),
13453 FALSE /* aCancelable */);
13454
13455 /* fill in the console task data */
13456 mConsoleTaskData.mLastState = mData->mMachineState;
13457 mConsoleTaskData.mProgress = pProgress;
13458
13459 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13460 i_setMachineState(MachineState_Stopping);
13461
13462 pProgress.queryInterfaceTo(aProgress.asOutParam());
13463
13464 return S_OK;
13465}
13466
13467/**
13468 * @note Locks this object for writing.
13469 */
13470HRESULT SessionMachine::endPoweringDown(LONG aResult,
13471 const com::Utf8Str &aErrMsg)
13472{
13473 LogFlowThisFuncEnter();
13474
13475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13476
13477 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13478 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13479 && mConsoleTaskData.mLastState != MachineState_Null,
13480 E_FAIL);
13481
13482 /*
13483 * On failure, set the state to the state we had when BeginPoweringDown()
13484 * was called (this is expected by Console::PowerDown() and the associated
13485 * task). On success the VM process already changed the state to
13486 * MachineState_PoweredOff, so no need to do anything.
13487 */
13488 if (FAILED(aResult))
13489 i_setMachineState(mConsoleTaskData.mLastState);
13490
13491 /* notify the progress object about operation completion */
13492 Assert(mConsoleTaskData.mProgress);
13493 if (SUCCEEDED(aResult))
13494 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13495 else
13496 {
13497 if (aErrMsg.length())
13498 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13499 COM_IIDOF(ISession),
13500 getComponentName(),
13501 aErrMsg.c_str());
13502 else
13503 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13504 }
13505
13506 /* clear out the temporary saved state data */
13507 mConsoleTaskData.mLastState = MachineState_Null;
13508 mConsoleTaskData.mProgress.setNull();
13509
13510 LogFlowThisFuncLeave();
13511 return S_OK;
13512}
13513
13514
13515/**
13516 * Goes through the USB filters of the given machine to see if the given
13517 * device matches any filter or not.
13518 *
13519 * @note Locks the same as USBController::hasMatchingFilter() does.
13520 */
13521HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13522 BOOL *aMatched,
13523 ULONG *aMaskedInterfaces)
13524{
13525 LogFlowThisFunc(("\n"));
13526
13527#ifdef VBOX_WITH_USB
13528 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13529#else
13530 NOREF(aDevice);
13531 NOREF(aMaskedInterfaces);
13532 *aMatched = FALSE;
13533#endif
13534
13535 return S_OK;
13536}
13537
13538/**
13539 * @note Locks the same as Host::captureUSBDevice() does.
13540 */
13541HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13542{
13543 LogFlowThisFunc(("\n"));
13544
13545#ifdef VBOX_WITH_USB
13546 /* if captureDeviceForVM() fails, it must have set extended error info */
13547 clearError();
13548 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13549 if (FAILED(rc)) return rc;
13550
13551 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13552 AssertReturn(service, E_FAIL);
13553 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13554#else
13555 NOREF(aId);
13556 return E_NOTIMPL;
13557#endif
13558}
13559
13560/**
13561 * @note Locks the same as Host::detachUSBDevice() does.
13562 */
13563HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13564 BOOL aDone)
13565{
13566 LogFlowThisFunc(("\n"));
13567
13568#ifdef VBOX_WITH_USB
13569 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13570 AssertReturn(service, E_FAIL);
13571 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13572#else
13573 NOREF(aId);
13574 NOREF(aDone);
13575 return E_NOTIMPL;
13576#endif
13577}
13578
13579/**
13580 * Inserts all machine filters to the USB proxy service and then calls
13581 * Host::autoCaptureUSBDevices().
13582 *
13583 * Called by Console from the VM process upon VM startup.
13584 *
13585 * @note Locks what called methods lock.
13586 */
13587HRESULT SessionMachine::autoCaptureUSBDevices()
13588{
13589 LogFlowThisFunc(("\n"));
13590
13591#ifdef VBOX_WITH_USB
13592 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13593 AssertComRC(rc);
13594 NOREF(rc);
13595
13596 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13597 AssertReturn(service, E_FAIL);
13598 return service->autoCaptureDevicesForVM(this);
13599#else
13600 return S_OK;
13601#endif
13602}
13603
13604/**
13605 * Removes all machine filters from the USB proxy service and then calls
13606 * Host::detachAllUSBDevices().
13607 *
13608 * Called by Console from the VM process upon normal VM termination or by
13609 * SessionMachine::uninit() upon abnormal VM termination (from under the
13610 * Machine/SessionMachine lock).
13611 *
13612 * @note Locks what called methods lock.
13613 */
13614HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13615{
13616 LogFlowThisFunc(("\n"));
13617
13618#ifdef VBOX_WITH_USB
13619 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13620 AssertComRC(rc);
13621 NOREF(rc);
13622
13623 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13624 AssertReturn(service, E_FAIL);
13625 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13626#else
13627 NOREF(aDone);
13628 return S_OK;
13629#endif
13630}
13631
13632/**
13633 * @note Locks this object for writing.
13634 */
13635HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13636 ComPtr<IProgress> &aProgress)
13637{
13638 LogFlowThisFuncEnter();
13639
13640 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13641 /*
13642 * We don't assert below because it might happen that a non-direct session
13643 * informs us it is closed right after we've been uninitialized -- it's ok.
13644 */
13645
13646 /* get IInternalSessionControl interface */
13647 ComPtr<IInternalSessionControl> control(aSession);
13648
13649 ComAssertRet(!control.isNull(), E_INVALIDARG);
13650
13651 /* Creating a Progress object requires the VirtualBox lock, and
13652 * thus locking it here is required by the lock order rules. */
13653 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13654
13655 if (control == mData->mSession.mDirectControl)
13656 {
13657 /* The direct session is being normally closed by the client process
13658 * ----------------------------------------------------------------- */
13659
13660 /* go to the closing state (essential for all open*Session() calls and
13661 * for #i_checkForDeath()) */
13662 Assert(mData->mSession.mState == SessionState_Locked);
13663 mData->mSession.mState = SessionState_Unlocking;
13664
13665 /* set direct control to NULL to release the remote instance */
13666 mData->mSession.mDirectControl.setNull();
13667 LogFlowThisFunc(("Direct control is set to NULL\n"));
13668
13669 if (mData->mSession.mProgress)
13670 {
13671 /* finalize the progress, someone might wait if a frontend
13672 * closes the session before powering on the VM. */
13673 mData->mSession.mProgress->notifyComplete(E_FAIL,
13674 COM_IIDOF(ISession),
13675 getComponentName(),
13676 tr("The VM session was closed before any attempt to power it on"));
13677 mData->mSession.mProgress.setNull();
13678 }
13679
13680 /* Create the progress object the client will use to wait until
13681 * #i_checkForDeath() is called to uninitialize this session object after
13682 * it releases the IPC semaphore.
13683 * Note! Because we're "reusing" mProgress here, this must be a proxy
13684 * object just like for LaunchVMProcess. */
13685 Assert(mData->mSession.mProgress.isNull());
13686 ComObjPtr<ProgressProxy> progress;
13687 progress.createObject();
13688 ComPtr<IUnknown> pPeer(mPeer);
13689 progress->init(mParent, pPeer,
13690 Bstr(tr("Closing session")).raw(),
13691 FALSE /* aCancelable */);
13692 progress.queryInterfaceTo(aProgress.asOutParam());
13693 mData->mSession.mProgress = progress;
13694 }
13695 else
13696 {
13697 /* the remote session is being normally closed */
13698 bool found = false;
13699 for (Data::Session::RemoteControlList::iterator
13700 it = mData->mSession.mRemoteControls.begin();
13701 it != mData->mSession.mRemoteControls.end();
13702 ++it)
13703 {
13704 if (control == *it)
13705 {
13706 found = true;
13707 // This MUST be erase(it), not remove(*it) as the latter
13708 // triggers a very nasty use after free due to the place where
13709 // the value "lives".
13710 mData->mSession.mRemoteControls.erase(it);
13711 break;
13712 }
13713 }
13714 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13715 E_INVALIDARG);
13716 }
13717
13718 /* signal the client watcher thread, because the client is going away */
13719 mParent->i_updateClientWatcher();
13720
13721 LogFlowThisFuncLeave();
13722 return S_OK;
13723}
13724
13725HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13726 std::vector<com::Utf8Str> &aValues,
13727 std::vector<LONG64> &aTimestamps,
13728 std::vector<com::Utf8Str> &aFlags)
13729{
13730 LogFlowThisFunc(("\n"));
13731
13732#ifdef VBOX_WITH_GUEST_PROPS
13733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13734
13735 size_t cEntries = mHWData->mGuestProperties.size();
13736 aNames.resize(cEntries);
13737 aValues.resize(cEntries);
13738 aTimestamps.resize(cEntries);
13739 aFlags.resize(cEntries);
13740
13741 size_t i = 0;
13742 for (HWData::GuestPropertyMap::const_iterator
13743 it = mHWData->mGuestProperties.begin();
13744 it != mHWData->mGuestProperties.end();
13745 ++it, ++i)
13746 {
13747 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13748 aNames[i] = it->first;
13749 aValues[i] = it->second.strValue;
13750 aTimestamps[i] = it->second.mTimestamp;
13751
13752 /* If it is NULL, keep it NULL. */
13753 if (it->second.mFlags)
13754 {
13755 GuestPropWriteFlags(it->second.mFlags, szFlags);
13756 aFlags[i] = szFlags;
13757 }
13758 else
13759 aFlags[i] = "";
13760 }
13761 return S_OK;
13762#else
13763 ReturnComNotImplemented();
13764#endif
13765}
13766
13767HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13768 const com::Utf8Str &aValue,
13769 LONG64 aTimestamp,
13770 const com::Utf8Str &aFlags)
13771{
13772 LogFlowThisFunc(("\n"));
13773
13774#ifdef VBOX_WITH_GUEST_PROPS
13775 try
13776 {
13777 /*
13778 * Convert input up front.
13779 */
13780 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13781 if (aFlags.length())
13782 {
13783 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13784 AssertRCReturn(vrc, E_INVALIDARG);
13785 }
13786
13787 /*
13788 * Now grab the object lock, validate the state and do the update.
13789 */
13790
13791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13792
13793 if (!Global::IsOnline(mData->mMachineState))
13794 {
13795 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13796 VBOX_E_INVALID_VM_STATE);
13797 }
13798
13799 i_setModified(IsModified_MachineData);
13800 mHWData.backup();
13801
13802 bool fDelete = !aValue.length();
13803 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13804 if (it != mHWData->mGuestProperties.end())
13805 {
13806 if (!fDelete)
13807 {
13808 it->second.strValue = aValue;
13809 it->second.mTimestamp = aTimestamp;
13810 it->second.mFlags = fFlags;
13811 }
13812 else
13813 mHWData->mGuestProperties.erase(it);
13814
13815 mData->mGuestPropertiesModified = TRUE;
13816 }
13817 else if (!fDelete)
13818 {
13819 HWData::GuestProperty prop;
13820 prop.strValue = aValue;
13821 prop.mTimestamp = aTimestamp;
13822 prop.mFlags = fFlags;
13823
13824 mHWData->mGuestProperties[aName] = prop;
13825 mData->mGuestPropertiesModified = TRUE;
13826 }
13827
13828 alock.release();
13829
13830 mParent->i_onGuestPropertyChange(mData->mUuid,
13831 Bstr(aName).raw(),
13832 Bstr(aValue).raw(),
13833 Bstr(aFlags).raw());
13834 }
13835 catch (...)
13836 {
13837 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13838 }
13839 return S_OK;
13840#else
13841 ReturnComNotImplemented();
13842#endif
13843}
13844
13845
13846HRESULT SessionMachine::lockMedia()
13847{
13848 AutoMultiWriteLock2 alock(this->lockHandle(),
13849 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13850
13851 AssertReturn( mData->mMachineState == MachineState_Starting
13852 || mData->mMachineState == MachineState_Restoring
13853 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13854
13855 clearError();
13856 alock.release();
13857 return i_lockMedia();
13858}
13859
13860HRESULT SessionMachine::unlockMedia()
13861{
13862 HRESULT hrc = i_unlockMedia();
13863 return hrc;
13864}
13865
13866HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13867 ComPtr<IMediumAttachment> &aNewAttachment)
13868{
13869 // request the host lock first, since might be calling Host methods for getting host drives;
13870 // next, protect the media tree all the while we're in here, as well as our member variables
13871 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13872 this->lockHandle(),
13873 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13874
13875 IMediumAttachment *iAttach = aAttachment;
13876 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13877
13878 Utf8Str ctrlName;
13879 LONG lPort;
13880 LONG lDevice;
13881 bool fTempEject;
13882 {
13883 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13884
13885 /* Need to query the details first, as the IMediumAttachment reference
13886 * might be to the original settings, which we are going to change. */
13887 ctrlName = pAttach->i_getControllerName();
13888 lPort = pAttach->i_getPort();
13889 lDevice = pAttach->i_getDevice();
13890 fTempEject = pAttach->i_getTempEject();
13891 }
13892
13893 if (!fTempEject)
13894 {
13895 /* Remember previously mounted medium. The medium before taking the
13896 * backup is not necessarily the same thing. */
13897 ComObjPtr<Medium> oldmedium;
13898 oldmedium = pAttach->i_getMedium();
13899
13900 i_setModified(IsModified_Storage);
13901 mMediumAttachments.backup();
13902
13903 // The backup operation makes the pAttach reference point to the
13904 // old settings. Re-get the correct reference.
13905 pAttach = i_findAttachment(*mMediumAttachments.data(),
13906 ctrlName,
13907 lPort,
13908 lDevice);
13909
13910 {
13911 AutoCaller autoAttachCaller(this);
13912 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13913
13914 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13915 if (!oldmedium.isNull())
13916 oldmedium->i_removeBackReference(mData->mUuid);
13917
13918 pAttach->i_updateMedium(NULL);
13919 pAttach->i_updateEjected();
13920 }
13921
13922 i_setModified(IsModified_Storage);
13923 }
13924 else
13925 {
13926 {
13927 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13928 pAttach->i_updateEjected();
13929 }
13930 }
13931
13932 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13933
13934 return S_OK;
13935}
13936
13937HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13938 com::Utf8Str &aResult)
13939{
13940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13941
13942 HRESULT hr = S_OK;
13943
13944 if (!mAuthLibCtx.hAuthLibrary)
13945 {
13946 /* Load the external authentication library. */
13947 Bstr authLibrary;
13948 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13949
13950 Utf8Str filename = authLibrary;
13951
13952 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13953 if (RT_FAILURE(vrc))
13954 hr = setErrorBoth(E_FAIL, vrc,
13955 tr("Could not load the external authentication library '%s' (%Rrc)"),
13956 filename.c_str(), vrc);
13957 }
13958
13959 /* The auth library might need the machine lock. */
13960 alock.release();
13961
13962 if (FAILED(hr))
13963 return hr;
13964
13965 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13966 {
13967 enum VRDEAuthParams
13968 {
13969 parmUuid = 1,
13970 parmGuestJudgement,
13971 parmUser,
13972 parmPassword,
13973 parmDomain,
13974 parmClientId
13975 };
13976
13977 AuthResult result = AuthResultAccessDenied;
13978
13979 Guid uuid(aAuthParams[parmUuid]);
13980 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13981 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13982
13983 result = AuthLibAuthenticate(&mAuthLibCtx,
13984 uuid.raw(), guestJudgement,
13985 aAuthParams[parmUser].c_str(),
13986 aAuthParams[parmPassword].c_str(),
13987 aAuthParams[parmDomain].c_str(),
13988 u32ClientId);
13989
13990 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13991 size_t cbPassword = aAuthParams[parmPassword].length();
13992 if (cbPassword)
13993 {
13994 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13995 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13996 }
13997
13998 if (result == AuthResultAccessGranted)
13999 aResult = "granted";
14000 else
14001 aResult = "denied";
14002
14003 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14004 aAuthParams[parmUser].c_str(), aResult.c_str()));
14005 }
14006 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14007 {
14008 enum VRDEAuthDisconnectParams
14009 {
14010 parmUuid = 1,
14011 parmClientId
14012 };
14013
14014 Guid uuid(aAuthParams[parmUuid]);
14015 uint32_t u32ClientId = 0;
14016 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14017 }
14018 else
14019 {
14020 hr = E_INVALIDARG;
14021 }
14022
14023 return hr;
14024}
14025
14026// public methods only for internal purposes
14027/////////////////////////////////////////////////////////////////////////////
14028
14029#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14030/**
14031 * Called from the client watcher thread to check for expected or unexpected
14032 * death of the client process that has a direct session to this machine.
14033 *
14034 * On Win32 and on OS/2, this method is called only when we've got the
14035 * mutex (i.e. the client has either died or terminated normally) so it always
14036 * returns @c true (the client is terminated, the session machine is
14037 * uninitialized).
14038 *
14039 * On other platforms, the method returns @c true if the client process has
14040 * terminated normally or abnormally and the session machine was uninitialized,
14041 * and @c false if the client process is still alive.
14042 *
14043 * @note Locks this object for writing.
14044 */
14045bool SessionMachine::i_checkForDeath()
14046{
14047 Uninit::Reason reason;
14048 bool terminated = false;
14049
14050 /* Enclose autoCaller with a block because calling uninit() from under it
14051 * will deadlock. */
14052 {
14053 AutoCaller autoCaller(this);
14054 if (!autoCaller.isOk())
14055 {
14056 /* return true if not ready, to cause the client watcher to exclude
14057 * the corresponding session from watching */
14058 LogFlowThisFunc(("Already uninitialized!\n"));
14059 return true;
14060 }
14061
14062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14063
14064 /* Determine the reason of death: if the session state is Closing here,
14065 * everything is fine. Otherwise it means that the client did not call
14066 * OnSessionEnd() before it released the IPC semaphore. This may happen
14067 * either because the client process has abnormally terminated, or
14068 * because it simply forgot to call ISession::Close() before exiting. We
14069 * threat the latter also as an abnormal termination (see
14070 * Session::uninit() for details). */
14071 reason = mData->mSession.mState == SessionState_Unlocking ?
14072 Uninit::Normal :
14073 Uninit::Abnormal;
14074
14075 if (mClientToken)
14076 terminated = mClientToken->release();
14077 } /* AutoCaller block */
14078
14079 if (terminated)
14080 uninit(reason);
14081
14082 return terminated;
14083}
14084
14085void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14086{
14087 LogFlowThisFunc(("\n"));
14088
14089 strTokenId.setNull();
14090
14091 AutoCaller autoCaller(this);
14092 AssertComRCReturnVoid(autoCaller.rc());
14093
14094 Assert(mClientToken);
14095 if (mClientToken)
14096 mClientToken->getId(strTokenId);
14097}
14098#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14099IToken *SessionMachine::i_getToken()
14100{
14101 LogFlowThisFunc(("\n"));
14102
14103 AutoCaller autoCaller(this);
14104 AssertComRCReturn(autoCaller.rc(), NULL);
14105
14106 Assert(mClientToken);
14107 if (mClientToken)
14108 return mClientToken->getToken();
14109 else
14110 return NULL;
14111}
14112#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14113
14114Machine::ClientToken *SessionMachine::i_getClientToken()
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.rc(), NULL);
14120
14121 return mClientToken;
14122}
14123
14124
14125/**
14126 * @note Locks this object for reading.
14127 */
14128HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14129{
14130 LogFlowThisFunc(("\n"));
14131
14132 AutoCaller autoCaller(this);
14133 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14134
14135 ComPtr<IInternalSessionControl> directControl;
14136 {
14137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14138 if (mData->mSession.mLockType == LockType_VM)
14139 directControl = mData->mSession.mDirectControl;
14140 }
14141
14142 /* ignore notifications sent after #OnSessionEnd() is called */
14143 if (!directControl)
14144 return S_OK;
14145
14146 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14147}
14148
14149/**
14150 * @note Locks this object for reading.
14151 */
14152HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14153 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14154 IN_BSTR aGuestIp, LONG aGuestPort)
14155{
14156 LogFlowThisFunc(("\n"));
14157
14158 AutoCaller autoCaller(this);
14159 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14160
14161 ComPtr<IInternalSessionControl> directControl;
14162 {
14163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14164 if (mData->mSession.mLockType == LockType_VM)
14165 directControl = mData->mSession.mDirectControl;
14166 }
14167
14168 /* ignore notifications sent after #OnSessionEnd() is called */
14169 if (!directControl)
14170 return S_OK;
14171 /*
14172 * instead acting like callback we ask IVirtualBox deliver corresponding event
14173 */
14174
14175 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14176 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14177 return S_OK;
14178}
14179
14180/**
14181 * @note Locks this object for reading.
14182 */
14183HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14184{
14185 LogFlowThisFunc(("\n"));
14186
14187 AutoCaller autoCaller(this);
14188 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14189
14190 ComPtr<IInternalSessionControl> directControl;
14191 {
14192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14193 if (mData->mSession.mLockType == LockType_VM)
14194 directControl = mData->mSession.mDirectControl;
14195 }
14196
14197 /* ignore notifications sent after #OnSessionEnd() is called */
14198 if (!directControl)
14199 return S_OK;
14200
14201 return directControl->OnAudioAdapterChange(audioAdapter);
14202}
14203
14204/**
14205 * @note Locks this object for reading.
14206 */
14207HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14208{
14209 LogFlowThisFunc(("\n"));
14210
14211 AutoCaller autoCaller(this);
14212 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14213
14214 ComPtr<IInternalSessionControl> directControl;
14215 {
14216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14217 if (mData->mSession.mLockType == LockType_VM)
14218 directControl = mData->mSession.mDirectControl;
14219 }
14220
14221 /* ignore notifications sent after #OnSessionEnd() is called */
14222 if (!directControl)
14223 return S_OK;
14224
14225 return directControl->OnSerialPortChange(serialPort);
14226}
14227
14228/**
14229 * @note Locks this object for reading.
14230 */
14231HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14232{
14233 LogFlowThisFunc(("\n"));
14234
14235 AutoCaller autoCaller(this);
14236 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14237
14238 ComPtr<IInternalSessionControl> directControl;
14239 {
14240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14241 if (mData->mSession.mLockType == LockType_VM)
14242 directControl = mData->mSession.mDirectControl;
14243 }
14244
14245 /* ignore notifications sent after #OnSessionEnd() is called */
14246 if (!directControl)
14247 return S_OK;
14248
14249 return directControl->OnParallelPortChange(parallelPort);
14250}
14251
14252/**
14253 * @note Locks this object for reading.
14254 */
14255HRESULT SessionMachine::i_onStorageControllerChange()
14256{
14257 LogFlowThisFunc(("\n"));
14258
14259 AutoCaller autoCaller(this);
14260 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14261
14262 ComPtr<IInternalSessionControl> directControl;
14263 {
14264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14265 if (mData->mSession.mLockType == LockType_VM)
14266 directControl = mData->mSession.mDirectControl;
14267 }
14268
14269 /* ignore notifications sent after #OnSessionEnd() is called */
14270 if (!directControl)
14271 return S_OK;
14272
14273 return directControl->OnStorageControllerChange();
14274}
14275
14276/**
14277 * @note Locks this object for reading.
14278 */
14279HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14280{
14281 LogFlowThisFunc(("\n"));
14282
14283 AutoCaller autoCaller(this);
14284 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14285
14286 ComPtr<IInternalSessionControl> directControl;
14287 {
14288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14289 if (mData->mSession.mLockType == LockType_VM)
14290 directControl = mData->mSession.mDirectControl;
14291 }
14292
14293 /* ignore notifications sent after #OnSessionEnd() is called */
14294 if (!directControl)
14295 return S_OK;
14296
14297 return directControl->OnMediumChange(aAttachment, aForce);
14298}
14299
14300/**
14301 * @note Locks this object for reading.
14302 */
14303HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14304{
14305 LogFlowThisFunc(("\n"));
14306
14307 AutoCaller autoCaller(this);
14308 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14309
14310 ComPtr<IInternalSessionControl> directControl;
14311 {
14312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14313 if (mData->mSession.mLockType == LockType_VM)
14314 directControl = mData->mSession.mDirectControl;
14315 }
14316
14317 /* ignore notifications sent after #OnSessionEnd() is called */
14318 if (!directControl)
14319 return S_OK;
14320
14321 return directControl->OnCPUChange(aCPU, aRemove);
14322}
14323
14324HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14325{
14326 LogFlowThisFunc(("\n"));
14327
14328 AutoCaller autoCaller(this);
14329 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14330
14331 ComPtr<IInternalSessionControl> directControl;
14332 {
14333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14334 if (mData->mSession.mLockType == LockType_VM)
14335 directControl = mData->mSession.mDirectControl;
14336 }
14337
14338 /* ignore notifications sent after #OnSessionEnd() is called */
14339 if (!directControl)
14340 return S_OK;
14341
14342 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14343}
14344
14345/**
14346 * @note Locks this object for reading.
14347 */
14348HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14349{
14350 LogFlowThisFunc(("\n"));
14351
14352 AutoCaller autoCaller(this);
14353 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14354
14355 ComPtr<IInternalSessionControl> directControl;
14356 {
14357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14358 if (mData->mSession.mLockType == LockType_VM)
14359 directControl = mData->mSession.mDirectControl;
14360 }
14361
14362 /* ignore notifications sent after #OnSessionEnd() is called */
14363 if (!directControl)
14364 return S_OK;
14365
14366 return directControl->OnVRDEServerChange(aRestart);
14367}
14368
14369/**
14370 * @note Locks this object for reading.
14371 */
14372HRESULT SessionMachine::i_onVideoCaptureChange()
14373{
14374 LogFlowThisFunc(("\n"));
14375
14376 AutoCaller autoCaller(this);
14377 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14378
14379 ComPtr<IInternalSessionControl> directControl;
14380 {
14381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14382 if (mData->mSession.mLockType == LockType_VM)
14383 directControl = mData->mSession.mDirectControl;
14384 }
14385
14386 /* ignore notifications sent after #OnSessionEnd() is called */
14387 if (!directControl)
14388 return S_OK;
14389
14390 return directControl->OnVideoCaptureChange();
14391}
14392
14393/**
14394 * @note Locks this object for reading.
14395 */
14396HRESULT SessionMachine::i_onUSBControllerChange()
14397{
14398 LogFlowThisFunc(("\n"));
14399
14400 AutoCaller autoCaller(this);
14401 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14402
14403 ComPtr<IInternalSessionControl> directControl;
14404 {
14405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14406 if (mData->mSession.mLockType == LockType_VM)
14407 directControl = mData->mSession.mDirectControl;
14408 }
14409
14410 /* ignore notifications sent after #OnSessionEnd() is called */
14411 if (!directControl)
14412 return S_OK;
14413
14414 return directControl->OnUSBControllerChange();
14415}
14416
14417/**
14418 * @note Locks this object for reading.
14419 */
14420HRESULT SessionMachine::i_onSharedFolderChange()
14421{
14422 LogFlowThisFunc(("\n"));
14423
14424 AutoCaller autoCaller(this);
14425 AssertComRCReturnRC(autoCaller.rc());
14426
14427 ComPtr<IInternalSessionControl> directControl;
14428 {
14429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14430 if (mData->mSession.mLockType == LockType_VM)
14431 directControl = mData->mSession.mDirectControl;
14432 }
14433
14434 /* ignore notifications sent after #OnSessionEnd() is called */
14435 if (!directControl)
14436 return S_OK;
14437
14438 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14439}
14440
14441/**
14442 * @note Locks this object for reading.
14443 */
14444HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14445{
14446 LogFlowThisFunc(("\n"));
14447
14448 AutoCaller autoCaller(this);
14449 AssertComRCReturnRC(autoCaller.rc());
14450
14451 ComPtr<IInternalSessionControl> directControl;
14452 {
14453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14454 if (mData->mSession.mLockType == LockType_VM)
14455 directControl = mData->mSession.mDirectControl;
14456 }
14457
14458 /* ignore notifications sent after #OnSessionEnd() is called */
14459 if (!directControl)
14460 return S_OK;
14461
14462 return directControl->OnClipboardModeChange(aClipboardMode);
14463}
14464
14465/**
14466 * @note Locks this object for reading.
14467 */
14468HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14469{
14470 LogFlowThisFunc(("\n"));
14471
14472 AutoCaller autoCaller(this);
14473 AssertComRCReturnRC(autoCaller.rc());
14474
14475 ComPtr<IInternalSessionControl> directControl;
14476 {
14477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14478 if (mData->mSession.mLockType == LockType_VM)
14479 directControl = mData->mSession.mDirectControl;
14480 }
14481
14482 /* ignore notifications sent after #OnSessionEnd() is called */
14483 if (!directControl)
14484 return S_OK;
14485
14486 return directControl->OnDnDModeChange(aDnDMode);
14487}
14488
14489/**
14490 * @note Locks this object for reading.
14491 */
14492HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14493{
14494 LogFlowThisFunc(("\n"));
14495
14496 AutoCaller autoCaller(this);
14497 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14498
14499 ComPtr<IInternalSessionControl> directControl;
14500 {
14501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14502 if (mData->mSession.mLockType == LockType_VM)
14503 directControl = mData->mSession.mDirectControl;
14504 }
14505
14506 /* ignore notifications sent after #OnSessionEnd() is called */
14507 if (!directControl)
14508 return S_OK;
14509
14510 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14511}
14512
14513/**
14514 * @note Locks this object for reading.
14515 */
14516HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14517{
14518 LogFlowThisFunc(("\n"));
14519
14520 AutoCaller autoCaller(this);
14521 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14522
14523 ComPtr<IInternalSessionControl> directControl;
14524 {
14525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14526 if (mData->mSession.mLockType == LockType_VM)
14527 directControl = mData->mSession.mDirectControl;
14528 }
14529
14530 /* ignore notifications sent after #OnSessionEnd() is called */
14531 if (!directControl)
14532 return S_OK;
14533
14534 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14535}
14536
14537/**
14538 * Returns @c true if this machine's USB controller reports it has a matching
14539 * filter for the given USB device and @c false otherwise.
14540 *
14541 * @note locks this object for reading.
14542 */
14543bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14544{
14545 AutoCaller autoCaller(this);
14546 /* silently return if not ready -- this method may be called after the
14547 * direct machine session has been called */
14548 if (!autoCaller.isOk())
14549 return false;
14550
14551#ifdef VBOX_WITH_USB
14552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14553
14554 switch (mData->mMachineState)
14555 {
14556 case MachineState_Starting:
14557 case MachineState_Restoring:
14558 case MachineState_TeleportingIn:
14559 case MachineState_Paused:
14560 case MachineState_Running:
14561 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14562 * elsewhere... */
14563 alock.release();
14564 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14565 default: break;
14566 }
14567#else
14568 NOREF(aDevice);
14569 NOREF(aMaskedIfs);
14570#endif
14571 return false;
14572}
14573
14574/**
14575 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14576 */
14577HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14578 IVirtualBoxErrorInfo *aError,
14579 ULONG aMaskedIfs,
14580 const com::Utf8Str &aCaptureFilename)
14581{
14582 LogFlowThisFunc(("\n"));
14583
14584 AutoCaller autoCaller(this);
14585
14586 /* This notification may happen after the machine object has been
14587 * uninitialized (the session was closed), so don't assert. */
14588 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14589
14590 ComPtr<IInternalSessionControl> directControl;
14591 {
14592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14593 if (mData->mSession.mLockType == LockType_VM)
14594 directControl = mData->mSession.mDirectControl;
14595 }
14596
14597 /* fail on notifications sent after #OnSessionEnd() is called, it is
14598 * expected by the caller */
14599 if (!directControl)
14600 return E_FAIL;
14601
14602 /* No locks should be held at this point. */
14603 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14604 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14605
14606 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14607}
14608
14609/**
14610 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14611 */
14612HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14613 IVirtualBoxErrorInfo *aError)
14614{
14615 LogFlowThisFunc(("\n"));
14616
14617 AutoCaller autoCaller(this);
14618
14619 /* This notification may happen after the machine object has been
14620 * uninitialized (the session was closed), so don't assert. */
14621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14622
14623 ComPtr<IInternalSessionControl> directControl;
14624 {
14625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14626 if (mData->mSession.mLockType == LockType_VM)
14627 directControl = mData->mSession.mDirectControl;
14628 }
14629
14630 /* fail on notifications sent after #OnSessionEnd() is called, it is
14631 * expected by the caller */
14632 if (!directControl)
14633 return E_FAIL;
14634
14635 /* No locks should be held at this point. */
14636 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14637 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14638
14639 return directControl->OnUSBDeviceDetach(aId, aError);
14640}
14641
14642// protected methods
14643/////////////////////////////////////////////////////////////////////////////
14644
14645/**
14646 * Deletes the given file if it is no longer in use by either the current machine state
14647 * (if the machine is "saved") or any of the machine's snapshots.
14648 *
14649 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14650 * but is different for each SnapshotMachine. When calling this, the order of calling this
14651 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14652 * is therefore critical. I know, it's all rather messy.
14653 *
14654 * @param strStateFile
14655 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14656 * the test for whether the saved state file is in use.
14657 */
14658void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14659 Snapshot *pSnapshotToIgnore)
14660{
14661 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14662 if ( (strStateFile.isNotEmpty())
14663 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14664 )
14665 // ... and it must also not be shared with other snapshots
14666 if ( !mData->mFirstSnapshot
14667 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14668 // this checks the SnapshotMachine's state file paths
14669 )
14670 RTFileDelete(strStateFile.c_str());
14671}
14672
14673/**
14674 * Locks the attached media.
14675 *
14676 * All attached hard disks are locked for writing and DVD/floppy are locked for
14677 * reading. Parents of attached hard disks (if any) are locked for reading.
14678 *
14679 * This method also performs accessibility check of all media it locks: if some
14680 * media is inaccessible, the method will return a failure and a bunch of
14681 * extended error info objects per each inaccessible medium.
14682 *
14683 * Note that this method is atomic: if it returns a success, all media are
14684 * locked as described above; on failure no media is locked at all (all
14685 * succeeded individual locks will be undone).
14686 *
14687 * The caller is responsible for doing the necessary state sanity checks.
14688 *
14689 * The locks made by this method must be undone by calling #unlockMedia() when
14690 * no more needed.
14691 */
14692HRESULT SessionMachine::i_lockMedia()
14693{
14694 AutoCaller autoCaller(this);
14695 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14696
14697 AutoMultiWriteLock2 alock(this->lockHandle(),
14698 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14699
14700 /* bail out if trying to lock things with already set up locking */
14701 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14702
14703 MultiResult mrc(S_OK);
14704
14705 /* Collect locking information for all medium objects attached to the VM. */
14706 for (MediumAttachmentList::const_iterator
14707 it = mMediumAttachments->begin();
14708 it != mMediumAttachments->end();
14709 ++it)
14710 {
14711 MediumAttachment *pAtt = *it;
14712 DeviceType_T devType = pAtt->i_getType();
14713 Medium *pMedium = pAtt->i_getMedium();
14714
14715 MediumLockList *pMediumLockList(new MediumLockList());
14716 // There can be attachments without a medium (floppy/dvd), and thus
14717 // it's impossible to create a medium lock list. It still makes sense
14718 // to have the empty medium lock list in the map in case a medium is
14719 // attached later.
14720 if (pMedium != NULL)
14721 {
14722 MediumType_T mediumType = pMedium->i_getType();
14723 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14724 || mediumType == MediumType_Shareable;
14725 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14726
14727 alock.release();
14728 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14729 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14730 false /* fMediumLockWriteAll */,
14731 NULL,
14732 *pMediumLockList);
14733 alock.acquire();
14734 if (FAILED(mrc))
14735 {
14736 delete pMediumLockList;
14737 mData->mSession.mLockedMedia.Clear();
14738 break;
14739 }
14740 }
14741
14742 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14743 if (FAILED(rc))
14744 {
14745 mData->mSession.mLockedMedia.Clear();
14746 mrc = setError(rc,
14747 tr("Collecting locking information for all attached media failed"));
14748 break;
14749 }
14750 }
14751
14752 if (SUCCEEDED(mrc))
14753 {
14754 /* Now lock all media. If this fails, nothing is locked. */
14755 alock.release();
14756 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14757 alock.acquire();
14758 if (FAILED(rc))
14759 {
14760 mrc = setError(rc,
14761 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14762 }
14763 }
14764
14765 return mrc;
14766}
14767
14768/**
14769 * Undoes the locks made by by #lockMedia().
14770 */
14771HRESULT SessionMachine::i_unlockMedia()
14772{
14773 AutoCaller autoCaller(this);
14774 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14775
14776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14777
14778 /* we may be holding important error info on the current thread;
14779 * preserve it */
14780 ErrorInfoKeeper eik;
14781
14782 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14783 AssertComRC(rc);
14784 return rc;
14785}
14786
14787/**
14788 * Helper to change the machine state (reimplementation).
14789 *
14790 * @note Locks this object for writing.
14791 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14792 * it can cause crashes in random places due to unexpectedly committing
14793 * the current settings. The caller is responsible for that. The call
14794 * to saveStateSettings is fine, because this method does not commit.
14795 */
14796HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14797{
14798 LogFlowThisFuncEnter();
14799 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14800
14801 AutoCaller autoCaller(this);
14802 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14803
14804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14805
14806 MachineState_T oldMachineState = mData->mMachineState;
14807
14808 AssertMsgReturn(oldMachineState != aMachineState,
14809 ("oldMachineState=%s, aMachineState=%s\n",
14810 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14811 E_FAIL);
14812
14813 HRESULT rc = S_OK;
14814
14815 int stsFlags = 0;
14816 bool deleteSavedState = false;
14817
14818 /* detect some state transitions */
14819
14820 if ( ( oldMachineState == MachineState_Saved
14821 && aMachineState == MachineState_Restoring)
14822 || ( ( oldMachineState == MachineState_PoweredOff
14823 || oldMachineState == MachineState_Teleported
14824 || oldMachineState == MachineState_Aborted
14825 )
14826 && ( aMachineState == MachineState_TeleportingIn
14827 || aMachineState == MachineState_Starting
14828 )
14829 )
14830 )
14831 {
14832 /* The EMT thread is about to start */
14833
14834 /* Nothing to do here for now... */
14835
14836 /// @todo NEWMEDIA don't let mDVDDrive and other children
14837 /// change anything when in the Starting/Restoring state
14838 }
14839 else if ( ( oldMachineState == MachineState_Running
14840 || oldMachineState == MachineState_Paused
14841 || oldMachineState == MachineState_Teleporting
14842 || oldMachineState == MachineState_OnlineSnapshotting
14843 || oldMachineState == MachineState_LiveSnapshotting
14844 || oldMachineState == MachineState_Stuck
14845 || oldMachineState == MachineState_Starting
14846 || oldMachineState == MachineState_Stopping
14847 || oldMachineState == MachineState_Saving
14848 || oldMachineState == MachineState_Restoring
14849 || oldMachineState == MachineState_TeleportingPausedVM
14850 || oldMachineState == MachineState_TeleportingIn
14851 )
14852 && ( aMachineState == MachineState_PoweredOff
14853 || aMachineState == MachineState_Saved
14854 || aMachineState == MachineState_Teleported
14855 || aMachineState == MachineState_Aborted
14856 )
14857 )
14858 {
14859 /* The EMT thread has just stopped, unlock attached media. Note that as
14860 * opposed to locking that is done from Console, we do unlocking here
14861 * because the VM process may have aborted before having a chance to
14862 * properly unlock all media it locked. */
14863
14864 unlockMedia();
14865 }
14866
14867 if (oldMachineState == MachineState_Restoring)
14868 {
14869 if (aMachineState != MachineState_Saved)
14870 {
14871 /*
14872 * delete the saved state file once the machine has finished
14873 * restoring from it (note that Console sets the state from
14874 * Restoring to Saved if the VM couldn't restore successfully,
14875 * to give the user an ability to fix an error and retry --
14876 * we keep the saved state file in this case)
14877 */
14878 deleteSavedState = true;
14879 }
14880 }
14881 else if ( oldMachineState == MachineState_Saved
14882 && ( aMachineState == MachineState_PoweredOff
14883 || aMachineState == MachineState_Aborted
14884 || aMachineState == MachineState_Teleported
14885 )
14886 )
14887 {
14888 /*
14889 * delete the saved state after SessionMachine::ForgetSavedState() is called
14890 * or if the VM process (owning a direct VM session) crashed while the
14891 * VM was Saved
14892 */
14893
14894 /// @todo (dmik)
14895 // Not sure that deleting the saved state file just because of the
14896 // client death before it attempted to restore the VM is a good
14897 // thing. But when it crashes we need to go to the Aborted state
14898 // which cannot have the saved state file associated... The only
14899 // way to fix this is to make the Aborted condition not a VM state
14900 // but a bool flag: i.e., when a crash occurs, set it to true and
14901 // change the state to PoweredOff or Saved depending on the
14902 // saved state presence.
14903
14904 deleteSavedState = true;
14905 mData->mCurrentStateModified = TRUE;
14906 stsFlags |= SaveSTS_CurStateModified;
14907 }
14908
14909 if ( aMachineState == MachineState_Starting
14910 || aMachineState == MachineState_Restoring
14911 || aMachineState == MachineState_TeleportingIn
14912 )
14913 {
14914 /* set the current state modified flag to indicate that the current
14915 * state is no more identical to the state in the
14916 * current snapshot */
14917 if (!mData->mCurrentSnapshot.isNull())
14918 {
14919 mData->mCurrentStateModified = TRUE;
14920 stsFlags |= SaveSTS_CurStateModified;
14921 }
14922 }
14923
14924 if (deleteSavedState)
14925 {
14926 if (mRemoveSavedState)
14927 {
14928 Assert(!mSSData->strStateFilePath.isEmpty());
14929
14930 // it is safe to delete the saved state file if ...
14931 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14932 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14933 // ... none of the snapshots share the saved state file
14934 )
14935 RTFileDelete(mSSData->strStateFilePath.c_str());
14936 }
14937
14938 mSSData->strStateFilePath.setNull();
14939 stsFlags |= SaveSTS_StateFilePath;
14940 }
14941
14942 /* redirect to the underlying peer machine */
14943 mPeer->i_setMachineState(aMachineState);
14944
14945 if ( oldMachineState != MachineState_RestoringSnapshot
14946 && ( aMachineState == MachineState_PoweredOff
14947 || aMachineState == MachineState_Teleported
14948 || aMachineState == MachineState_Aborted
14949 || aMachineState == MachineState_Saved))
14950 {
14951 /* the machine has stopped execution
14952 * (or the saved state file was adopted) */
14953 stsFlags |= SaveSTS_StateTimeStamp;
14954 }
14955
14956 if ( ( oldMachineState == MachineState_PoweredOff
14957 || oldMachineState == MachineState_Aborted
14958 || oldMachineState == MachineState_Teleported
14959 )
14960 && aMachineState == MachineState_Saved)
14961 {
14962 /* the saved state file was adopted */
14963 Assert(!mSSData->strStateFilePath.isEmpty());
14964 stsFlags |= SaveSTS_StateFilePath;
14965 }
14966
14967#ifdef VBOX_WITH_GUEST_PROPS
14968 if ( aMachineState == MachineState_PoweredOff
14969 || aMachineState == MachineState_Aborted
14970 || aMachineState == MachineState_Teleported)
14971 {
14972 /* Make sure any transient guest properties get removed from the
14973 * property store on shutdown. */
14974 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14975
14976 /* remove it from the settings representation */
14977 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14978 for (settings::GuestPropertiesList::iterator
14979 it = llGuestProperties.begin();
14980 it != llGuestProperties.end();
14981 /*nothing*/)
14982 {
14983 const settings::GuestProperty &prop = *it;
14984 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14985 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14986 {
14987 it = llGuestProperties.erase(it);
14988 fNeedsSaving = true;
14989 }
14990 else
14991 {
14992 ++it;
14993 }
14994 }
14995
14996 /* Additionally remove it from the HWData representation. Required to
14997 * keep everything in sync, as this is what the API keeps using. */
14998 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14999 for (HWData::GuestPropertyMap::iterator
15000 it = llHWGuestProperties.begin();
15001 it != llHWGuestProperties.end();
15002 /*nothing*/)
15003 {
15004 uint32_t fFlags = it->second.mFlags;
15005 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15006 {
15007 /* iterator where we need to continue after the erase call
15008 * (C++03 is a fact still, and it doesn't return the iterator
15009 * which would allow continuing) */
15010 HWData::GuestPropertyMap::iterator it2 = it;
15011 ++it2;
15012 llHWGuestProperties.erase(it);
15013 it = it2;
15014 fNeedsSaving = true;
15015 }
15016 else
15017 {
15018 ++it;
15019 }
15020 }
15021
15022 if (fNeedsSaving)
15023 {
15024 mData->mCurrentStateModified = TRUE;
15025 stsFlags |= SaveSTS_CurStateModified;
15026 }
15027 }
15028#endif /* VBOX_WITH_GUEST_PROPS */
15029
15030 rc = i_saveStateSettings(stsFlags);
15031
15032 if ( ( oldMachineState != MachineState_PoweredOff
15033 && oldMachineState != MachineState_Aborted
15034 && oldMachineState != MachineState_Teleported
15035 )
15036 && ( aMachineState == MachineState_PoweredOff
15037 || aMachineState == MachineState_Aborted
15038 || aMachineState == MachineState_Teleported
15039 )
15040 )
15041 {
15042 /* we've been shut down for any reason */
15043 /* no special action so far */
15044 }
15045
15046 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15047 LogFlowThisFuncLeave();
15048 return rc;
15049}
15050
15051/**
15052 * Sends the current machine state value to the VM process.
15053 *
15054 * @note Locks this object for reading, then calls a client process.
15055 */
15056HRESULT SessionMachine::i_updateMachineStateOnClient()
15057{
15058 AutoCaller autoCaller(this);
15059 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15060
15061 ComPtr<IInternalSessionControl> directControl;
15062 {
15063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15064 AssertReturn(!!mData, E_FAIL);
15065 if (mData->mSession.mLockType == LockType_VM)
15066 directControl = mData->mSession.mDirectControl;
15067
15068 /* directControl may be already set to NULL here in #OnSessionEnd()
15069 * called too early by the direct session process while there is still
15070 * some operation (like deleting the snapshot) in progress. The client
15071 * process in this case is waiting inside Session::close() for the
15072 * "end session" process object to complete, while #uninit() called by
15073 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15074 * operation to complete. For now, we accept this inconsistent behavior
15075 * and simply do nothing here. */
15076
15077 if (mData->mSession.mState == SessionState_Unlocking)
15078 return S_OK;
15079 }
15080
15081 /* ignore notifications sent after #OnSessionEnd() is called */
15082 if (!directControl)
15083 return S_OK;
15084
15085 return directControl->UpdateMachineState(mData->mMachineState);
15086}
15087
15088
15089/*static*/
15090HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15091{
15092 va_list args;
15093 va_start(args, pcszMsg);
15094 HRESULT rc = setErrorInternal(aResultCode,
15095 getStaticClassIID(),
15096 getStaticComponentName(),
15097 Utf8Str(pcszMsg, args),
15098 false /* aWarning */,
15099 true /* aLogIt */);
15100 va_end(args);
15101 return rc;
15102}
15103
15104
15105HRESULT Machine::updateState(MachineState_T aState)
15106{
15107 NOREF(aState);
15108 ReturnComNotImplemented();
15109}
15110
15111HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15112{
15113 NOREF(aProgress);
15114 ReturnComNotImplemented();
15115}
15116
15117HRESULT Machine::endPowerUp(LONG aResult)
15118{
15119 NOREF(aResult);
15120 ReturnComNotImplemented();
15121}
15122
15123HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15124{
15125 NOREF(aProgress);
15126 ReturnComNotImplemented();
15127}
15128
15129HRESULT Machine::endPoweringDown(LONG aResult,
15130 const com::Utf8Str &aErrMsg)
15131{
15132 NOREF(aResult);
15133 NOREF(aErrMsg);
15134 ReturnComNotImplemented();
15135}
15136
15137HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15138 BOOL *aMatched,
15139 ULONG *aMaskedInterfaces)
15140{
15141 NOREF(aDevice);
15142 NOREF(aMatched);
15143 NOREF(aMaskedInterfaces);
15144 ReturnComNotImplemented();
15145
15146}
15147
15148HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15149{
15150 NOREF(aId); NOREF(aCaptureFilename);
15151 ReturnComNotImplemented();
15152}
15153
15154HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15155 BOOL aDone)
15156{
15157 NOREF(aId);
15158 NOREF(aDone);
15159 ReturnComNotImplemented();
15160}
15161
15162HRESULT Machine::autoCaptureUSBDevices()
15163{
15164 ReturnComNotImplemented();
15165}
15166
15167HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15168{
15169 NOREF(aDone);
15170 ReturnComNotImplemented();
15171}
15172
15173HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15174 ComPtr<IProgress> &aProgress)
15175{
15176 NOREF(aSession);
15177 NOREF(aProgress);
15178 ReturnComNotImplemented();
15179}
15180
15181HRESULT Machine::finishOnlineMergeMedium()
15182{
15183 ReturnComNotImplemented();
15184}
15185
15186HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15187 std::vector<com::Utf8Str> &aValues,
15188 std::vector<LONG64> &aTimestamps,
15189 std::vector<com::Utf8Str> &aFlags)
15190{
15191 NOREF(aNames);
15192 NOREF(aValues);
15193 NOREF(aTimestamps);
15194 NOREF(aFlags);
15195 ReturnComNotImplemented();
15196}
15197
15198HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15199 const com::Utf8Str &aValue,
15200 LONG64 aTimestamp,
15201 const com::Utf8Str &aFlags)
15202{
15203 NOREF(aName);
15204 NOREF(aValue);
15205 NOREF(aTimestamp);
15206 NOREF(aFlags);
15207 ReturnComNotImplemented();
15208}
15209
15210HRESULT Machine::lockMedia()
15211{
15212 ReturnComNotImplemented();
15213}
15214
15215HRESULT Machine::unlockMedia()
15216{
15217 ReturnComNotImplemented();
15218}
15219
15220HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15221 ComPtr<IMediumAttachment> &aNewAttachment)
15222{
15223 NOREF(aAttachment);
15224 NOREF(aNewAttachment);
15225 ReturnComNotImplemented();
15226}
15227
15228HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15229 ULONG aCpuUser,
15230 ULONG aCpuKernel,
15231 ULONG aCpuIdle,
15232 ULONG aMemTotal,
15233 ULONG aMemFree,
15234 ULONG aMemBalloon,
15235 ULONG aMemShared,
15236 ULONG aMemCache,
15237 ULONG aPagedTotal,
15238 ULONG aMemAllocTotal,
15239 ULONG aMemFreeTotal,
15240 ULONG aMemBalloonTotal,
15241 ULONG aMemSharedTotal,
15242 ULONG aVmNetRx,
15243 ULONG aVmNetTx)
15244{
15245 NOREF(aValidStats);
15246 NOREF(aCpuUser);
15247 NOREF(aCpuKernel);
15248 NOREF(aCpuIdle);
15249 NOREF(aMemTotal);
15250 NOREF(aMemFree);
15251 NOREF(aMemBalloon);
15252 NOREF(aMemShared);
15253 NOREF(aMemCache);
15254 NOREF(aPagedTotal);
15255 NOREF(aMemAllocTotal);
15256 NOREF(aMemFreeTotal);
15257 NOREF(aMemBalloonTotal);
15258 NOREF(aMemSharedTotal);
15259 NOREF(aVmNetRx);
15260 NOREF(aVmNetTx);
15261 ReturnComNotImplemented();
15262}
15263
15264HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15265 com::Utf8Str &aResult)
15266{
15267 NOREF(aAuthParams);
15268 NOREF(aResult);
15269 ReturnComNotImplemented();
15270}
15271
15272HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15273{
15274 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15275
15276 AutoCaller autoCaller(this);
15277 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15278
15279 HRESULT rc = S_OK;
15280
15281 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15282 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15283 rc = getUSBDeviceFilters(usbDeviceFilters);
15284 if (FAILED(rc)) return rc;
15285
15286 NOREF(aFlags);
15287 com::Utf8Str osTypeId;
15288 ComObjPtr<GuestOSType> osType = NULL;
15289
15290 /* Get the guest os type as a string from the VB. */
15291 rc = getOSTypeId(osTypeId);
15292 if (FAILED(rc)) return rc;
15293
15294 /* Get the os type obj that coresponds, can be used to get
15295 * the defaults for this guest OS. */
15296 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15297 if (FAILED(rc)) return rc;
15298
15299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15300
15301 /* Let the OS type select 64-bit ness. */
15302 mHWData->mLongMode = osType->i_is64Bit()
15303 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15304
15305 /* Apply network adapters defaults */
15306 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15307 mNetworkAdapters[slot]->i_applyDefaults(osType);
15308
15309 /* Apply serial port defaults */
15310 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15311 mSerialPorts[slot]->i_applyDefaults(osType);
15312
15313 /* Apply parallel port defaults - not OS dependent*/
15314 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15315 mParallelPorts[slot]->i_applyDefaults();
15316
15317
15318 /* Let the OS type enable the X2APIC */
15319 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15320
15321 /* This one covers IOAPICEnabled. */
15322 mBIOSSettings->i_applyDefaults(osType);
15323
15324 /* Initialize default BIOS settings here */
15325 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15326 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15327
15328 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15329 if (FAILED(rc)) return rc;
15330
15331 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15332 if (FAILED(rc)) return rc;
15333
15334 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15335 if (FAILED(rc)) return rc;
15336
15337 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15338 if (FAILED(rc)) return rc;
15339
15340 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15341 if (FAILED(rc)) return rc;
15342
15343 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15344 if (FAILED(rc)) return rc;
15345
15346 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15347 if (FAILED(rc)) return rc;
15348
15349 BOOL mRTCUseUTC;
15350 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15351 if (FAILED(rc)) return rc;
15352
15353 setRTCUseUTC(mRTCUseUTC);
15354 if (FAILED(rc)) return rc;
15355
15356 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15357 if (FAILED(rc)) return rc;
15358
15359 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15360 if (FAILED(rc)) return rc;
15361
15362 /* Audio stuff. */
15363 AudioCodecType_T audioCodec;
15364 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15365 if (FAILED(rc)) return rc;
15366
15367 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15368 if (FAILED(rc)) return rc;
15369
15370 AudioControllerType_T audioController;
15371 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15372 if (FAILED(rc)) return rc;
15373
15374 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15375 if (FAILED(rc)) return rc;
15376
15377 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15378 if (FAILED(rc)) return rc;
15379
15380 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15381 if (FAILED(rc)) return rc;
15382
15383 /* Storage Controllers */
15384 StorageControllerType_T hdStorageControllerType;
15385 StorageBus_T hdStorageBusType;
15386 StorageControllerType_T dvdStorageControllerType;
15387 StorageBus_T dvdStorageBusType;
15388 BOOL recommendedFloppy;
15389 ComPtr<IStorageController> floppyController;
15390 ComPtr<IStorageController> hdController;
15391 ComPtr<IStorageController> dvdController;
15392 Utf8Str strFloppyName, strDVDName, strHDName;
15393
15394 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15395 strFloppyName = Bstr("Floppy 1").raw();
15396 strDVDName = Bstr("DVD 1").raw();
15397 strHDName = Bstr("HDD 1").raw();
15398
15399 /* Floppy recommended? add one. */
15400 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15401 if (FAILED(rc)) return rc;
15402 if (recommendedFloppy)
15403 {
15404 rc = addStorageController(strFloppyName,
15405 StorageBus_Floppy,
15406 floppyController);
15407 if (FAILED(rc)) return rc;
15408 }
15409
15410 /* Setup one DVD storage controller. */
15411 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15412 if (FAILED(rc)) return rc;
15413
15414 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15415 if (FAILED(rc)) return rc;
15416
15417 rc = addStorageController(strDVDName,
15418 dvdStorageBusType,
15419 dvdController);
15420 if (FAILED(rc)) return rc;
15421
15422 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15423 if (FAILED(rc)) return rc;
15424
15425 /* Setup one HDD storage controller. */
15426 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15427 if (FAILED(rc)) return rc;
15428
15429 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15430 if (FAILED(rc)) return rc;
15431
15432 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15433 {
15434 rc = addStorageController(strHDName,
15435 hdStorageBusType,
15436 hdController);
15437 if (FAILED(rc)) return rc;
15438
15439 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15440 if (FAILED(rc)) return rc;
15441 }
15442 else
15443 {
15444 /* The HD controller is the same as DVD: */
15445 hdController = dvdController;
15446 strHDName = Bstr("DVD 1").raw();
15447 }
15448
15449 /* Limit the AHCI port count if it's used because windows has trouble with
15450 * too many ports and other guest (OS X in particular) may take extra long
15451 * boot: */
15452
15453 // pParent = static_cast<Medium*>(aP)
15454 IStorageController *temp = hdController;
15455 ComObjPtr<StorageController> storageController;
15456 storageController = static_cast<StorageController *>(temp);
15457
15458 // tempHDController = aHDController;
15459 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15460 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15461 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15462 storageController->COMSETTER(PortCount)(1);
15463
15464 /* USB stuff */
15465
15466 bool ohciEnabled = false;
15467
15468 ComPtr<IUSBController> usbController;
15469 BOOL recommendedUSB3;
15470 BOOL recommendedUSB;
15471 BOOL usbProxyAvailable;
15472
15473 getUSBProxyAvailable(&usbProxyAvailable);
15474 if (FAILED(rc)) return rc;
15475
15476 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15477 if (FAILED(rc)) return rc;
15478 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15479 if (FAILED(rc)) return rc;
15480
15481 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15482 {
15483 /* USB 3.0 is only available if the proper ExtPack is installed. */
15484 ExtPackManager *aManager = mParent->i_getExtPackManager();
15485 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15486 {
15487 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15488 if (FAILED(rc)) return rc;
15489
15490 /* xHci includes OHCI */
15491 ohciEnabled = true;
15492 }
15493 }
15494 if ( !ohciEnabled
15495 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15496 {
15497 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15498 if (FAILED(rc)) return rc;
15499 ohciEnabled = true;
15500
15501 /* USB 2.0 is only available if the proper ExtPack is installed.
15502 * Note. Configuring EHCI here and providing messages about
15503 * the missing extpack isn't exactly clean, but it is a
15504 * necessary evil to patch over legacy compatability issues
15505 * introduced by the new distribution model. */
15506 ExtPackManager *manager = mParent->i_getExtPackManager();
15507 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15508 {
15509 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15510 if (FAILED(rc)) return rc;
15511 }
15512 }
15513
15514 /* Set recommended human interface device types: */
15515 BOOL recommendedUSBHID;
15516 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15517 if (FAILED(rc)) return rc;
15518
15519 if (recommendedUSBHID)
15520 {
15521 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15522 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15523 if (!ohciEnabled && !usbDeviceFilters.isNull())
15524 {
15525 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15526 if (FAILED(rc)) return rc;
15527 }
15528 }
15529
15530 BOOL recommendedUSBTablet;
15531 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15532 if (FAILED(rc)) return rc;
15533
15534 if (recommendedUSBTablet)
15535 {
15536 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15537 if (!ohciEnabled && !usbDeviceFilters.isNull())
15538 {
15539 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15540 if (FAILED(rc)) return rc;
15541 }
15542 }
15543 return S_OK;
15544}
15545
15546/* This isn't handled entirely by the wrapper generator yet. */
15547#ifdef VBOX_WITH_XPCOM
15548NS_DECL_CLASSINFO(SessionMachine)
15549NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15550
15551NS_DECL_CLASSINFO(SnapshotMachine)
15552NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15553#endif
Note: See TracBrowser for help on using the repository browser.

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