VirtualBox

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

Last change on this file since 74081 was 73805, checked in by vboxsync, 6 years ago

fixed signed/unsigned mismatch in the comparsion

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 538.5 KB
Line 
1/* $Id: MachineImpl.cpp 73805 2018-08-21 16:40:42Z 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#include <iprt/ctype.h>
72
73#include <VBox/com/array.h>
74#include <VBox/com/list.h>
75
76#include <VBox/err.h>
77#include <VBox/param.h>
78#include <VBox/settings.h>
79#include <VBox/vmm/ssm.h>
80
81#ifdef VBOX_WITH_GUEST_PROPS
82# include <VBox/HostServices/GuestPropertySvc.h>
83# include <VBox/com/array.h>
84#endif
85
86#include "VBox/com/MultiResult.h"
87
88#include <algorithm>
89
90#ifdef VBOX_WITH_DTRACE_R3_MAIN
91# include "dtrace/VBoxAPI.h"
92#endif
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mLockType = LockType_Null;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureRate = 512;
173 mVideoCaptureFPS = 25;
174 mVideoCaptureMaxTime = 0;
175 mVideoCaptureMaxFileSize = 0;
176 mVideoCaptureEnabled = false;
177 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
178 maVideoCaptureScreens[i] = true;
179
180 mHWVirtExEnabled = true;
181 mHWVirtExNestedPagingEnabled = true;
182#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
183 mHWVirtExLargePagesEnabled = true;
184#else
185 /* Not supported on 32 bits hosts. */
186 mHWVirtExLargePagesEnabled = false;
187#endif
188 mHWVirtExVPIDEnabled = true;
189 mHWVirtExUXEnabled = true;
190 mHWVirtExForceEnabled = false;
191 mHWVirtExUseNativeApi = false;
192#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
193 mPAEEnabled = true;
194#else
195 mPAEEnabled = false;
196#endif
197 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
198 mTripleFaultReset = false;
199 mAPIC = true;
200 mX2APIC = false;
201 mIBPBOnVMExit = false;
202 mIBPBOnVMEntry = false;
203 mSpecCtrl = false;
204 mSpecCtrlByHost = false;
205 mNestedHWVirt = false;
206 mHPETEnabled = false;
207 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
208 mCpuIdPortabilityLevel = 0;
209 mCpuProfile = "host";
210
211 /* default boot order: floppy - DVD - HDD */
212 mBootOrder[0] = DeviceType_Floppy;
213 mBootOrder[1] = DeviceType_DVD;
214 mBootOrder[2] = DeviceType_HardDisk;
215 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
216 mBootOrder[i] = DeviceType_Null;
217
218 mClipboardMode = ClipboardMode_Disabled;
219 mDnDMode = DnDMode_Disabled;
220
221 mFirmwareType = FirmwareType_BIOS;
222 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
223 mPointingHIDType = PointingHIDType_PS2Mouse;
224 mChipsetType = ChipsetType_PIIX3;
225 mParavirtProvider = ParavirtProvider_Default;
226 mEmulatedUSBCardReaderEnabled = FALSE;
227
228 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
229 mCPUAttached[i] = false;
230
231 mIOCacheEnabled = true;
232 mIOCacheSize = 5; /* 5MB */
233}
234
235Machine::HWData::~HWData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine() :
247#ifdef VBOX_WITH_RESOURCE_USAGE_API
248 mCollectorGuest(NULL),
249#endif
250 mPeer(NULL),
251 mParent(NULL),
252 mSerialPorts(),
253 mParallelPorts(),
254 uRegistryNeedsSaving(0)
255{}
256
257Machine::~Machine()
258{}
259
260HRESULT Machine::FinalConstruct()
261{
262 LogFlowThisFunc(("\n"));
263 return BaseFinalConstruct();
264}
265
266void Machine::FinalRelease()
267{
268 LogFlowThisFunc(("\n"));
269 uninit();
270 BaseFinalRelease();
271}
272
273/**
274 * Initializes a new machine instance; this init() variant creates a new, empty machine.
275 * This gets called from VirtualBox::CreateMachine().
276 *
277 * @param aParent Associated parent object
278 * @param strConfigFile Local file system path to the VM settings file (can
279 * be relative to the VirtualBox config directory).
280 * @param strName name for the machine
281 * @param llGroups list of groups for the machine
282 * @param strOsType OS Type string (stored as is if aOsType is NULL).
283 * @param aOsType OS Type of this machine or NULL.
284 * @param aId UUID for the new machine.
285 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
286 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
287 * scheme (includes the UUID).
288 *
289 * @return Success indicator. if not S_OK, the machine object is invalid
290 */
291HRESULT Machine::init(VirtualBox *aParent,
292 const Utf8Str &strConfigFile,
293 const Utf8Str &strName,
294 const StringsList &llGroups,
295 const Utf8Str &strOsType,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 if (llGroups.size())
332 mUserData->s.llGroups = llGroups;
333
334 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
335 // the "name sync" flag determines whether the machine directory gets renamed along
336 // with the machine file; say so if the settings file name is the same as the
337 // settings file parent directory (machine directory)
338 mUserData->s.fNameSync = i_isInOwnDir();
339
340 // initialize the default snapshots folder
341 rc = COMSETTER(SnapshotFolder)(NULL);
342 AssertComRC(rc);
343
344 if (aOsType)
345 {
346 /* Store OS type */
347 mUserData->s.strOsType = aOsType->i_id();
348
349 /* Let the OS type select 64-bit ness. */
350 mHWData->mLongMode = aOsType->i_is64Bit()
351 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
352
353 /* Let the OS type enable the X2APIC */
354 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
355 }
356 else if (!strOsType.isEmpty())
357 {
358 /* Store OS type */
359 mUserData->s.strOsType = strOsType;
360
361 /* No guest OS type object. Pick some plausible defaults which the
362 * host can handle. There's no way to know or validate anything. */
363 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
364 mHWData->mX2APIC = false;
365 }
366
367 /* Apply BIOS defaults */
368 mBIOSSettings->i_applyDefaults(aOsType);
369
370 /* Apply network adapters defaults */
371 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
372 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
373
374 /* Apply serial port defaults */
375 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
376 mSerialPorts[slot]->i_applyDefaults(aOsType);
377
378 /* Apply parallel port defaults */
379 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
380 mParallelPorts[slot]->i_applyDefaults();
381
382 /* At this point the changing of the current state modification
383 * flag is allowed. */
384 i_allowStateModification();
385
386 /* commit all changes made during the initialization */
387 i_commit();
388 }
389
390 /* Confirm a successful initialization when it's the case */
391 if (SUCCEEDED(rc))
392 {
393 if (mData->mAccessible)
394 autoInitSpan.setSucceeded();
395 else
396 autoInitSpan.setLimited();
397 }
398
399 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
400 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
401 mData->mRegistered,
402 mData->mAccessible,
403 rc));
404
405 LogFlowThisFuncLeave();
406
407 return rc;
408}
409
410/**
411 * Initializes a new instance with data from machine XML (formerly Init_Registered).
412 * Gets called in two modes:
413 *
414 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
415 * UUID is specified and we mark the machine as "registered";
416 *
417 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
418 * and the machine remains unregistered until RegisterMachine() is called.
419 *
420 * @param aParent Associated parent object
421 * @param strConfigFile Local file system path to the VM settings file (can
422 * be relative to the VirtualBox config directory).
423 * @param aId UUID of the machine or NULL (see above).
424 *
425 * @return Success indicator. if not S_OK, the machine object is invalid
426 */
427HRESULT Machine::initFromSettings(VirtualBox *aParent,
428 const Utf8Str &strConfigFile,
429 const Guid *aId)
430{
431 LogFlowThisFuncEnter();
432 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
433
434 /* Enclose the state transition NotReady->InInit->Ready */
435 AutoInitSpan autoInitSpan(this);
436 AssertReturn(autoInitSpan.isOk(), E_FAIL);
437
438 HRESULT rc = initImpl(aParent, strConfigFile);
439 if (FAILED(rc)) return rc;
440
441 if (aId)
442 {
443 // loading a registered VM:
444 unconst(mData->mUuid) = *aId;
445 mData->mRegistered = TRUE;
446 // now load the settings from XML:
447 rc = i_registeredInit();
448 // this calls initDataAndChildObjects() and loadSettings()
449 }
450 else
451 {
452 // opening an unregistered VM (VirtualBox::OpenMachine()):
453 rc = initDataAndChildObjects();
454
455 if (SUCCEEDED(rc))
456 {
457 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
458 mData->mAccessible = TRUE;
459
460 try
461 {
462 // load and parse machine XML; this will throw on XML or logic errors
463 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
464
465 // reject VM UUID duplicates, they can happen if someone
466 // tries to register an already known VM config again
467 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
468 true /* fPermitInaccessible */,
469 false /* aDoSetError */,
470 NULL) != VBOX_E_OBJECT_NOT_FOUND)
471 {
472 throw setError(E_FAIL,
473 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
474 mData->m_strConfigFile.c_str());
475 }
476
477 // use UUID from machine config
478 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
479
480 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
481 NULL /* puuidRegistry */);
482 if (FAILED(rc)) throw rc;
483
484 /* At this point the changing of the current state modification
485 * flag is allowed. */
486 i_allowStateModification();
487
488 i_commit();
489 }
490 catch (HRESULT err)
491 {
492 /* we assume that error info is set by the thrower */
493 rc = err;
494 }
495 catch (...)
496 {
497 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
498 }
499 }
500 }
501
502 /* Confirm a successful initialization when it's the case */
503 if (SUCCEEDED(rc))
504 {
505 if (mData->mAccessible)
506 autoInitSpan.setSucceeded();
507 else
508 {
509 autoInitSpan.setLimited();
510
511 // uninit media from this machine's media registry, or else
512 // reloading the settings will fail
513 mParent->i_unregisterMachineMedia(i_getId());
514 }
515 }
516
517 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
518 "rc=%08X\n",
519 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
520 mData->mRegistered, mData->mAccessible, rc));
521
522 LogFlowThisFuncLeave();
523
524 return rc;
525}
526
527/**
528 * Initializes a new instance from a machine config that is already in memory
529 * (import OVF case). Since we are importing, the UUID in the machine
530 * config is ignored and we always generate a fresh one.
531 *
532 * @param aParent Associated parent object.
533 * @param strName Name for the new machine; this overrides what is specified in config.
534 * @param strSettingsFilename File name of .vbox file.
535 * @param config Machine configuration loaded and parsed from XML.
536 *
537 * @return Success indicator. if not S_OK, the machine object is invalid
538 */
539HRESULT Machine::init(VirtualBox *aParent,
540 const Utf8Str &strName,
541 const Utf8Str &strSettingsFilename,
542 const settings::MachineConfigFile &config)
543{
544 LogFlowThisFuncEnter();
545
546 /* Enclose the state transition NotReady->InInit->Ready */
547 AutoInitSpan autoInitSpan(this);
548 AssertReturn(autoInitSpan.isOk(), E_FAIL);
549
550 HRESULT rc = initImpl(aParent, strSettingsFilename);
551 if (FAILED(rc)) return rc;
552
553 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
554 if (FAILED(rc)) return rc;
555
556 rc = initDataAndChildObjects();
557
558 if (SUCCEEDED(rc))
559 {
560 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
561 mData->mAccessible = TRUE;
562
563 // create empty machine config for instance data
564 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
565
566 // generate fresh UUID, ignore machine config
567 unconst(mData->mUuid).create();
568
569 rc = i_loadMachineDataFromSettings(config,
570 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
571
572 // override VM name as well, it may be different
573 mUserData->s.strName = strName;
574
575 if (SUCCEEDED(rc))
576 {
577 /* At this point the changing of the current state modification
578 * flag is allowed. */
579 i_allowStateModification();
580
581 /* commit all changes made during the initialization */
582 i_commit();
583 }
584 }
585
586 /* Confirm a successful initialization when it's the case */
587 if (SUCCEEDED(rc))
588 {
589 if (mData->mAccessible)
590 autoInitSpan.setSucceeded();
591 else
592 {
593 /* Ignore all errors from unregistering, they would destroy
594- * the more interesting error information we already have,
595- * pinpointing the issue with the VM config. */
596 ErrorInfoKeeper eik;
597
598 autoInitSpan.setLimited();
599
600 // uninit media from this machine's media registry, or else
601 // reloading the settings will fail
602 mParent->i_unregisterMachineMedia(i_getId());
603 }
604 }
605
606 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
607 "rc=%08X\n",
608 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
609 mData->mRegistered, mData->mAccessible, rc));
610
611 LogFlowThisFuncLeave();
612
613 return rc;
614}
615
616/**
617 * Shared code between the various init() implementations.
618 * @param aParent The VirtualBox object.
619 * @param strConfigFile Settings file.
620 * @return
621 */
622HRESULT Machine::initImpl(VirtualBox *aParent,
623 const Utf8Str &strConfigFile)
624{
625 LogFlowThisFuncEnter();
626
627 AssertReturn(aParent, E_INVALIDARG);
628 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
629
630 HRESULT rc = S_OK;
631
632 /* share the parent weakly */
633 unconst(mParent) = aParent;
634
635 /* allocate the essential machine data structure (the rest will be
636 * allocated later by initDataAndChildObjects() */
637 mData.allocate();
638
639 /* memorize the config file name (as provided) */
640 mData->m_strConfigFile = strConfigFile;
641
642 /* get the full file name */
643 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
644 if (RT_FAILURE(vrc1))
645 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
646 tr("Invalid machine settings file name '%s' (%Rrc)"),
647 strConfigFile.c_str(),
648 vrc1);
649
650 LogFlowThisFuncLeave();
651
652 return rc;
653}
654
655/**
656 * Tries to create a machine settings file in the path stored in the machine
657 * instance data. Used when a new machine is created to fail gracefully if
658 * the settings file could not be written (e.g. because machine dir is read-only).
659 * @return
660 */
661HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
662{
663 HRESULT rc = S_OK;
664
665 // when we create a new machine, we must be able to create the settings file
666 RTFILE f = NIL_RTFILE;
667 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
668 if ( RT_SUCCESS(vrc)
669 || vrc == VERR_SHARING_VIOLATION
670 )
671 {
672 if (RT_SUCCESS(vrc))
673 RTFileClose(f);
674 if (!fForceOverwrite)
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Machine settings file '%s' already exists"),
677 mData->m_strConfigFileFull.c_str());
678 else
679 {
680 /* try to delete the config file, as otherwise the creation
681 * of a new settings file will fail. */
682 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
683 if (RT_FAILURE(vrc2))
684 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
685 tr("Could not delete the existing settings file '%s' (%Rrc)"),
686 mData->m_strConfigFileFull.c_str(), vrc2);
687 }
688 }
689 else if ( vrc != VERR_FILE_NOT_FOUND
690 && vrc != VERR_PATH_NOT_FOUND
691 )
692 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
693 tr("Invalid machine settings file name '%s' (%Rrc)"),
694 mData->m_strConfigFileFull.c_str(),
695 vrc);
696 return rc;
697}
698
699/**
700 * Initializes the registered machine by loading the settings file.
701 * This method is separated from #init() in order to make it possible to
702 * retry the operation after VirtualBox startup instead of refusing to
703 * startup the whole VirtualBox server in case if the settings file of some
704 * registered VM is invalid or inaccessible.
705 *
706 * @note Must be always called from this object's write lock
707 * (unless called from #init() that doesn't need any locking).
708 * @note Locks the mUSBController method for writing.
709 * @note Subclasses must not call this method.
710 */
711HRESULT Machine::i_registeredInit()
712{
713 AssertReturn(!i_isSessionMachine(), E_FAIL);
714 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
715 AssertReturn(mData->mUuid.isValid(), E_FAIL);
716 AssertReturn(!mData->mAccessible, E_FAIL);
717
718 HRESULT rc = initDataAndChildObjects();
719
720 if (SUCCEEDED(rc))
721 {
722 /* Temporarily reset the registered flag in order to let setters
723 * potentially called from loadSettings() succeed (isMutable() used in
724 * all setters will return FALSE for a Machine instance if mRegistered
725 * is TRUE). */
726 mData->mRegistered = FALSE;
727
728 try
729 {
730 // load and parse machine XML; this will throw on XML or logic errors
731 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
732
733 if (mData->mUuid != mData->pMachineConfigFile->uuid)
734 throw setError(E_FAIL,
735 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
736 mData->pMachineConfigFile->uuid.raw(),
737 mData->m_strConfigFileFull.c_str(),
738 mData->mUuid.toString().c_str(),
739 mParent->i_settingsFilePath().c_str());
740
741 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
742 NULL /* const Guid *puuidRegistry */);
743 if (FAILED(rc)) throw rc;
744 }
745 catch (HRESULT err)
746 {
747 /* we assume that error info is set by the thrower */
748 rc = err;
749 }
750 catch (...)
751 {
752 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
753 }
754
755 /* Restore the registered flag (even on failure) */
756 mData->mRegistered = TRUE;
757 }
758
759 if (SUCCEEDED(rc))
760 {
761 /* Set mAccessible to TRUE only if we successfully locked and loaded
762 * the settings file */
763 mData->mAccessible = TRUE;
764
765 /* commit all changes made during loading the settings file */
766 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
767 /// @todo r=klaus for some reason the settings loading logic backs up
768 // the settings, and therefore a commit is needed. Should probably be changed.
769 }
770 else
771 {
772 /* If the machine is registered, then, instead of returning a
773 * failure, we mark it as inaccessible and set the result to
774 * success to give it a try later */
775
776 /* fetch the current error info */
777 mData->mAccessError = com::ErrorInfo();
778 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
779
780 /* rollback all changes */
781 i_rollback(false /* aNotify */);
782
783 // uninit media from this machine's media registry, or else
784 // reloading the settings will fail
785 mParent->i_unregisterMachineMedia(i_getId());
786
787 /* uninitialize the common part to make sure all data is reset to
788 * default (null) values */
789 uninitDataAndChildObjects();
790
791 rc = S_OK;
792 }
793
794 return rc;
795}
796
797/**
798 * Uninitializes the instance.
799 * Called either from FinalRelease() or by the parent when it gets destroyed.
800 *
801 * @note The caller of this method must make sure that this object
802 * a) doesn't have active callers on the current thread and b) is not locked
803 * by the current thread; otherwise uninit() will hang either a) due to
804 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
805 * a dead-lock caused by this thread waiting for all callers on the other
806 * threads are done but preventing them from doing so by holding a lock.
807 */
808void Machine::uninit()
809{
810 LogFlowThisFuncEnter();
811
812 Assert(!isWriteLockOnCurrentThread());
813
814 Assert(!uRegistryNeedsSaving);
815 if (uRegistryNeedsSaving)
816 {
817 AutoCaller autoCaller(this);
818 if (SUCCEEDED(autoCaller.rc()))
819 {
820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
821 i_saveSettings(NULL, Machine::SaveS_Force);
822 }
823 }
824
825 /* Enclose the state transition Ready->InUninit->NotReady */
826 AutoUninitSpan autoUninitSpan(this);
827 if (autoUninitSpan.uninitDone())
828 return;
829
830 Assert(!i_isSnapshotMachine());
831 Assert(!i_isSessionMachine());
832 Assert(!!mData);
833
834 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
835 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
836
837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
838
839 if (!mData->mSession.mMachine.isNull())
840 {
841 /* Theoretically, this can only happen if the VirtualBox server has been
842 * terminated while there were clients running that owned open direct
843 * sessions. Since in this case we are definitely called by
844 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
845 * won't happen on the client watcher thread (because it has a
846 * VirtualBox caller for the duration of the
847 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
848 * cannot happen until the VirtualBox caller is released). This is
849 * important, because SessionMachine::uninit() cannot correctly operate
850 * after we return from this method (it expects the Machine instance is
851 * still valid). We'll call it ourselves below.
852 */
853 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
854 (SessionMachine*)mData->mSession.mMachine));
855
856 if (Global::IsOnlineOrTransient(mData->mMachineState))
857 {
858 Log1WarningThisFunc(("Setting state to Aborted!\n"));
859 /* set machine state using SessionMachine reimplementation */
860 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
861 }
862
863 /*
864 * Uninitialize SessionMachine using public uninit() to indicate
865 * an unexpected uninitialization.
866 */
867 mData->mSession.mMachine->uninit();
868 /* SessionMachine::uninit() must set mSession.mMachine to null */
869 Assert(mData->mSession.mMachine.isNull());
870 }
871
872 // uninit media from this machine's media registry, if they're still there
873 Guid uuidMachine(i_getId());
874
875 /* the lock is no more necessary (SessionMachine is uninitialized) */
876 alock.release();
877
878 /* XXX This will fail with
879 * "cannot be closed because it is still attached to 1 virtual machines"
880 * because at this point we did not call uninitDataAndChildObjects() yet
881 * and therefore also removeBackReference() for all these mediums was not called! */
882
883 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
884 mParent->i_unregisterMachineMedia(uuidMachine);
885
886 // has machine been modified?
887 if (mData->flModifications)
888 {
889 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
890 i_rollback(false /* aNotify */);
891 }
892
893 if (mData->mAccessible)
894 uninitDataAndChildObjects();
895
896 /* free the essential data structure last */
897 mData.free();
898
899 LogFlowThisFuncLeave();
900}
901
902// Wrapped IMachine properties
903/////////////////////////////////////////////////////////////////////////////
904HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
905{
906 /* mParent is constant during life time, no need to lock */
907 ComObjPtr<VirtualBox> pVirtualBox(mParent);
908 aParent = pVirtualBox;
909
910 return S_OK;
911}
912
913
914HRESULT Machine::getAccessible(BOOL *aAccessible)
915{
916 /* In some cases (medium registry related), it is necessary to be able to
917 * go through the list of all machines. Happens when an inaccessible VM
918 * has a sensible medium registry. */
919 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
921
922 HRESULT rc = S_OK;
923
924 if (!mData->mAccessible)
925 {
926 /* try to initialize the VM once more if not accessible */
927
928 AutoReinitSpan autoReinitSpan(this);
929 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
930
931#ifdef DEBUG
932 LogFlowThisFunc(("Dumping media backreferences\n"));
933 mParent->i_dumpAllBackRefs();
934#endif
935
936 if (mData->pMachineConfigFile)
937 {
938 // reset the XML file to force loadSettings() (called from i_registeredInit())
939 // to parse it again; the file might have changed
940 delete mData->pMachineConfigFile;
941 mData->pMachineConfigFile = NULL;
942 }
943
944 rc = i_registeredInit();
945
946 if (SUCCEEDED(rc) && mData->mAccessible)
947 {
948 autoReinitSpan.setSucceeded();
949
950 /* make sure interesting parties will notice the accessibility
951 * state change */
952 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
953 mParent->i_onMachineDataChange(mData->mUuid);
954 }
955 }
956
957 if (SUCCEEDED(rc))
958 *aAccessible = mData->mAccessible;
959
960 LogFlowThisFuncLeave();
961
962 return rc;
963}
964
965HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
966{
967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
968
969 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
970 {
971 /* return shortly */
972 aAccessError = NULL;
973 return S_OK;
974 }
975
976 HRESULT rc = S_OK;
977
978 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
979 rc = errorInfo.createObject();
980 if (SUCCEEDED(rc))
981 {
982 errorInfo->init(mData->mAccessError.getResultCode(),
983 mData->mAccessError.getInterfaceID().ref(),
984 Utf8Str(mData->mAccessError.getComponent()).c_str(),
985 Utf8Str(mData->mAccessError.getText()));
986 aAccessError = errorInfo;
987 }
988
989 return rc;
990}
991
992HRESULT Machine::getName(com::Utf8Str &aName)
993{
994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 aName = mUserData->s.strName;
997
998 return S_OK;
999}
1000
1001HRESULT Machine::setName(const com::Utf8Str &aName)
1002{
1003 // prohibit setting a UUID only as the machine name, or else it can
1004 // never be found by findMachine()
1005 Guid test(aName);
1006
1007 if (test.isValid())
1008 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1009
1010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1011
1012 HRESULT rc = i_checkStateDependency(MutableStateDep);
1013 if (FAILED(rc)) return rc;
1014
1015 i_setModified(IsModified_MachineData);
1016 mUserData.backup();
1017 mUserData->s.strName = aName;
1018
1019 return S_OK;
1020}
1021
1022HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1023{
1024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 aDescription = mUserData->s.strDescription;
1027
1028 return S_OK;
1029}
1030
1031HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1032{
1033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 // this can be done in principle in any state as it doesn't affect the VM
1036 // significantly, but play safe by not messing around while complex
1037 // activities are going on
1038 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1039 if (FAILED(rc)) return rc;
1040
1041 i_setModified(IsModified_MachineData);
1042 mUserData.backup();
1043 mUserData->s.strDescription = aDescription;
1044
1045 return S_OK;
1046}
1047
1048HRESULT Machine::getId(com::Guid &aId)
1049{
1050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1051
1052 aId = mData->mUuid;
1053
1054 return S_OK;
1055}
1056
1057HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1058{
1059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1060 aGroups.resize(mUserData->s.llGroups.size());
1061 size_t i = 0;
1062 for (StringsList::const_iterator
1063 it = mUserData->s.llGroups.begin();
1064 it != mUserData->s.llGroups.end();
1065 ++it, ++i)
1066 aGroups[i] = (*it);
1067
1068 return S_OK;
1069}
1070
1071HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1072{
1073 StringsList llGroups;
1074 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1075 if (FAILED(rc))
1076 return rc;
1077
1078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 rc = i_checkStateDependency(MutableOrSavedStateDep);
1081 if (FAILED(rc)) return rc;
1082
1083 i_setModified(IsModified_MachineData);
1084 mUserData.backup();
1085 mUserData->s.llGroups = llGroups;
1086
1087 return S_OK;
1088}
1089
1090HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1091{
1092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1093
1094 aOSTypeId = mUserData->s.strOsType;
1095
1096 return S_OK;
1097}
1098
1099HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1100{
1101 /* look up the object by Id to check it is valid */
1102 ComObjPtr<GuestOSType> pGuestOSType;
1103 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1104
1105 /* when setting, always use the "etalon" value for consistency -- lookup
1106 * by ID is case-insensitive and the input value may have different case */
1107 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1108
1109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1110
1111 HRESULT rc = i_checkStateDependency(MutableStateDep);
1112 if (FAILED(rc)) return rc;
1113
1114 i_setModified(IsModified_MachineData);
1115 mUserData.backup();
1116 mUserData->s.strOsType = osTypeId;
1117
1118 return S_OK;
1119}
1120
1121HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1122{
1123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1124
1125 *aFirmwareType = mHWData->mFirmwareType;
1126
1127 return S_OK;
1128}
1129
1130HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1131{
1132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 HRESULT rc = i_checkStateDependency(MutableStateDep);
1135 if (FAILED(rc)) return rc;
1136
1137 i_setModified(IsModified_MachineData);
1138 mHWData.backup();
1139 mHWData->mFirmwareType = aFirmwareType;
1140
1141 return S_OK;
1142}
1143
1144HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1145{
1146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1147
1148 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1149
1150 return S_OK;
1151}
1152
1153HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1154{
1155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1156
1157 HRESULT rc = i_checkStateDependency(MutableStateDep);
1158 if (FAILED(rc)) return rc;
1159
1160 i_setModified(IsModified_MachineData);
1161 mHWData.backup();
1162 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1163
1164 return S_OK;
1165}
1166
1167HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1168{
1169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1170
1171 *aPointingHIDType = mHWData->mPointingHIDType;
1172
1173 return S_OK;
1174}
1175
1176HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1177{
1178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 HRESULT rc = i_checkStateDependency(MutableStateDep);
1181 if (FAILED(rc)) return rc;
1182
1183 i_setModified(IsModified_MachineData);
1184 mHWData.backup();
1185 mHWData->mPointingHIDType = aPointingHIDType;
1186
1187 return S_OK;
1188}
1189
1190HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1191{
1192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 *aChipsetType = mHWData->mChipsetType;
1195
1196 return S_OK;
1197}
1198
1199HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1200{
1201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 HRESULT rc = i_checkStateDependency(MutableStateDep);
1204 if (FAILED(rc)) return rc;
1205
1206 if (aChipsetType != mHWData->mChipsetType)
1207 {
1208 i_setModified(IsModified_MachineData);
1209 mHWData.backup();
1210 mHWData->mChipsetType = aChipsetType;
1211
1212 // Resize network adapter array, to be finalized on commit/rollback.
1213 // We must not throw away entries yet, otherwise settings are lost
1214 // without a way to roll back.
1215 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1216 size_t oldCount = mNetworkAdapters.size();
1217 if (newCount > oldCount)
1218 {
1219 mNetworkAdapters.resize(newCount);
1220 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1221 {
1222 unconst(mNetworkAdapters[slot]).createObject();
1223 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1224 }
1225 }
1226 }
1227
1228 return S_OK;
1229}
1230
1231HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1232{
1233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 aParavirtDebug = mHWData->mParavirtDebug;
1236 return S_OK;
1237}
1238
1239HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1240{
1241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 HRESULT rc = i_checkStateDependency(MutableStateDep);
1244 if (FAILED(rc)) return rc;
1245
1246 /** @todo Parse/validate options? */
1247 if (aParavirtDebug != mHWData->mParavirtDebug)
1248 {
1249 i_setModified(IsModified_MachineData);
1250 mHWData.backup();
1251 mHWData->mParavirtDebug = aParavirtDebug;
1252 }
1253
1254 return S_OK;
1255}
1256
1257HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1258{
1259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1260
1261 *aParavirtProvider = mHWData->mParavirtProvider;
1262
1263 return S_OK;
1264}
1265
1266HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1267{
1268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1269
1270 HRESULT rc = i_checkStateDependency(MutableStateDep);
1271 if (FAILED(rc)) return rc;
1272
1273 if (aParavirtProvider != mHWData->mParavirtProvider)
1274 {
1275 i_setModified(IsModified_MachineData);
1276 mHWData.backup();
1277 mHWData->mParavirtProvider = aParavirtProvider;
1278 }
1279
1280 return S_OK;
1281}
1282
1283HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1284{
1285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1286
1287 *aParavirtProvider = mHWData->mParavirtProvider;
1288 switch (mHWData->mParavirtProvider)
1289 {
1290 case ParavirtProvider_None:
1291 case ParavirtProvider_HyperV:
1292 case ParavirtProvider_KVM:
1293 case ParavirtProvider_Minimal:
1294 break;
1295
1296 /* Resolve dynamic provider types to the effective types. */
1297 default:
1298 {
1299 ComObjPtr<GuestOSType> pGuestOSType;
1300 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1301 pGuestOSType);
1302 if (FAILED(hrc2) || pGuestOSType.isNull())
1303 {
1304 *aParavirtProvider = ParavirtProvider_None;
1305 break;
1306 }
1307
1308 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1309 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1310
1311 switch (mHWData->mParavirtProvider)
1312 {
1313 case ParavirtProvider_Legacy:
1314 {
1315 if (fOsXGuest)
1316 *aParavirtProvider = ParavirtProvider_Minimal;
1317 else
1318 *aParavirtProvider = ParavirtProvider_None;
1319 break;
1320 }
1321
1322 case ParavirtProvider_Default:
1323 {
1324 if (fOsXGuest)
1325 *aParavirtProvider = ParavirtProvider_Minimal;
1326 else if ( mUserData->s.strOsType == "Windows10"
1327 || mUserData->s.strOsType == "Windows10_64"
1328 || mUserData->s.strOsType == "Windows81"
1329 || mUserData->s.strOsType == "Windows81_64"
1330 || mUserData->s.strOsType == "Windows8"
1331 || mUserData->s.strOsType == "Windows8_64"
1332 || mUserData->s.strOsType == "Windows7"
1333 || mUserData->s.strOsType == "Windows7_64"
1334 || mUserData->s.strOsType == "WindowsVista"
1335 || mUserData->s.strOsType == "WindowsVista_64"
1336 || mUserData->s.strOsType == "Windows2012"
1337 || mUserData->s.strOsType == "Windows2012_64"
1338 || mUserData->s.strOsType == "Windows2008"
1339 || mUserData->s.strOsType == "Windows2008_64")
1340 {
1341 *aParavirtProvider = ParavirtProvider_HyperV;
1342 }
1343 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1344 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1345 || mUserData->s.strOsType == "Linux"
1346 || mUserData->s.strOsType == "Linux_64"
1347 || mUserData->s.strOsType == "ArchLinux"
1348 || mUserData->s.strOsType == "ArchLinux_64"
1349 || mUserData->s.strOsType == "Debian"
1350 || mUserData->s.strOsType == "Debian_64"
1351 || mUserData->s.strOsType == "Fedora"
1352 || mUserData->s.strOsType == "Fedora_64"
1353 || mUserData->s.strOsType == "Gentoo"
1354 || mUserData->s.strOsType == "Gentoo_64"
1355 || mUserData->s.strOsType == "Mandriva"
1356 || mUserData->s.strOsType == "Mandriva_64"
1357 || mUserData->s.strOsType == "OpenSUSE"
1358 || mUserData->s.strOsType == "OpenSUSE_64"
1359 || mUserData->s.strOsType == "Oracle"
1360 || mUserData->s.strOsType == "Oracle_64"
1361 || mUserData->s.strOsType == "RedHat"
1362 || mUserData->s.strOsType == "RedHat_64"
1363 || mUserData->s.strOsType == "Turbolinux"
1364 || mUserData->s.strOsType == "Turbolinux_64"
1365 || mUserData->s.strOsType == "Ubuntu"
1366 || mUserData->s.strOsType == "Ubuntu_64"
1367 || mUserData->s.strOsType == "Xandros"
1368 || mUserData->s.strOsType == "Xandros_64")
1369 {
1370 *aParavirtProvider = ParavirtProvider_KVM;
1371 }
1372 else
1373 *aParavirtProvider = ParavirtProvider_None;
1374 break;
1375 }
1376
1377 default: AssertFailedBreak(); /* Shut up MSC. */
1378 }
1379 break;
1380 }
1381 }
1382
1383 Assert( *aParavirtProvider == ParavirtProvider_None
1384 || *aParavirtProvider == ParavirtProvider_Minimal
1385 || *aParavirtProvider == ParavirtProvider_HyperV
1386 || *aParavirtProvider == ParavirtProvider_KVM);
1387 return S_OK;
1388}
1389
1390HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1391{
1392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1393
1394 aHardwareVersion = mHWData->mHWVersion;
1395
1396 return S_OK;
1397}
1398
1399HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1400{
1401 /* check known version */
1402 Utf8Str hwVersion = aHardwareVersion;
1403 if ( hwVersion.compare("1") != 0
1404 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1405 return setError(E_INVALIDARG,
1406 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = i_checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 i_setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mHWVersion = aHardwareVersion;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1421{
1422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1423
1424 if (!mHWData->mHardwareUUID.isZero())
1425 aHardwareUUID = mHWData->mHardwareUUID;
1426 else
1427 aHardwareUUID = mData->mUuid;
1428
1429 return S_OK;
1430}
1431
1432HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1433{
1434 if (!aHardwareUUID.isValid())
1435 return E_INVALIDARG;
1436
1437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 HRESULT rc = i_checkStateDependency(MutableStateDep);
1440 if (FAILED(rc)) return rc;
1441
1442 i_setModified(IsModified_MachineData);
1443 mHWData.backup();
1444 if (aHardwareUUID == mData->mUuid)
1445 mHWData->mHardwareUUID.clear();
1446 else
1447 mHWData->mHardwareUUID = aHardwareUUID;
1448
1449 return S_OK;
1450}
1451
1452HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1453{
1454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 *aMemorySize = mHWData->mMemorySize;
1457
1458 return S_OK;
1459}
1460
1461HRESULT Machine::setMemorySize(ULONG aMemorySize)
1462{
1463 /* check RAM limits */
1464 if ( aMemorySize < MM_RAM_MIN_IN_MB
1465 || aMemorySize > MM_RAM_MAX_IN_MB
1466 )
1467 return setError(E_INVALIDARG,
1468 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1469 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1470
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 HRESULT rc = i_checkStateDependency(MutableStateDep);
1474 if (FAILED(rc)) return rc;
1475
1476 i_setModified(IsModified_MachineData);
1477 mHWData.backup();
1478 mHWData->mMemorySize = aMemorySize;
1479
1480 return S_OK;
1481}
1482
1483HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1484{
1485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 *aCPUCount = mHWData->mCPUCount;
1488
1489 return S_OK;
1490}
1491
1492HRESULT Machine::setCPUCount(ULONG aCPUCount)
1493{
1494 /* check CPU limits */
1495 if ( aCPUCount < SchemaDefs::MinCPUCount
1496 || aCPUCount > SchemaDefs::MaxCPUCount
1497 )
1498 return setError(E_INVALIDARG,
1499 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1500 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1505 if (mHWData->mCPUHotPlugEnabled)
1506 {
1507 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1508 {
1509 if (mHWData->mCPUAttached[idx])
1510 return setError(E_INVALIDARG,
1511 tr("There is still a CPU attached to socket %lu."
1512 "Detach the CPU before removing the socket"),
1513 aCPUCount, idx+1);
1514 }
1515 }
1516
1517 HRESULT rc = i_checkStateDependency(MutableStateDep);
1518 if (FAILED(rc)) return rc;
1519
1520 i_setModified(IsModified_MachineData);
1521 mHWData.backup();
1522 mHWData->mCPUCount = aCPUCount;
1523
1524 return S_OK;
1525}
1526
1527HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1528{
1529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1530
1531 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1532
1533 return S_OK;
1534}
1535
1536HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1537{
1538 HRESULT rc = S_OK;
1539
1540 /* check throttle limits */
1541 if ( aCPUExecutionCap < 1
1542 || aCPUExecutionCap > 100
1543 )
1544 return setError(E_INVALIDARG,
1545 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1546 aCPUExecutionCap, 1, 100);
1547
1548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1549
1550 alock.release();
1551 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1552 alock.acquire();
1553 if (FAILED(rc)) return rc;
1554
1555 i_setModified(IsModified_MachineData);
1556 mHWData.backup();
1557 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1558
1559 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1560 if (Global::IsOnline(mData->mMachineState))
1561 i_saveSettings(NULL);
1562
1563 return S_OK;
1564}
1565
1566HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1567{
1568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1571
1572 return S_OK;
1573}
1574
1575HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1576{
1577 HRESULT rc = S_OK;
1578
1579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 rc = i_checkStateDependency(MutableStateDep);
1582 if (FAILED(rc)) return rc;
1583
1584 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1585 {
1586 if (aCPUHotPlugEnabled)
1587 {
1588 i_setModified(IsModified_MachineData);
1589 mHWData.backup();
1590
1591 /* Add the amount of CPUs currently attached */
1592 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1593 mHWData->mCPUAttached[i] = true;
1594 }
1595 else
1596 {
1597 /*
1598 * We can disable hotplug only if the amount of maximum CPUs is equal
1599 * to the amount of attached CPUs
1600 */
1601 unsigned cCpusAttached = 0;
1602 unsigned iHighestId = 0;
1603
1604 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1605 {
1606 if (mHWData->mCPUAttached[i])
1607 {
1608 cCpusAttached++;
1609 iHighestId = i;
1610 }
1611 }
1612
1613 if ( (cCpusAttached != mHWData->mCPUCount)
1614 || (iHighestId >= mHWData->mCPUCount))
1615 return setError(E_INVALIDARG,
1616 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1617
1618 i_setModified(IsModified_MachineData);
1619 mHWData.backup();
1620 }
1621 }
1622
1623 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1624
1625 return rc;
1626}
1627
1628HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1629{
1630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1631
1632 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1633
1634 return S_OK;
1635}
1636
1637HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1638{
1639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1640
1641 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1642 if (SUCCEEDED(hrc))
1643 {
1644 i_setModified(IsModified_MachineData);
1645 mHWData.backup();
1646 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1647 }
1648 return hrc;
1649}
1650
1651HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1652{
1653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1654 aCPUProfile = mHWData->mCpuProfile;
1655 return S_OK;
1656}
1657
1658HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1659{
1660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1661 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1662 if (SUCCEEDED(hrc))
1663 {
1664 i_setModified(IsModified_MachineData);
1665 mHWData.backup();
1666 /* Empty equals 'host'. */
1667 if (aCPUProfile.isNotEmpty())
1668 mHWData->mCpuProfile = aCPUProfile;
1669 else
1670 mHWData->mCpuProfile = "host";
1671 }
1672 return hrc;
1673}
1674
1675HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1676{
1677#ifdef VBOX_WITH_USB_CARDREADER
1678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1681
1682 return S_OK;
1683#else
1684 NOREF(aEmulatedUSBCardReaderEnabled);
1685 return E_NOTIMPL;
1686#endif
1687}
1688
1689HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1690{
1691#ifdef VBOX_WITH_USB_CARDREADER
1692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1693
1694 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1695 if (FAILED(rc)) return rc;
1696
1697 i_setModified(IsModified_MachineData);
1698 mHWData.backup();
1699 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1700
1701 return S_OK;
1702#else
1703 NOREF(aEmulatedUSBCardReaderEnabled);
1704 return E_NOTIMPL;
1705#endif
1706}
1707
1708HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1709{
1710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 *aHPETEnabled = mHWData->mHPETEnabled;
1713
1714 return S_OK;
1715}
1716
1717HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1718{
1719 HRESULT rc = S_OK;
1720
1721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 rc = i_checkStateDependency(MutableStateDep);
1724 if (FAILED(rc)) return rc;
1725
1726 i_setModified(IsModified_MachineData);
1727 mHWData.backup();
1728
1729 mHWData->mHPETEnabled = aHPETEnabled;
1730
1731 return rc;
1732}
1733
1734HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1735{
1736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1739 return S_OK;
1740}
1741
1742HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1743{
1744 HRESULT rc = S_OK;
1745
1746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1747
1748 i_setModified(IsModified_MachineData);
1749 mHWData.backup();
1750 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1751
1752 alock.release();
1753 rc = i_onVideoCaptureChange();
1754 alock.acquire();
1755 if (FAILED(rc))
1756 {
1757 /*
1758 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1759 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1760 * determine if it should start or stop capturing. Therefore we need to manually
1761 * undo change.
1762 */
1763 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1764 return rc;
1765 }
1766
1767 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1768 if (Global::IsOnline(mData->mMachineState))
1769 i_saveSettings(NULL);
1770
1771 return rc;
1772}
1773
1774HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1775{
1776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1777 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1778 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1779 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1780 return S_OK;
1781}
1782
1783HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1784{
1785 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1786 bool fChanged = false;
1787
1788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1789
1790 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1791 {
1792 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1793 {
1794 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1795 fChanged = true;
1796 }
1797 }
1798 if (fChanged)
1799 {
1800 alock.release();
1801 HRESULT rc = i_onVideoCaptureChange();
1802 alock.acquire();
1803 if (FAILED(rc)) return rc;
1804 i_setModified(IsModified_MachineData);
1805
1806 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1807 if (Global::IsOnline(mData->mMachineState))
1808 i_saveSettings(NULL);
1809 }
1810
1811 return S_OK;
1812}
1813
1814HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1815{
1816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1817 if (mHWData->mVideoCaptureFile.isEmpty())
1818 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1819 else
1820 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1821 return S_OK;
1822}
1823
1824HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1825{
1826 Utf8Str strFile(aVideoCaptureFile);
1827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 if ( Global::IsOnline(mData->mMachineState)
1830 && mHWData->mVideoCaptureEnabled)
1831 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1832
1833 if (!RTPathStartsWithRoot(strFile.c_str()))
1834 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1835
1836 if (!strFile.isEmpty())
1837 {
1838 Utf8Str defaultFile;
1839 i_getDefaultVideoCaptureFile(defaultFile);
1840 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1841 strFile.setNull();
1842 }
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureFile = strFile;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1855 return S_OK;
1856}
1857
1858HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1859{
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 i_setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1877 return S_OK;
1878}
1879
1880HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1881{
1882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 if ( Global::IsOnline(mData->mMachineState)
1885 && mHWData->mVideoCaptureEnabled)
1886 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1887
1888 i_setModified(IsModified_MachineData);
1889 mHWData.backup();
1890 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1896{
1897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1898 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1899 return S_OK;
1900}
1901
1902HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1903{
1904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1905
1906 if ( Global::IsOnline(mData->mMachineState)
1907 && mHWData->mVideoCaptureEnabled)
1908 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1909
1910 i_setModified(IsModified_MachineData);
1911 mHWData.backup();
1912 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1913
1914 return S_OK;
1915}
1916
1917HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1918{
1919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1920 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1921 return S_OK;
1922}
1923
1924HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1925{
1926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1927
1928 if ( Global::IsOnline(mData->mMachineState)
1929 && mHWData->mVideoCaptureEnabled)
1930 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1931
1932 i_setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1935
1936 return S_OK;
1937}
1938
1939HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1940{
1941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1942 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1943 return S_OK;
1944}
1945
1946HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1947{
1948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1949
1950 if ( Global::IsOnline(mData->mMachineState)
1951 && mHWData->mVideoCaptureEnabled)
1952 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1953
1954 i_setModified(IsModified_MachineData);
1955 mHWData.backup();
1956 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1957
1958 return S_OK;
1959}
1960
1961HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1962{
1963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1964 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1965 return S_OK;
1966}
1967
1968HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1969{
1970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1971
1972 if ( Global::IsOnline(mData->mMachineState)
1973 && mHWData->mVideoCaptureEnabled)
1974 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1975
1976 i_setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1979
1980 return S_OK;
1981}
1982
1983HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1984{
1985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1986
1987 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1988 return S_OK;
1989}
1990
1991HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1992{
1993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1994
1995 if ( Global::IsOnline(mData->mMachineState)
1996 && mHWData->mVideoCaptureEnabled)
1997 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1998
1999 i_setModified(IsModified_MachineData);
2000 mHWData.backup();
2001 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
2002
2003 return S_OK;
2004}
2005
2006HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
2007{
2008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2009
2010 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2011
2012 return S_OK;
2013}
2014
2015HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2016{
2017 switch (aGraphicsControllerType)
2018 {
2019 case GraphicsControllerType_Null:
2020 case GraphicsControllerType_VBoxVGA:
2021#ifdef VBOX_WITH_VMSVGA
2022 case GraphicsControllerType_VMSVGA:
2023#endif
2024 break;
2025 default:
2026 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2027 }
2028
2029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 HRESULT rc = i_checkStateDependency(MutableStateDep);
2032 if (FAILED(rc)) return rc;
2033
2034 i_setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2037
2038 return S_OK;
2039}
2040
2041HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2042{
2043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2044
2045 *aVRAMSize = mHWData->mVRAMSize;
2046
2047 return S_OK;
2048}
2049
2050HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2051{
2052 /* check VRAM limits */
2053 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2054 return setError(E_INVALIDARG,
2055 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2056 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2057
2058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2059
2060 HRESULT rc = i_checkStateDependency(MutableStateDep);
2061 if (FAILED(rc)) return rc;
2062
2063 i_setModified(IsModified_MachineData);
2064 mHWData.backup();
2065 mHWData->mVRAMSize = aVRAMSize;
2066
2067 return S_OK;
2068}
2069
2070/** @todo this method should not be public */
2071HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2072{
2073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2074
2075 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2076
2077 return S_OK;
2078}
2079
2080/**
2081 * Set the memory balloon size.
2082 *
2083 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2084 * we have to make sure that we never call IGuest from here.
2085 */
2086HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2087{
2088 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2089#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2090 /* check limits */
2091 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2092 return setError(E_INVALIDARG,
2093 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2094 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2095
2096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2097
2098 i_setModified(IsModified_MachineData);
2099 mHWData.backup();
2100 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2101
2102 return S_OK;
2103#else
2104 NOREF(aMemoryBalloonSize);
2105 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2106#endif
2107}
2108
2109HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2110{
2111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2112
2113 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2114 return S_OK;
2115}
2116
2117HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2118{
2119#ifdef VBOX_WITH_PAGE_SHARING
2120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2121
2122 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2123 i_setModified(IsModified_MachineData);
2124 mHWData.backup();
2125 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2126 return S_OK;
2127#else
2128 NOREF(aPageFusionEnabled);
2129 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2130#endif
2131}
2132
2133HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2134{
2135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2138
2139 return S_OK;
2140}
2141
2142HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2143{
2144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2145
2146 HRESULT rc = i_checkStateDependency(MutableStateDep);
2147 if (FAILED(rc)) return rc;
2148
2149 /** @todo check validity! */
2150
2151 i_setModified(IsModified_MachineData);
2152 mHWData.backup();
2153 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2154
2155 return S_OK;
2156}
2157
2158
2159HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2160{
2161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2164
2165 return S_OK;
2166}
2167
2168HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2169{
2170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2171
2172 HRESULT rc = i_checkStateDependency(MutableStateDep);
2173 if (FAILED(rc)) return rc;
2174
2175 /** @todo check validity! */
2176 i_setModified(IsModified_MachineData);
2177 mHWData.backup();
2178 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2179
2180 return S_OK;
2181}
2182
2183HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2184{
2185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2186
2187 *aMonitorCount = mHWData->mMonitorCount;
2188
2189 return S_OK;
2190}
2191
2192HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2193{
2194 /* make sure monitor count is a sensible number */
2195 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2196 return setError(E_INVALIDARG,
2197 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2198 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2199
2200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2201
2202 HRESULT rc = i_checkStateDependency(MutableStateDep);
2203 if (FAILED(rc)) return rc;
2204
2205 i_setModified(IsModified_MachineData);
2206 mHWData.backup();
2207 mHWData->mMonitorCount = aMonitorCount;
2208
2209 return S_OK;
2210}
2211
2212HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2213{
2214 /* mBIOSSettings is constant during life time, no need to lock */
2215 aBIOSSettings = mBIOSSettings;
2216
2217 return S_OK;
2218}
2219
2220HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2221{
2222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2223
2224 switch (aProperty)
2225 {
2226 case CPUPropertyType_PAE:
2227 *aValue = mHWData->mPAEEnabled;
2228 break;
2229
2230 case CPUPropertyType_LongMode:
2231 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2232 *aValue = TRUE;
2233 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2234 *aValue = FALSE;
2235#if HC_ARCH_BITS == 64
2236 else
2237 *aValue = TRUE;
2238#else
2239 else
2240 {
2241 *aValue = FALSE;
2242
2243 ComObjPtr<GuestOSType> pGuestOSType;
2244 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2245 pGuestOSType);
2246 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2247 {
2248 if (pGuestOSType->i_is64Bit())
2249 {
2250 ComObjPtr<Host> pHost = mParent->i_host();
2251 alock.release();
2252
2253 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2254 if (FAILED(hrc2))
2255 *aValue = FALSE;
2256 }
2257 }
2258 }
2259#endif
2260 break;
2261
2262 case CPUPropertyType_TripleFaultReset:
2263 *aValue = mHWData->mTripleFaultReset;
2264 break;
2265
2266 case CPUPropertyType_APIC:
2267 *aValue = mHWData->mAPIC;
2268 break;
2269
2270 case CPUPropertyType_X2APIC:
2271 *aValue = mHWData->mX2APIC;
2272 break;
2273
2274 case CPUPropertyType_IBPBOnVMExit:
2275 *aValue = mHWData->mIBPBOnVMExit;
2276 break;
2277
2278 case CPUPropertyType_IBPBOnVMEntry:
2279 *aValue = mHWData->mIBPBOnVMEntry;
2280 break;
2281
2282 case CPUPropertyType_SpecCtrl:
2283 *aValue = mHWData->mSpecCtrl;
2284 break;
2285
2286 case CPUPropertyType_SpecCtrlByHost:
2287 *aValue = mHWData->mSpecCtrlByHost;
2288 break;
2289
2290 case CPUPropertyType_HWVirt:
2291 *aValue = mHWData->mNestedHWVirt;
2292 break;
2293
2294 default:
2295 return E_INVALIDARG;
2296 }
2297 return S_OK;
2298}
2299
2300HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2301{
2302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2303
2304 HRESULT rc = i_checkStateDependency(MutableStateDep);
2305 if (FAILED(rc)) return rc;
2306
2307 switch (aProperty)
2308 {
2309 case CPUPropertyType_PAE:
2310 i_setModified(IsModified_MachineData);
2311 mHWData.backup();
2312 mHWData->mPAEEnabled = !!aValue;
2313 break;
2314
2315 case CPUPropertyType_LongMode:
2316 i_setModified(IsModified_MachineData);
2317 mHWData.backup();
2318 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2319 break;
2320
2321 case CPUPropertyType_TripleFaultReset:
2322 i_setModified(IsModified_MachineData);
2323 mHWData.backup();
2324 mHWData->mTripleFaultReset = !!aValue;
2325 break;
2326
2327 case CPUPropertyType_APIC:
2328 if (mHWData->mX2APIC)
2329 aValue = TRUE;
2330 i_setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 mHWData->mAPIC = !!aValue;
2333 break;
2334
2335 case CPUPropertyType_X2APIC:
2336 i_setModified(IsModified_MachineData);
2337 mHWData.backup();
2338 mHWData->mX2APIC = !!aValue;
2339 if (aValue)
2340 mHWData->mAPIC = !!aValue;
2341 break;
2342
2343 case CPUPropertyType_IBPBOnVMExit:
2344 i_setModified(IsModified_MachineData);
2345 mHWData.backup();
2346 mHWData->mIBPBOnVMExit = !!aValue;
2347 break;
2348
2349 case CPUPropertyType_IBPBOnVMEntry:
2350 i_setModified(IsModified_MachineData);
2351 mHWData.backup();
2352 mHWData->mIBPBOnVMEntry = !!aValue;
2353 break;
2354
2355 case CPUPropertyType_SpecCtrl:
2356 i_setModified(IsModified_MachineData);
2357 mHWData.backup();
2358 mHWData->mSpecCtrl = !!aValue;
2359 break;
2360
2361 case CPUPropertyType_SpecCtrlByHost:
2362 i_setModified(IsModified_MachineData);
2363 mHWData.backup();
2364 mHWData->mSpecCtrlByHost = !!aValue;
2365 break;
2366
2367 case CPUPropertyType_HWVirt:
2368 i_setModified(IsModified_MachineData);
2369 mHWData.backup();
2370 mHWData->mNestedHWVirt = !!aValue;
2371 break;
2372
2373 default:
2374 return E_INVALIDARG;
2375 }
2376 return S_OK;
2377}
2378
2379HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2380 ULONG *aValEcx, ULONG *aValEdx)
2381{
2382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2383 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2384 {
2385 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2386 it != mHWData->mCpuIdLeafList.end();
2387 ++it)
2388 {
2389 if (aOrdinal == 0)
2390 {
2391 const settings::CpuIdLeaf &rLeaf= *it;
2392 *aIdx = rLeaf.idx;
2393 *aSubIdx = rLeaf.idxSub;
2394 *aValEax = rLeaf.uEax;
2395 *aValEbx = rLeaf.uEbx;
2396 *aValEcx = rLeaf.uEcx;
2397 *aValEdx = rLeaf.uEdx;
2398 return S_OK;
2399 }
2400 aOrdinal--;
2401 }
2402 }
2403 return E_INVALIDARG;
2404}
2405
2406HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2407{
2408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2409
2410 /*
2411 * Search the list.
2412 */
2413 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2414 {
2415 const settings::CpuIdLeaf &rLeaf= *it;
2416 if ( rLeaf.idx == aIdx
2417 && ( aSubIdx == UINT32_MAX
2418 || rLeaf.idxSub == aSubIdx) )
2419 {
2420 *aValEax = rLeaf.uEax;
2421 *aValEbx = rLeaf.uEbx;
2422 *aValEcx = rLeaf.uEcx;
2423 *aValEdx = rLeaf.uEdx;
2424 return S_OK;
2425 }
2426 }
2427
2428 return E_INVALIDARG;
2429}
2430
2431
2432HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2433{
2434 /*
2435 * Validate input before taking locks and checking state.
2436 */
2437 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2438 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2439 if ( aIdx >= UINT32_C(0x20)
2440 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2441 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2442 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2443
2444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2445 HRESULT rc = i_checkStateDependency(MutableStateDep);
2446 if (FAILED(rc)) return rc;
2447
2448 /*
2449 * Impose a maximum number of leaves.
2450 */
2451 if (mHWData->mCpuIdLeafList.size() > 256)
2452 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2453
2454 /*
2455 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2456 */
2457 i_setModified(IsModified_MachineData);
2458 mHWData.backup();
2459
2460 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2461 {
2462 settings::CpuIdLeaf &rLeaf= *it;
2463 if ( rLeaf.idx == aIdx
2464 && ( aSubIdx == UINT32_MAX
2465 || rLeaf.idxSub == aSubIdx) )
2466 it = mHWData->mCpuIdLeafList.erase(it);
2467 else
2468 ++it;
2469 }
2470
2471 settings::CpuIdLeaf NewLeaf;
2472 NewLeaf.idx = aIdx;
2473 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2474 NewLeaf.uEax = aValEax;
2475 NewLeaf.uEbx = aValEbx;
2476 NewLeaf.uEcx = aValEcx;
2477 NewLeaf.uEdx = aValEdx;
2478 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2479 return S_OK;
2480}
2481
2482HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2483{
2484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2485
2486 HRESULT rc = i_checkStateDependency(MutableStateDep);
2487 if (FAILED(rc)) return rc;
2488
2489 /*
2490 * Do the removal.
2491 */
2492 bool fModified = false;
2493 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2494 {
2495 settings::CpuIdLeaf &rLeaf= *it;
2496 if ( rLeaf.idx == aIdx
2497 && ( aSubIdx == UINT32_MAX
2498 || rLeaf.idxSub == aSubIdx) )
2499 {
2500 if (!fModified)
2501 {
2502 fModified = true;
2503 i_setModified(IsModified_MachineData);
2504 mHWData.backup();
2505 }
2506 it = mHWData->mCpuIdLeafList.erase(it);
2507 }
2508 else
2509 ++it;
2510 }
2511
2512 return S_OK;
2513}
2514
2515HRESULT Machine::removeAllCPUIDLeaves()
2516{
2517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2518
2519 HRESULT rc = i_checkStateDependency(MutableStateDep);
2520 if (FAILED(rc)) return rc;
2521
2522 if (mHWData->mCpuIdLeafList.size() > 0)
2523 {
2524 i_setModified(IsModified_MachineData);
2525 mHWData.backup();
2526
2527 mHWData->mCpuIdLeafList.clear();
2528 }
2529
2530 return S_OK;
2531}
2532HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2533{
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 switch(aProperty)
2537 {
2538 case HWVirtExPropertyType_Enabled:
2539 *aValue = mHWData->mHWVirtExEnabled;
2540 break;
2541
2542 case HWVirtExPropertyType_VPID:
2543 *aValue = mHWData->mHWVirtExVPIDEnabled;
2544 break;
2545
2546 case HWVirtExPropertyType_NestedPaging:
2547 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2548 break;
2549
2550 case HWVirtExPropertyType_UnrestrictedExecution:
2551 *aValue = mHWData->mHWVirtExUXEnabled;
2552 break;
2553
2554 case HWVirtExPropertyType_LargePages:
2555 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2556#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2557 *aValue = FALSE;
2558#endif
2559 break;
2560
2561 case HWVirtExPropertyType_Force:
2562 *aValue = mHWData->mHWVirtExForceEnabled;
2563 break;
2564
2565 case HWVirtExPropertyType_UseNativeApi:
2566 *aValue = mHWData->mHWVirtExUseNativeApi;
2567 break;
2568
2569 default:
2570 return E_INVALIDARG;
2571 }
2572 return S_OK;
2573}
2574
2575HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2576{
2577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2578
2579 HRESULT rc = i_checkStateDependency(MutableStateDep);
2580 if (FAILED(rc)) return rc;
2581
2582 switch (aProperty)
2583 {
2584 case HWVirtExPropertyType_Enabled:
2585 i_setModified(IsModified_MachineData);
2586 mHWData.backup();
2587 mHWData->mHWVirtExEnabled = !!aValue;
2588 break;
2589
2590 case HWVirtExPropertyType_VPID:
2591 i_setModified(IsModified_MachineData);
2592 mHWData.backup();
2593 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2594 break;
2595
2596 case HWVirtExPropertyType_NestedPaging:
2597 i_setModified(IsModified_MachineData);
2598 mHWData.backup();
2599 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2600 break;
2601
2602 case HWVirtExPropertyType_UnrestrictedExecution:
2603 i_setModified(IsModified_MachineData);
2604 mHWData.backup();
2605 mHWData->mHWVirtExUXEnabled = !!aValue;
2606 break;
2607
2608 case HWVirtExPropertyType_LargePages:
2609 i_setModified(IsModified_MachineData);
2610 mHWData.backup();
2611 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2612 break;
2613
2614 case HWVirtExPropertyType_Force:
2615 i_setModified(IsModified_MachineData);
2616 mHWData.backup();
2617 mHWData->mHWVirtExForceEnabled = !!aValue;
2618 break;
2619
2620 case HWVirtExPropertyType_UseNativeApi:
2621 i_setModified(IsModified_MachineData);
2622 mHWData.backup();
2623 mHWData->mHWVirtExUseNativeApi = !!aValue;
2624 break;
2625
2626 default:
2627 return E_INVALIDARG;
2628 }
2629
2630 return S_OK;
2631}
2632
2633HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2634{
2635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2636
2637 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2638
2639 return S_OK;
2640}
2641
2642HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2643{
2644 /** @todo (r=dmik):
2645 * 1. Allow to change the name of the snapshot folder containing snapshots
2646 * 2. Rename the folder on disk instead of just changing the property
2647 * value (to be smart and not to leave garbage). Note that it cannot be
2648 * done here because the change may be rolled back. Thus, the right
2649 * place is #saveSettings().
2650 */
2651
2652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2653
2654 HRESULT rc = i_checkStateDependency(MutableStateDep);
2655 if (FAILED(rc)) return rc;
2656
2657 if (!mData->mCurrentSnapshot.isNull())
2658 return setError(E_FAIL,
2659 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2660
2661 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2662
2663 if (strSnapshotFolder.isEmpty())
2664 strSnapshotFolder = "Snapshots";
2665 int vrc = i_calculateFullPath(strSnapshotFolder,
2666 strSnapshotFolder);
2667 if (RT_FAILURE(vrc))
2668 return setErrorBoth(E_FAIL, vrc,
2669 tr("Invalid snapshot folder '%s' (%Rrc)"),
2670 strSnapshotFolder.c_str(), vrc);
2671
2672 i_setModified(IsModified_MachineData);
2673 mUserData.backup();
2674
2675 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2676
2677 return S_OK;
2678}
2679
2680HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2681{
2682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 aMediumAttachments.resize(mMediumAttachments->size());
2685 size_t i = 0;
2686 for (MediumAttachmentList::const_iterator
2687 it = mMediumAttachments->begin();
2688 it != mMediumAttachments->end();
2689 ++it, ++i)
2690 aMediumAttachments[i] = *it;
2691
2692 return S_OK;
2693}
2694
2695HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2696{
2697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2698
2699 Assert(!!mVRDEServer);
2700
2701 aVRDEServer = mVRDEServer;
2702
2703 return S_OK;
2704}
2705
2706HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2707{
2708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2709
2710 aAudioAdapter = mAudioAdapter;
2711
2712 return S_OK;
2713}
2714
2715HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2716{
2717#ifdef VBOX_WITH_VUSB
2718 clearError();
2719 MultiResult rc(S_OK);
2720
2721# ifdef VBOX_WITH_USB
2722 rc = mParent->i_host()->i_checkUSBProxyService();
2723 if (FAILED(rc)) return rc;
2724# endif
2725
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 aUSBControllers.resize(mUSBControllers->size());
2729 size_t i = 0;
2730 for (USBControllerList::const_iterator
2731 it = mUSBControllers->begin();
2732 it != mUSBControllers->end();
2733 ++it, ++i)
2734 aUSBControllers[i] = *it;
2735
2736 return S_OK;
2737#else
2738 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2739 * extended error info to indicate that USB is simply not available
2740 * (w/o treating it as a failure), for example, as in OSE */
2741 NOREF(aUSBControllers);
2742 ReturnComNotImplemented();
2743#endif /* VBOX_WITH_VUSB */
2744}
2745
2746HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2747{
2748#ifdef VBOX_WITH_VUSB
2749 clearError();
2750 MultiResult rc(S_OK);
2751
2752# ifdef VBOX_WITH_USB
2753 rc = mParent->i_host()->i_checkUSBProxyService();
2754 if (FAILED(rc)) return rc;
2755# endif
2756
2757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2758
2759 aUSBDeviceFilters = mUSBDeviceFilters;
2760 return rc;
2761#else
2762 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2763 * extended error info to indicate that USB is simply not available
2764 * (w/o treating it as a failure), for example, as in OSE */
2765 NOREF(aUSBDeviceFilters);
2766 ReturnComNotImplemented();
2767#endif /* VBOX_WITH_VUSB */
2768}
2769
2770HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 aSettingsFilePath = mData->m_strConfigFileFull;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2780{
2781 RT_NOREF(aSettingsFilePath);
2782 ReturnComNotImplemented();
2783}
2784
2785HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2786{
2787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2788
2789 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2790 if (FAILED(rc)) return rc;
2791
2792 if (!mData->pMachineConfigFile->fileExists())
2793 // this is a new machine, and no config file exists yet:
2794 *aSettingsModified = TRUE;
2795 else
2796 *aSettingsModified = (mData->flModifications != 0);
2797
2798 return S_OK;
2799}
2800
2801HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2802{
2803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2804
2805 *aSessionState = mData->mSession.mState;
2806
2807 return S_OK;
2808}
2809
2810HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2811{
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 aSessionName = mData->mSession.mName;
2815
2816 return S_OK;
2817}
2818
2819HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2820{
2821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2822
2823 *aSessionPID = mData->mSession.mPID;
2824
2825 return S_OK;
2826}
2827
2828HRESULT Machine::getState(MachineState_T *aState)
2829{
2830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2831
2832 *aState = mData->mMachineState;
2833 Assert(mData->mMachineState != MachineState_Null);
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2843
2844 return S_OK;
2845}
2846
2847HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2848{
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 aStateFilePath = mSSData->strStateFilePath;
2852
2853 return S_OK;
2854}
2855
2856HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2857{
2858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2859
2860 i_getLogFolder(aLogFolder);
2861
2862 return S_OK;
2863}
2864
2865HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2866{
2867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 aCurrentSnapshot = mData->mCurrentSnapshot;
2870
2871 return S_OK;
2872}
2873
2874HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2875{
2876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2877
2878 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2879 ? 0
2880 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2881
2882 return S_OK;
2883}
2884
2885HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2886{
2887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2888
2889 /* Note: for machines with no snapshots, we always return FALSE
2890 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2891 * reasons :) */
2892
2893 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2894 ? FALSE
2895 : mData->mCurrentStateModified;
2896
2897 return S_OK;
2898}
2899
2900HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2901{
2902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2903
2904 aSharedFolders.resize(mHWData->mSharedFolders.size());
2905 size_t i = 0;
2906 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2907 it = mHWData->mSharedFolders.begin();
2908 it != mHWData->mSharedFolders.end();
2909 ++it, ++i)
2910 aSharedFolders[i] = *it;
2911
2912 return S_OK;
2913}
2914
2915HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2916{
2917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 *aClipboardMode = mHWData->mClipboardMode;
2920
2921 return S_OK;
2922}
2923
2924HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2925{
2926 HRESULT rc = S_OK;
2927
2928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 alock.release();
2931 rc = i_onClipboardModeChange(aClipboardMode);
2932 alock.acquire();
2933 if (FAILED(rc)) return rc;
2934
2935 i_setModified(IsModified_MachineData);
2936 mHWData.backup();
2937 mHWData->mClipboardMode = aClipboardMode;
2938
2939 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2940 if (Global::IsOnline(mData->mMachineState))
2941 i_saveSettings(NULL);
2942
2943 return S_OK;
2944}
2945
2946HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2947{
2948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 *aDnDMode = mHWData->mDnDMode;
2951
2952 return S_OK;
2953}
2954
2955HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2956{
2957 HRESULT rc = S_OK;
2958
2959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 alock.release();
2962 rc = i_onDnDModeChange(aDnDMode);
2963
2964 alock.acquire();
2965 if (FAILED(rc)) return rc;
2966
2967 i_setModified(IsModified_MachineData);
2968 mHWData.backup();
2969 mHWData->mDnDMode = aDnDMode;
2970
2971 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2972 if (Global::IsOnline(mData->mMachineState))
2973 i_saveSettings(NULL);
2974
2975 return S_OK;
2976}
2977
2978HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2979{
2980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2981
2982 aStorageControllers.resize(mStorageControllers->size());
2983 size_t i = 0;
2984 for (StorageControllerList::const_iterator
2985 it = mStorageControllers->begin();
2986 it != mStorageControllers->end();
2987 ++it, ++i)
2988 aStorageControllers[i] = *it;
2989
2990 return S_OK;
2991}
2992
2993HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2994{
2995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2996
2997 *aEnabled = mUserData->s.fTeleporterEnabled;
2998
2999 return S_OK;
3000}
3001
3002HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3003{
3004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3005
3006 /* Only allow it to be set to true when PoweredOff or Aborted.
3007 (Clearing it is always permitted.) */
3008 if ( aTeleporterEnabled
3009 && mData->mRegistered
3010 && ( !i_isSessionMachine()
3011 || ( mData->mMachineState != MachineState_PoweredOff
3012 && mData->mMachineState != MachineState_Teleported
3013 && mData->mMachineState != MachineState_Aborted
3014 )
3015 )
3016 )
3017 return setError(VBOX_E_INVALID_VM_STATE,
3018 tr("The machine is not powered off (state is %s)"),
3019 Global::stringifyMachineState(mData->mMachineState));
3020
3021 i_setModified(IsModified_MachineData);
3022 mUserData.backup();
3023 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3024
3025 return S_OK;
3026}
3027
3028HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3029{
3030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3031
3032 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3033
3034 return S_OK;
3035}
3036
3037HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3038{
3039 if (aTeleporterPort >= _64K)
3040 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3041
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043
3044 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3045 if (FAILED(rc)) return rc;
3046
3047 i_setModified(IsModified_MachineData);
3048 mUserData.backup();
3049 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3050
3051 return S_OK;
3052}
3053
3054HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3055{
3056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3059
3060 return S_OK;
3061}
3062
3063HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3064{
3065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3066
3067 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3068 if (FAILED(rc)) return rc;
3069
3070 i_setModified(IsModified_MachineData);
3071 mUserData.backup();
3072 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3073
3074 return S_OK;
3075}
3076
3077HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3078{
3079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3080 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3081
3082 return S_OK;
3083}
3084
3085HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3086{
3087 /*
3088 * Hash the password first.
3089 */
3090 com::Utf8Str aT = aTeleporterPassword;
3091
3092 if (!aT.isEmpty())
3093 {
3094 if (VBoxIsPasswordHashed(&aT))
3095 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3096 VBoxHashPassword(&aT);
3097 }
3098
3099 /*
3100 * Do the update.
3101 */
3102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3103 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3104 if (SUCCEEDED(hrc))
3105 {
3106 i_setModified(IsModified_MachineData);
3107 mUserData.backup();
3108 mUserData->s.strTeleporterPassword = aT;
3109 }
3110
3111 return hrc;
3112}
3113
3114HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3115{
3116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3117
3118 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3119 return S_OK;
3120}
3121
3122HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3123{
3124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3125
3126 /** @todo deal with running state change. */
3127 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3128 if (FAILED(rc)) return rc;
3129
3130 i_setModified(IsModified_MachineData);
3131 mUserData.backup();
3132 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3133 return S_OK;
3134}
3135
3136HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3137{
3138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3139
3140 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3141 return S_OK;
3142}
3143
3144HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3145{
3146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3147
3148 /** @todo deal with running state change. */
3149 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3150 if (FAILED(rc)) return rc;
3151
3152 i_setModified(IsModified_MachineData);
3153 mUserData.backup();
3154 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3155 return S_OK;
3156}
3157
3158HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3159{
3160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3163 return S_OK;
3164}
3165
3166HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3167{
3168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 /** @todo deal with running state change. */
3171 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3172 if (FAILED(rc)) return rc;
3173
3174 i_setModified(IsModified_MachineData);
3175 mUserData.backup();
3176 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3177 return S_OK;
3178}
3179
3180HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3181{
3182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3185
3186 return S_OK;
3187}
3188
3189HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3190{
3191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3192
3193 /** @todo deal with running state change. */
3194 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3195 if (FAILED(rc)) return rc;
3196
3197 i_setModified(IsModified_MachineData);
3198 mUserData.backup();
3199 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3200
3201 return S_OK;
3202}
3203
3204HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3205{
3206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3207
3208 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3209 return S_OK;
3210}
3211
3212HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3213{
3214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3215
3216 /** @todo deal with running state change. */
3217 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3218 if (FAILED(rc)) return rc;
3219
3220 i_setModified(IsModified_MachineData);
3221 mUserData.backup();
3222 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3223 return S_OK;
3224}
3225
3226HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3227{
3228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3229
3230 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3231
3232 return S_OK;
3233}
3234
3235HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3236{
3237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3238
3239 /* Only allow it to be set to true when PoweredOff or Aborted.
3240 (Clearing it is always permitted.) */
3241 if ( aRTCUseUTC
3242 && mData->mRegistered
3243 && ( !i_isSessionMachine()
3244 || ( mData->mMachineState != MachineState_PoweredOff
3245 && mData->mMachineState != MachineState_Teleported
3246 && mData->mMachineState != MachineState_Aborted
3247 )
3248 )
3249 )
3250 return setError(VBOX_E_INVALID_VM_STATE,
3251 tr("The machine is not powered off (state is %s)"),
3252 Global::stringifyMachineState(mData->mMachineState));
3253
3254 i_setModified(IsModified_MachineData);
3255 mUserData.backup();
3256 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3257
3258 return S_OK;
3259}
3260
3261HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3262{
3263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3264
3265 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3266
3267 return S_OK;
3268}
3269
3270HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3271{
3272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3273
3274 HRESULT rc = i_checkStateDependency(MutableStateDep);
3275 if (FAILED(rc)) return rc;
3276
3277 i_setModified(IsModified_MachineData);
3278 mHWData.backup();
3279 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3280
3281 return S_OK;
3282}
3283
3284HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3285{
3286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3287
3288 *aIOCacheSize = mHWData->mIOCacheSize;
3289
3290 return S_OK;
3291}
3292
3293HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3294{
3295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3296
3297 HRESULT rc = i_checkStateDependency(MutableStateDep);
3298 if (FAILED(rc)) return rc;
3299
3300 i_setModified(IsModified_MachineData);
3301 mHWData.backup();
3302 mHWData->mIOCacheSize = aIOCacheSize;
3303
3304 return S_OK;
3305}
3306
3307
3308/**
3309 * @note Locks objects!
3310 */
3311HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3312 LockType_T aLockType)
3313{
3314 /* check the session state */
3315 SessionState_T state;
3316 HRESULT rc = aSession->COMGETTER(State)(&state);
3317 if (FAILED(rc)) return rc;
3318
3319 if (state != SessionState_Unlocked)
3320 return setError(VBOX_E_INVALID_OBJECT_STATE,
3321 tr("The given session is busy"));
3322
3323 // get the client's IInternalSessionControl interface
3324 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3325 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3326 E_INVALIDARG);
3327
3328 // session name (only used in some code paths)
3329 Utf8Str strSessionName;
3330
3331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3332
3333 if (!mData->mRegistered)
3334 return setError(E_UNEXPECTED,
3335 tr("The machine '%s' is not registered"),
3336 mUserData->s.strName.c_str());
3337
3338 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3339
3340 SessionState_T oldState = mData->mSession.mState;
3341 /* Hack: in case the session is closing and there is a progress object
3342 * which allows waiting for the session to be closed, take the opportunity
3343 * and do a limited wait (max. 1 second). This helps a lot when the system
3344 * is busy and thus session closing can take a little while. */
3345 if ( mData->mSession.mState == SessionState_Unlocking
3346 && mData->mSession.mProgress)
3347 {
3348 alock.release();
3349 mData->mSession.mProgress->WaitForCompletion(1000);
3350 alock.acquire();
3351 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3352 }
3353
3354 // try again now
3355 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3356 // (i.e. session machine exists)
3357 && (aLockType == LockType_Shared) // caller wants a shared link to the
3358 // existing session that holds the write lock:
3359 )
3360 {
3361 // OK, share the session... we are now dealing with three processes:
3362 // 1) VBoxSVC (where this code runs);
3363 // 2) process C: the caller's client process (who wants a shared session);
3364 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3365
3366 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3367 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3368 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3369 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3370 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3371
3372 /*
3373 * Release the lock before calling the client process. It's safe here
3374 * since the only thing to do after we get the lock again is to add
3375 * the remote control to the list (which doesn't directly influence
3376 * anything).
3377 */
3378 alock.release();
3379
3380 // get the console of the session holding the write lock (this is a remote call)
3381 ComPtr<IConsole> pConsoleW;
3382 if (mData->mSession.mLockType == LockType_VM)
3383 {
3384 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3385 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3386 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3387 if (FAILED(rc))
3388 // the failure may occur w/o any error info (from RPC), so provide one
3389 return setError(VBOX_E_VM_ERROR,
3390 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3391 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3392 }
3393
3394 // share the session machine and W's console with the caller's session
3395 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3396 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3397 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3398
3399 if (FAILED(rc))
3400 // the failure may occur w/o any error info (from RPC), so provide one
3401 return setError(VBOX_E_VM_ERROR,
3402 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3403 alock.acquire();
3404
3405 // need to revalidate the state after acquiring the lock again
3406 if (mData->mSession.mState != SessionState_Locked)
3407 {
3408 pSessionControl->Uninitialize();
3409 return setError(VBOX_E_INVALID_SESSION_STATE,
3410 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3411 mUserData->s.strName.c_str());
3412 }
3413
3414 // add the caller's session to the list
3415 mData->mSession.mRemoteControls.push_back(pSessionControl);
3416 }
3417 else if ( mData->mSession.mState == SessionState_Locked
3418 || mData->mSession.mState == SessionState_Unlocking
3419 )
3420 {
3421 // sharing not permitted, or machine still unlocking:
3422 return setError(VBOX_E_INVALID_OBJECT_STATE,
3423 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3424 mUserData->s.strName.c_str());
3425 }
3426 else
3427 {
3428 // machine is not locked: then write-lock the machine (create the session machine)
3429
3430 // must not be busy
3431 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3432
3433 // get the caller's session PID
3434 RTPROCESS pid = NIL_RTPROCESS;
3435 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3436 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3437 Assert(pid != NIL_RTPROCESS);
3438
3439 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3440
3441 if (fLaunchingVMProcess)
3442 {
3443 if (mData->mSession.mPID == NIL_RTPROCESS)
3444 {
3445 // two or more clients racing for a lock, the one which set the
3446 // session state to Spawning will win, the others will get an
3447 // error as we can't decide here if waiting a little would help
3448 // (only for shared locks this would avoid an error)
3449 return setError(VBOX_E_INVALID_OBJECT_STATE,
3450 tr("The machine '%s' already has a lock request pending"),
3451 mUserData->s.strName.c_str());
3452 }
3453
3454 // this machine is awaiting for a spawning session to be opened:
3455 // then the calling process must be the one that got started by
3456 // LaunchVMProcess()
3457
3458 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3459 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3460
3461#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3462 /* Hardened windows builds spawns three processes when a VM is
3463 launched, the 3rd one is the one that will end up here. */
3464 RTPROCESS ppid;
3465 int rc = RTProcQueryParent(pid, &ppid);
3466 if (RT_SUCCESS(rc))
3467 rc = RTProcQueryParent(ppid, &ppid);
3468 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3469 || rc == VERR_ACCESS_DENIED)
3470 {
3471 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3472 mData->mSession.mPID = pid;
3473 }
3474#endif
3475
3476 if (mData->mSession.mPID != pid)
3477 return setError(E_ACCESSDENIED,
3478 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3479 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3480 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3481 }
3482
3483 // create the mutable SessionMachine from the current machine
3484 ComObjPtr<SessionMachine> sessionMachine;
3485 sessionMachine.createObject();
3486 rc = sessionMachine->init(this);
3487 AssertComRC(rc);
3488
3489 /* NOTE: doing return from this function after this point but
3490 * before the end is forbidden since it may call SessionMachine::uninit()
3491 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3492 * lock while still holding the Machine lock in alock so that a deadlock
3493 * is possible due to the wrong lock order. */
3494
3495 if (SUCCEEDED(rc))
3496 {
3497 /*
3498 * Set the session state to Spawning to protect against subsequent
3499 * attempts to open a session and to unregister the machine after
3500 * we release the lock.
3501 */
3502 SessionState_T origState = mData->mSession.mState;
3503 mData->mSession.mState = SessionState_Spawning;
3504
3505#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3506 /* Get the client token ID to be passed to the client process */
3507 Utf8Str strTokenId;
3508 sessionMachine->i_getTokenId(strTokenId);
3509 Assert(!strTokenId.isEmpty());
3510#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3511 /* Get the client token to be passed to the client process */
3512 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3513 /* The token is now "owned" by pToken, fix refcount */
3514 if (!pToken.isNull())
3515 pToken->Release();
3516#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3517
3518 /*
3519 * Release the lock before calling the client process -- it will call
3520 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3521 * because the state is Spawning, so that LaunchVMProcess() and
3522 * LockMachine() calls will fail. This method, called before we
3523 * acquire the lock again, will fail because of the wrong PID.
3524 *
3525 * Note that mData->mSession.mRemoteControls accessed outside
3526 * the lock may not be modified when state is Spawning, so it's safe.
3527 */
3528 alock.release();
3529
3530 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3531#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3532 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3533#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3534 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3535 /* Now the token is owned by the client process. */
3536 pToken.setNull();
3537#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3538 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3539
3540 /* The failure may occur w/o any error info (from RPC), so provide one */
3541 if (FAILED(rc))
3542 setError(VBOX_E_VM_ERROR,
3543 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3544
3545 // get session name, either to remember or to compare against
3546 // the already known session name.
3547 {
3548 Bstr bstrSessionName;
3549 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3550 if (SUCCEEDED(rc2))
3551 strSessionName = bstrSessionName;
3552 }
3553
3554 if ( SUCCEEDED(rc)
3555 && fLaunchingVMProcess
3556 )
3557 {
3558 /* complete the remote session initialization */
3559
3560 /* get the console from the direct session */
3561 ComPtr<IConsole> console;
3562 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3563 ComAssertComRC(rc);
3564
3565 if (SUCCEEDED(rc) && !console)
3566 {
3567 ComAssert(!!console);
3568 rc = E_FAIL;
3569 }
3570
3571 /* assign machine & console to the remote session */
3572 if (SUCCEEDED(rc))
3573 {
3574 /*
3575 * after LaunchVMProcess(), the first and the only
3576 * entry in remoteControls is that remote session
3577 */
3578 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3579 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3580 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3581
3582 /* The failure may occur w/o any error info (from RPC), so provide one */
3583 if (FAILED(rc))
3584 setError(VBOX_E_VM_ERROR,
3585 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3586 }
3587
3588 if (FAILED(rc))
3589 pSessionControl->Uninitialize();
3590 }
3591
3592 /* acquire the lock again */
3593 alock.acquire();
3594
3595 /* Restore the session state */
3596 mData->mSession.mState = origState;
3597 }
3598
3599 // finalize spawning anyway (this is why we don't return on errors above)
3600 if (fLaunchingVMProcess)
3601 {
3602 Assert(mData->mSession.mName == strSessionName);
3603 /* Note that the progress object is finalized later */
3604 /** @todo Consider checking mData->mSession.mProgress for cancellation
3605 * around here. */
3606
3607 /* We don't reset mSession.mPID here because it is necessary for
3608 * SessionMachine::uninit() to reap the child process later. */
3609
3610 if (FAILED(rc))
3611 {
3612 /* Close the remote session, remove the remote control from the list
3613 * and reset session state to Closed (@note keep the code in sync
3614 * with the relevant part in checkForSpawnFailure()). */
3615
3616 Assert(mData->mSession.mRemoteControls.size() == 1);
3617 if (mData->mSession.mRemoteControls.size() == 1)
3618 {
3619 ErrorInfoKeeper eik;
3620 mData->mSession.mRemoteControls.front()->Uninitialize();
3621 }
3622
3623 mData->mSession.mRemoteControls.clear();
3624 mData->mSession.mState = SessionState_Unlocked;
3625 }
3626 }
3627 else
3628 {
3629 /* memorize PID of the directly opened session */
3630 if (SUCCEEDED(rc))
3631 mData->mSession.mPID = pid;
3632 }
3633
3634 if (SUCCEEDED(rc))
3635 {
3636 mData->mSession.mLockType = aLockType;
3637 /* memorize the direct session control and cache IUnknown for it */
3638 mData->mSession.mDirectControl = pSessionControl;
3639 mData->mSession.mState = SessionState_Locked;
3640 if (!fLaunchingVMProcess)
3641 mData->mSession.mName = strSessionName;
3642 /* associate the SessionMachine with this Machine */
3643 mData->mSession.mMachine = sessionMachine;
3644
3645 /* request an IUnknown pointer early from the remote party for later
3646 * identity checks (it will be internally cached within mDirectControl
3647 * at least on XPCOM) */
3648 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3649 NOREF(unk);
3650 }
3651
3652 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3653 * would break the lock order */
3654 alock.release();
3655
3656 /* uninitialize the created session machine on failure */
3657 if (FAILED(rc))
3658 sessionMachine->uninit();
3659 }
3660
3661 if (SUCCEEDED(rc))
3662 {
3663 /*
3664 * tell the client watcher thread to update the set of
3665 * machines that have open sessions
3666 */
3667 mParent->i_updateClientWatcher();
3668
3669 if (oldState != SessionState_Locked)
3670 /* fire an event */
3671 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3672 }
3673
3674 return rc;
3675}
3676
3677/**
3678 * @note Locks objects!
3679 */
3680HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3681 const com::Utf8Str &aName,
3682 const com::Utf8Str &aEnvironment,
3683 ComPtr<IProgress> &aProgress)
3684{
3685 Utf8Str strFrontend(aName);
3686 /* "emergencystop" doesn't need the session, so skip the checks/interface
3687 * retrieval. This code doesn't quite fit in here, but introducing a
3688 * special API method would be even more effort, and would require explicit
3689 * support by every API client. It's better to hide the feature a bit. */
3690 if (strFrontend != "emergencystop")
3691 CheckComArgNotNull(aSession);
3692
3693 HRESULT rc = S_OK;
3694 if (strFrontend.isEmpty())
3695 {
3696 Bstr bstrFrontend;
3697 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3698 if (FAILED(rc))
3699 return rc;
3700 strFrontend = bstrFrontend;
3701 if (strFrontend.isEmpty())
3702 {
3703 ComPtr<ISystemProperties> systemProperties;
3704 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3705 if (FAILED(rc))
3706 return rc;
3707 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3708 if (FAILED(rc))
3709 return rc;
3710 strFrontend = bstrFrontend;
3711 }
3712 /* paranoia - emergencystop is not a valid default */
3713 if (strFrontend == "emergencystop")
3714 strFrontend = Utf8Str::Empty;
3715 }
3716 /* default frontend: Qt GUI */
3717 if (strFrontend.isEmpty())
3718 strFrontend = "GUI/Qt";
3719
3720 if (strFrontend != "emergencystop")
3721 {
3722 /* check the session state */
3723 SessionState_T state;
3724 rc = aSession->COMGETTER(State)(&state);
3725 if (FAILED(rc))
3726 return rc;
3727
3728 if (state != SessionState_Unlocked)
3729 return setError(VBOX_E_INVALID_OBJECT_STATE,
3730 tr("The given session is busy"));
3731
3732 /* get the IInternalSessionControl interface */
3733 ComPtr<IInternalSessionControl> control(aSession);
3734 ComAssertMsgRet(!control.isNull(),
3735 ("No IInternalSessionControl interface"),
3736 E_INVALIDARG);
3737
3738 /* get the teleporter enable state for the progress object init. */
3739 BOOL fTeleporterEnabled;
3740 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3741 if (FAILED(rc))
3742 return rc;
3743
3744 /* create a progress object */
3745 ComObjPtr<ProgressProxy> progress;
3746 progress.createObject();
3747 rc = progress->init(mParent,
3748 static_cast<IMachine*>(this),
3749 Bstr(tr("Starting VM")).raw(),
3750 TRUE /* aCancelable */,
3751 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3752 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3753 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3754 2 /* uFirstOperationWeight */,
3755 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3756
3757 if (SUCCEEDED(rc))
3758 {
3759 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3760 if (SUCCEEDED(rc))
3761 {
3762 aProgress = progress;
3763
3764 /* signal the client watcher thread */
3765 mParent->i_updateClientWatcher();
3766
3767 /* fire an event */
3768 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3769 }
3770 }
3771 }
3772 else
3773 {
3774 /* no progress object - either instant success or failure */
3775 aProgress = NULL;
3776
3777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3778
3779 if (mData->mSession.mState != SessionState_Locked)
3780 return setError(VBOX_E_INVALID_OBJECT_STATE,
3781 tr("The machine '%s' is not locked by a session"),
3782 mUserData->s.strName.c_str());
3783
3784 /* must have a VM process associated - do not kill normal API clients
3785 * with an open session */
3786 if (!Global::IsOnline(mData->mMachineState))
3787 return setError(VBOX_E_INVALID_OBJECT_STATE,
3788 tr("The machine '%s' does not have a VM process"),
3789 mUserData->s.strName.c_str());
3790
3791 /* forcibly terminate the VM process */
3792 if (mData->mSession.mPID != NIL_RTPROCESS)
3793 RTProcTerminate(mData->mSession.mPID);
3794
3795 /* signal the client watcher thread, as most likely the client has
3796 * been terminated */
3797 mParent->i_updateClientWatcher();
3798 }
3799
3800 return rc;
3801}
3802
3803HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3804{
3805 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3806 return setError(E_INVALIDARG,
3807 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3808 aPosition, SchemaDefs::MaxBootPosition);
3809
3810 if (aDevice == DeviceType_USB)
3811 return setError(E_NOTIMPL,
3812 tr("Booting from USB device is currently not supported"));
3813
3814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3815
3816 HRESULT rc = i_checkStateDependency(MutableStateDep);
3817 if (FAILED(rc)) return rc;
3818
3819 i_setModified(IsModified_MachineData);
3820 mHWData.backup();
3821 mHWData->mBootOrder[aPosition - 1] = aDevice;
3822
3823 return S_OK;
3824}
3825
3826HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3827{
3828 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3829 return setError(E_INVALIDARG,
3830 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3831 aPosition, SchemaDefs::MaxBootPosition);
3832
3833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3834
3835 *aDevice = mHWData->mBootOrder[aPosition - 1];
3836
3837 return S_OK;
3838}
3839
3840HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3841 LONG aControllerPort,
3842 LONG aDevice,
3843 DeviceType_T aType,
3844 const ComPtr<IMedium> &aMedium)
3845{
3846 IMedium *aM = aMedium;
3847 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3848 aName.c_str(), aControllerPort, aDevice, aType, aM));
3849
3850 // request the host lock first, since might be calling Host methods for getting host drives;
3851 // next, protect the media tree all the while we're in here, as well as our member variables
3852 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3853 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3854
3855 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3856 if (FAILED(rc)) return rc;
3857
3858 /// @todo NEWMEDIA implicit machine registration
3859 if (!mData->mRegistered)
3860 return setError(VBOX_E_INVALID_OBJECT_STATE,
3861 tr("Cannot attach storage devices to an unregistered machine"));
3862
3863 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3864
3865 /* Check for an existing controller. */
3866 ComObjPtr<StorageController> ctl;
3867 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3868 if (FAILED(rc)) return rc;
3869
3870 StorageControllerType_T ctrlType;
3871 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3872 if (FAILED(rc))
3873 return setError(E_FAIL,
3874 tr("Could not get type of controller '%s'"),
3875 aName.c_str());
3876
3877 bool fSilent = false;
3878 Utf8Str strReconfig;
3879
3880 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3881 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3882 if ( mData->mMachineState == MachineState_Paused
3883 && strReconfig == "1")
3884 fSilent = true;
3885
3886 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3887 bool fHotplug = false;
3888 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3889 fHotplug = true;
3890
3891 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3892 return setError(VBOX_E_INVALID_VM_STATE,
3893 tr("Controller '%s' does not support hotplugging"),
3894 aName.c_str());
3895
3896 // check that the port and device are not out of range
3897 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3898 if (FAILED(rc)) return rc;
3899
3900 /* check if the device slot is already busy */
3901 MediumAttachment *pAttachTemp;
3902 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3903 aName,
3904 aControllerPort,
3905 aDevice)))
3906 {
3907 Medium *pMedium = pAttachTemp->i_getMedium();
3908 if (pMedium)
3909 {
3910 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3911 return setError(VBOX_E_OBJECT_IN_USE,
3912 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3913 pMedium->i_getLocationFull().c_str(),
3914 aControllerPort,
3915 aDevice,
3916 aName.c_str());
3917 }
3918 else
3919 return setError(VBOX_E_OBJECT_IN_USE,
3920 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3921 aControllerPort, aDevice, aName.c_str());
3922 }
3923
3924 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3925 if (aMedium && medium.isNull())
3926 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3927
3928 AutoCaller mediumCaller(medium);
3929 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3930
3931 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3932
3933 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3934 && !medium.isNull()
3935 )
3936 return setError(VBOX_E_OBJECT_IN_USE,
3937 tr("Medium '%s' is already attached to this virtual machine"),
3938 medium->i_getLocationFull().c_str());
3939
3940 if (!medium.isNull())
3941 {
3942 MediumType_T mtype = medium->i_getType();
3943 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3944 // For DVDs it's not written to the config file, so needs no global config
3945 // version bump. For floppies it's a new attribute "type", which is ignored
3946 // by older VirtualBox version, so needs no global config version bump either.
3947 // For hard disks this type is not accepted.
3948 if (mtype == MediumType_MultiAttach)
3949 {
3950 // This type is new with VirtualBox 4.0 and therefore requires settings
3951 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3952 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3953 // two reasons: The medium type is a property of the media registry tree, which
3954 // can reside in the global config file (for pre-4.0 media); we would therefore
3955 // possibly need to bump the global config version. We don't want to do that though
3956 // because that might make downgrading to pre-4.0 impossible.
3957 // As a result, we can only use these two new types if the medium is NOT in the
3958 // global registry:
3959 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3960 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3961 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3962 )
3963 return setError(VBOX_E_INVALID_OBJECT_STATE,
3964 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3965 "to machines that were created with VirtualBox 4.0 or later"),
3966 medium->i_getLocationFull().c_str());
3967 }
3968 }
3969
3970 bool fIndirect = false;
3971 if (!medium.isNull())
3972 fIndirect = medium->i_isReadOnly();
3973 bool associate = true;
3974
3975 do
3976 {
3977 if ( aType == DeviceType_HardDisk
3978 && mMediumAttachments.isBackedUp())
3979 {
3980 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3981
3982 /* check if the medium was attached to the VM before we started
3983 * changing attachments in which case the attachment just needs to
3984 * be restored */
3985 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3986 {
3987 AssertReturn(!fIndirect, E_FAIL);
3988
3989 /* see if it's the same bus/channel/device */
3990 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3991 {
3992 /* the simplest case: restore the whole attachment
3993 * and return, nothing else to do */
3994 mMediumAttachments->push_back(pAttachTemp);
3995
3996 /* Reattach the medium to the VM. */
3997 if (fHotplug || fSilent)
3998 {
3999 mediumLock.release();
4000 treeLock.release();
4001 alock.release();
4002
4003 MediumLockList *pMediumLockList(new MediumLockList());
4004
4005 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4006 medium /* pToLockWrite */,
4007 false /* fMediumLockWriteAll */,
4008 NULL,
4009 *pMediumLockList);
4010 alock.acquire();
4011 if (FAILED(rc))
4012 delete pMediumLockList;
4013 else
4014 {
4015 mData->mSession.mLockedMedia.Unlock();
4016 alock.release();
4017 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4018 mData->mSession.mLockedMedia.Lock();
4019 alock.acquire();
4020 }
4021 alock.release();
4022
4023 if (SUCCEEDED(rc))
4024 {
4025 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4026 /* Remove lock list in case of error. */
4027 if (FAILED(rc))
4028 {
4029 mData->mSession.mLockedMedia.Unlock();
4030 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4031 mData->mSession.mLockedMedia.Lock();
4032 }
4033 }
4034 }
4035
4036 return S_OK;
4037 }
4038
4039 /* bus/channel/device differ; we need a new attachment object,
4040 * but don't try to associate it again */
4041 associate = false;
4042 break;
4043 }
4044 }
4045
4046 /* go further only if the attachment is to be indirect */
4047 if (!fIndirect)
4048 break;
4049
4050 /* perform the so called smart attachment logic for indirect
4051 * attachments. Note that smart attachment is only applicable to base
4052 * hard disks. */
4053
4054 if (medium->i_getParent().isNull())
4055 {
4056 /* first, investigate the backup copy of the current hard disk
4057 * attachments to make it possible to re-attach existing diffs to
4058 * another device slot w/o losing their contents */
4059 if (mMediumAttachments.isBackedUp())
4060 {
4061 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4062
4063 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4064 uint32_t foundLevel = 0;
4065
4066 for (MediumAttachmentList::const_iterator
4067 it = oldAtts.begin();
4068 it != oldAtts.end();
4069 ++it)
4070 {
4071 uint32_t level = 0;
4072 MediumAttachment *pAttach = *it;
4073 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4074 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4075 if (pMedium.isNull())
4076 continue;
4077
4078 if (pMedium->i_getBase(&level) == medium)
4079 {
4080 /* skip the hard disk if its currently attached (we
4081 * cannot attach the same hard disk twice) */
4082 if (i_findAttachment(*mMediumAttachments.data(),
4083 pMedium))
4084 continue;
4085
4086 /* matched device, channel and bus (i.e. attached to the
4087 * same place) will win and immediately stop the search;
4088 * otherwise the attachment that has the youngest
4089 * descendant of medium will be used
4090 */
4091 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4092 {
4093 /* the simplest case: restore the whole attachment
4094 * and return, nothing else to do */
4095 mMediumAttachments->push_back(*it);
4096
4097 /* Reattach the medium to the VM. */
4098 if (fHotplug || fSilent)
4099 {
4100 mediumLock.release();
4101 treeLock.release();
4102 alock.release();
4103
4104 MediumLockList *pMediumLockList(new MediumLockList());
4105
4106 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4107 medium /* pToLockWrite */,
4108 false /* fMediumLockWriteAll */,
4109 NULL,
4110 *pMediumLockList);
4111 alock.acquire();
4112 if (FAILED(rc))
4113 delete pMediumLockList;
4114 else
4115 {
4116 mData->mSession.mLockedMedia.Unlock();
4117 alock.release();
4118 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4119 mData->mSession.mLockedMedia.Lock();
4120 alock.acquire();
4121 }
4122 alock.release();
4123
4124 if (SUCCEEDED(rc))
4125 {
4126 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4127 /* Remove lock list in case of error. */
4128 if (FAILED(rc))
4129 {
4130 mData->mSession.mLockedMedia.Unlock();
4131 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4132 mData->mSession.mLockedMedia.Lock();
4133 }
4134 }
4135 }
4136
4137 return S_OK;
4138 }
4139 else if ( foundIt == oldAtts.end()
4140 || level > foundLevel /* prefer younger */
4141 )
4142 {
4143 foundIt = it;
4144 foundLevel = level;
4145 }
4146 }
4147 }
4148
4149 if (foundIt != oldAtts.end())
4150 {
4151 /* use the previously attached hard disk */
4152 medium = (*foundIt)->i_getMedium();
4153 mediumCaller.attach(medium);
4154 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4155 mediumLock.attach(medium);
4156 /* not implicit, doesn't require association with this VM */
4157 fIndirect = false;
4158 associate = false;
4159 /* go right to the MediumAttachment creation */
4160 break;
4161 }
4162 }
4163
4164 /* must give up the medium lock and medium tree lock as below we
4165 * go over snapshots, which needs a lock with higher lock order. */
4166 mediumLock.release();
4167 treeLock.release();
4168
4169 /* then, search through snapshots for the best diff in the given
4170 * hard disk's chain to base the new diff on */
4171
4172 ComObjPtr<Medium> base;
4173 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4174 while (snap)
4175 {
4176 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4177
4178 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4179
4180 MediumAttachment *pAttachFound = NULL;
4181 uint32_t foundLevel = 0;
4182
4183 for (MediumAttachmentList::const_iterator
4184 it = snapAtts.begin();
4185 it != snapAtts.end();
4186 ++it)
4187 {
4188 MediumAttachment *pAttach = *it;
4189 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4190 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4191 if (pMedium.isNull())
4192 continue;
4193
4194 uint32_t level = 0;
4195 if (pMedium->i_getBase(&level) == medium)
4196 {
4197 /* matched device, channel and bus (i.e. attached to the
4198 * same place) will win and immediately stop the search;
4199 * otherwise the attachment that has the youngest
4200 * descendant of medium will be used
4201 */
4202 if ( pAttach->i_getDevice() == aDevice
4203 && pAttach->i_getPort() == aControllerPort
4204 && pAttach->i_getControllerName() == aName
4205 )
4206 {
4207 pAttachFound = pAttach;
4208 break;
4209 }
4210 else if ( !pAttachFound
4211 || level > foundLevel /* prefer younger */
4212 )
4213 {
4214 pAttachFound = pAttach;
4215 foundLevel = level;
4216 }
4217 }
4218 }
4219
4220 if (pAttachFound)
4221 {
4222 base = pAttachFound->i_getMedium();
4223 break;
4224 }
4225
4226 snap = snap->i_getParent();
4227 }
4228
4229 /* re-lock medium tree and the medium, as we need it below */
4230 treeLock.acquire();
4231 mediumLock.acquire();
4232
4233 /* found a suitable diff, use it as a base */
4234 if (!base.isNull())
4235 {
4236 medium = base;
4237 mediumCaller.attach(medium);
4238 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4239 mediumLock.attach(medium);
4240 }
4241 }
4242
4243 Utf8Str strFullSnapshotFolder;
4244 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4245
4246 ComObjPtr<Medium> diff;
4247 diff.createObject();
4248 // store this diff in the same registry as the parent
4249 Guid uuidRegistryParent;
4250 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4251 {
4252 // parent image has no registry: this can happen if we're attaching a new immutable
4253 // image that has not yet been attached (medium then points to the base and we're
4254 // creating the diff image for the immutable, and the parent is not yet registered);
4255 // put the parent in the machine registry then
4256 mediumLock.release();
4257 treeLock.release();
4258 alock.release();
4259 i_addMediumToRegistry(medium);
4260 alock.acquire();
4261 treeLock.acquire();
4262 mediumLock.acquire();
4263 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4264 }
4265 rc = diff->init(mParent,
4266 medium->i_getPreferredDiffFormat(),
4267 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4268 uuidRegistryParent,
4269 DeviceType_HardDisk);
4270 if (FAILED(rc)) return rc;
4271
4272 /* Apply the normal locking logic to the entire chain. */
4273 MediumLockList *pMediumLockList(new MediumLockList());
4274 mediumLock.release();
4275 treeLock.release();
4276 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4277 diff /* pToLockWrite */,
4278 false /* fMediumLockWriteAll */,
4279 medium,
4280 *pMediumLockList);
4281 treeLock.acquire();
4282 mediumLock.acquire();
4283 if (SUCCEEDED(rc))
4284 {
4285 mediumLock.release();
4286 treeLock.release();
4287 rc = pMediumLockList->Lock();
4288 treeLock.acquire();
4289 mediumLock.acquire();
4290 if (FAILED(rc))
4291 setError(rc,
4292 tr("Could not lock medium when creating diff '%s'"),
4293 diff->i_getLocationFull().c_str());
4294 else
4295 {
4296 /* will release the lock before the potentially lengthy
4297 * operation, so protect with the special state */
4298 MachineState_T oldState = mData->mMachineState;
4299 i_setMachineState(MachineState_SettingUp);
4300
4301 mediumLock.release();
4302 treeLock.release();
4303 alock.release();
4304
4305 rc = medium->i_createDiffStorage(diff,
4306 medium->i_getPreferredDiffVariant(),
4307 pMediumLockList,
4308 NULL /* aProgress */,
4309 true /* aWait */);
4310
4311 alock.acquire();
4312 treeLock.acquire();
4313 mediumLock.acquire();
4314
4315 i_setMachineState(oldState);
4316 }
4317 }
4318
4319 /* Unlock the media and free the associated memory. */
4320 delete pMediumLockList;
4321
4322 if (FAILED(rc)) return rc;
4323
4324 /* use the created diff for the actual attachment */
4325 medium = diff;
4326 mediumCaller.attach(medium);
4327 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4328 mediumLock.attach(medium);
4329 }
4330 while (0);
4331
4332 ComObjPtr<MediumAttachment> attachment;
4333 attachment.createObject();
4334 rc = attachment->init(this,
4335 medium,
4336 aName,
4337 aControllerPort,
4338 aDevice,
4339 aType,
4340 fIndirect,
4341 false /* fPassthrough */,
4342 false /* fTempEject */,
4343 false /* fNonRotational */,
4344 false /* fDiscard */,
4345 fHotplug /* fHotPluggable */,
4346 Utf8Str::Empty);
4347 if (FAILED(rc)) return rc;
4348
4349 if (associate && !medium.isNull())
4350 {
4351 // as the last step, associate the medium to the VM
4352 rc = medium->i_addBackReference(mData->mUuid);
4353 // here we can fail because of Deleting, or being in process of creating a Diff
4354 if (FAILED(rc)) return rc;
4355
4356 mediumLock.release();
4357 treeLock.release();
4358 alock.release();
4359 i_addMediumToRegistry(medium);
4360 alock.acquire();
4361 treeLock.acquire();
4362 mediumLock.acquire();
4363 }
4364
4365 /* success: finally remember the attachment */
4366 i_setModified(IsModified_Storage);
4367 mMediumAttachments.backup();
4368 mMediumAttachments->push_back(attachment);
4369
4370 mediumLock.release();
4371 treeLock.release();
4372 alock.release();
4373
4374 if (fHotplug || fSilent)
4375 {
4376 if (!medium.isNull())
4377 {
4378 MediumLockList *pMediumLockList(new MediumLockList());
4379
4380 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4381 medium /* pToLockWrite */,
4382 false /* fMediumLockWriteAll */,
4383 NULL,
4384 *pMediumLockList);
4385 alock.acquire();
4386 if (FAILED(rc))
4387 delete pMediumLockList;
4388 else
4389 {
4390 mData->mSession.mLockedMedia.Unlock();
4391 alock.release();
4392 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4393 mData->mSession.mLockedMedia.Lock();
4394 alock.acquire();
4395 }
4396 alock.release();
4397 }
4398
4399 if (SUCCEEDED(rc))
4400 {
4401 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4402 /* Remove lock list in case of error. */
4403 if (FAILED(rc))
4404 {
4405 mData->mSession.mLockedMedia.Unlock();
4406 mData->mSession.mLockedMedia.Remove(attachment);
4407 mData->mSession.mLockedMedia.Lock();
4408 }
4409 }
4410 }
4411
4412 /* Save modified registries, but skip this machine as it's the caller's
4413 * job to save its settings like all other settings changes. */
4414 mParent->i_unmarkRegistryModified(i_getId());
4415 mParent->i_saveModifiedRegistries();
4416
4417 return rc;
4418}
4419
4420HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4421 LONG aDevice)
4422{
4423 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4424 aName.c_str(), aControllerPort, aDevice));
4425
4426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4427
4428 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4429 if (FAILED(rc)) return rc;
4430
4431 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4432
4433 /* Check for an existing controller. */
4434 ComObjPtr<StorageController> ctl;
4435 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4436 if (FAILED(rc)) return rc;
4437
4438 StorageControllerType_T ctrlType;
4439 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4440 if (FAILED(rc))
4441 return setError(E_FAIL,
4442 tr("Could not get type of controller '%s'"),
4443 aName.c_str());
4444
4445 bool fSilent = false;
4446 Utf8Str strReconfig;
4447
4448 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4449 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4450 if ( mData->mMachineState == MachineState_Paused
4451 && strReconfig == "1")
4452 fSilent = true;
4453
4454 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4455 bool fHotplug = false;
4456 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4457 fHotplug = true;
4458
4459 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4460 return setError(VBOX_E_INVALID_VM_STATE,
4461 tr("Controller '%s' does not support hotplugging"),
4462 aName.c_str());
4463
4464 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4465 aName,
4466 aControllerPort,
4467 aDevice);
4468 if (!pAttach)
4469 return setError(VBOX_E_OBJECT_NOT_FOUND,
4470 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4471 aDevice, aControllerPort, aName.c_str());
4472
4473 if (fHotplug && !pAttach->i_getHotPluggable())
4474 return setError(VBOX_E_NOT_SUPPORTED,
4475 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4476 aDevice, aControllerPort, aName.c_str());
4477
4478 /*
4479 * The VM has to detach the device before we delete any implicit diffs.
4480 * If this fails we can roll back without loosing data.
4481 */
4482 if (fHotplug || fSilent)
4483 {
4484 alock.release();
4485 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4486 alock.acquire();
4487 }
4488 if (FAILED(rc)) return rc;
4489
4490 /* If we are here everything went well and we can delete the implicit now. */
4491 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4492
4493 alock.release();
4494
4495 /* Save modified registries, but skip this machine as it's the caller's
4496 * job to save its settings like all other settings changes. */
4497 mParent->i_unmarkRegistryModified(i_getId());
4498 mParent->i_saveModifiedRegistries();
4499
4500 return rc;
4501}
4502
4503HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4504 LONG aDevice, BOOL aPassthrough)
4505{
4506 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4507 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4508
4509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4510
4511 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4512 if (FAILED(rc)) return rc;
4513
4514 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4515
4516 /* Check for an existing controller. */
4517 ComObjPtr<StorageController> ctl;
4518 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4519 if (FAILED(rc)) return rc;
4520
4521 StorageControllerType_T ctrlType;
4522 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4523 if (FAILED(rc))
4524 return setError(E_FAIL,
4525 tr("Could not get type of controller '%s'"),
4526 aName.c_str());
4527
4528 bool fSilent = false;
4529 Utf8Str strReconfig;
4530
4531 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4532 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4533 if ( mData->mMachineState == MachineState_Paused
4534 && strReconfig == "1")
4535 fSilent = true;
4536
4537 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4538 bool fHotplug = false;
4539 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4540 fHotplug = true;
4541
4542 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4543 return setError(VBOX_E_INVALID_VM_STATE,
4544 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4545 aName.c_str());
4546
4547 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4548 aName,
4549 aControllerPort,
4550 aDevice);
4551 if (!pAttach)
4552 return setError(VBOX_E_OBJECT_NOT_FOUND,
4553 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4554 aDevice, aControllerPort, aName.c_str());
4555
4556
4557 i_setModified(IsModified_Storage);
4558 mMediumAttachments.backup();
4559
4560 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4561
4562 if (pAttach->i_getType() != DeviceType_DVD)
4563 return setError(E_INVALIDARG,
4564 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4565 aDevice, aControllerPort, aName.c_str());
4566 pAttach->i_updatePassthrough(!!aPassthrough);
4567
4568 attLock.release();
4569 alock.release();
4570 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4571
4572 return rc;
4573}
4574
4575HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4576 LONG aDevice, BOOL aTemporaryEject)
4577{
4578
4579 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4580 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4581
4582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4583
4584 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4585 if (FAILED(rc)) return rc;
4586
4587 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4588 aName,
4589 aControllerPort,
4590 aDevice);
4591 if (!pAttach)
4592 return setError(VBOX_E_OBJECT_NOT_FOUND,
4593 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4594 aDevice, aControllerPort, aName.c_str());
4595
4596
4597 i_setModified(IsModified_Storage);
4598 mMediumAttachments.backup();
4599
4600 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4601
4602 if (pAttach->i_getType() != DeviceType_DVD)
4603 return setError(E_INVALIDARG,
4604 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4605 aDevice, aControllerPort, aName.c_str());
4606 pAttach->i_updateTempEject(!!aTemporaryEject);
4607
4608 return S_OK;
4609}
4610
4611HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4612 LONG aDevice, BOOL aNonRotational)
4613{
4614
4615 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4616 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4617
4618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4619
4620 HRESULT rc = i_checkStateDependency(MutableStateDep);
4621 if (FAILED(rc)) return rc;
4622
4623 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4624
4625 if (Global::IsOnlineOrTransient(mData->mMachineState))
4626 return setError(VBOX_E_INVALID_VM_STATE,
4627 tr("Invalid machine state: %s"),
4628 Global::stringifyMachineState(mData->mMachineState));
4629
4630 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4631 aName,
4632 aControllerPort,
4633 aDevice);
4634 if (!pAttach)
4635 return setError(VBOX_E_OBJECT_NOT_FOUND,
4636 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4637 aDevice, aControllerPort, aName.c_str());
4638
4639
4640 i_setModified(IsModified_Storage);
4641 mMediumAttachments.backup();
4642
4643 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4644
4645 if (pAttach->i_getType() != DeviceType_HardDisk)
4646 return setError(E_INVALIDARG,
4647 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"),
4648 aDevice, aControllerPort, aName.c_str());
4649 pAttach->i_updateNonRotational(!!aNonRotational);
4650
4651 return S_OK;
4652}
4653
4654HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4655 LONG aDevice, BOOL aDiscard)
4656{
4657
4658 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4659 aName.c_str(), aControllerPort, aDevice, aDiscard));
4660
4661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4662
4663 HRESULT rc = i_checkStateDependency(MutableStateDep);
4664 if (FAILED(rc)) return rc;
4665
4666 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4667
4668 if (Global::IsOnlineOrTransient(mData->mMachineState))
4669 return setError(VBOX_E_INVALID_VM_STATE,
4670 tr("Invalid machine state: %s"),
4671 Global::stringifyMachineState(mData->mMachineState));
4672
4673 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4674 aName,
4675 aControllerPort,
4676 aDevice);
4677 if (!pAttach)
4678 return setError(VBOX_E_OBJECT_NOT_FOUND,
4679 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4680 aDevice, aControllerPort, aName.c_str());
4681
4682
4683 i_setModified(IsModified_Storage);
4684 mMediumAttachments.backup();
4685
4686 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4687
4688 if (pAttach->i_getType() != DeviceType_HardDisk)
4689 return setError(E_INVALIDARG,
4690 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"),
4691 aDevice, aControllerPort, aName.c_str());
4692 pAttach->i_updateDiscard(!!aDiscard);
4693
4694 return S_OK;
4695}
4696
4697HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4698 LONG aDevice, BOOL aHotPluggable)
4699{
4700 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4701 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4702
4703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4704
4705 HRESULT rc = i_checkStateDependency(MutableStateDep);
4706 if (FAILED(rc)) return rc;
4707
4708 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4709
4710 if (Global::IsOnlineOrTransient(mData->mMachineState))
4711 return setError(VBOX_E_INVALID_VM_STATE,
4712 tr("Invalid machine state: %s"),
4713 Global::stringifyMachineState(mData->mMachineState));
4714
4715 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4716 aName,
4717 aControllerPort,
4718 aDevice);
4719 if (!pAttach)
4720 return setError(VBOX_E_OBJECT_NOT_FOUND,
4721 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4722 aDevice, aControllerPort, aName.c_str());
4723
4724 /* Check for an existing controller. */
4725 ComObjPtr<StorageController> ctl;
4726 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4727 if (FAILED(rc)) return rc;
4728
4729 StorageControllerType_T ctrlType;
4730 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4731 if (FAILED(rc))
4732 return setError(E_FAIL,
4733 tr("Could not get type of controller '%s'"),
4734 aName.c_str());
4735
4736 if (!i_isControllerHotplugCapable(ctrlType))
4737 return setError(VBOX_E_NOT_SUPPORTED,
4738 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4739 aName.c_str());
4740
4741 i_setModified(IsModified_Storage);
4742 mMediumAttachments.backup();
4743
4744 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4745
4746 if (pAttach->i_getType() == DeviceType_Floppy)
4747 return setError(E_INVALIDARG,
4748 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"),
4749 aDevice, aControllerPort, aName.c_str());
4750 pAttach->i_updateHotPluggable(!!aHotPluggable);
4751
4752 return S_OK;
4753}
4754
4755HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4756 LONG aDevice)
4757{
4758 int rc = S_OK;
4759 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4760 aName.c_str(), aControllerPort, aDevice));
4761
4762 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4763
4764 return rc;
4765}
4766
4767HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4768 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4769{
4770 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4771 aName.c_str(), aControllerPort, aDevice));
4772
4773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4774
4775 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4776 if (FAILED(rc)) return rc;
4777
4778 if (Global::IsOnlineOrTransient(mData->mMachineState))
4779 return setError(VBOX_E_INVALID_VM_STATE,
4780 tr("Invalid machine state: %s"),
4781 Global::stringifyMachineState(mData->mMachineState));
4782
4783 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4784 aName,
4785 aControllerPort,
4786 aDevice);
4787 if (!pAttach)
4788 return setError(VBOX_E_OBJECT_NOT_FOUND,
4789 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4790 aDevice, aControllerPort, aName.c_str());
4791
4792
4793 i_setModified(IsModified_Storage);
4794 mMediumAttachments.backup();
4795
4796 IBandwidthGroup *iB = aBandwidthGroup;
4797 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4798 if (aBandwidthGroup && group.isNull())
4799 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4800
4801 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4802
4803 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4804 if (strBandwidthGroupOld.isNotEmpty())
4805 {
4806 /* Get the bandwidth group object and release it - this must not fail. */
4807 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4808 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4809 Assert(SUCCEEDED(rc));
4810
4811 pBandwidthGroupOld->i_release();
4812 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4813 }
4814
4815 if (!group.isNull())
4816 {
4817 group->i_reference();
4818 pAttach->i_updateBandwidthGroup(group->i_getName());
4819 }
4820
4821 return S_OK;
4822}
4823
4824HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4825 LONG aControllerPort,
4826 LONG aDevice,
4827 DeviceType_T aType)
4828{
4829 HRESULT rc = S_OK;
4830
4831 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4832 aName.c_str(), aControllerPort, aDevice, aType));
4833
4834 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4835
4836 return rc;
4837}
4838
4839
4840HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4841 LONG aControllerPort,
4842 LONG aDevice,
4843 BOOL aForce)
4844{
4845 int rc = S_OK;
4846 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4847 aName.c_str(), aControllerPort, aForce));
4848
4849 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4850
4851 return rc;
4852}
4853
4854HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4855 LONG aControllerPort,
4856 LONG aDevice,
4857 const ComPtr<IMedium> &aMedium,
4858 BOOL aForce)
4859{
4860 int rc = S_OK;
4861 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4862 aName.c_str(), aControllerPort, aDevice, aForce));
4863
4864 // request the host lock first, since might be calling Host methods for getting host drives;
4865 // next, protect the media tree all the while we're in here, as well as our member variables
4866 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4867 this->lockHandle(),
4868 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4869
4870 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4871 aName,
4872 aControllerPort,
4873 aDevice);
4874 if (pAttach.isNull())
4875 return setError(VBOX_E_OBJECT_NOT_FOUND,
4876 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4877 aDevice, aControllerPort, aName.c_str());
4878
4879 /* Remember previously mounted medium. The medium before taking the
4880 * backup is not necessarily the same thing. */
4881 ComObjPtr<Medium> oldmedium;
4882 oldmedium = pAttach->i_getMedium();
4883
4884 IMedium *iM = aMedium;
4885 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4886 if (aMedium && pMedium.isNull())
4887 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4888
4889 AutoCaller mediumCaller(pMedium);
4890 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4891
4892 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4893 if (pMedium)
4894 {
4895 DeviceType_T mediumType = pAttach->i_getType();
4896 switch (mediumType)
4897 {
4898 case DeviceType_DVD:
4899 case DeviceType_Floppy:
4900 break;
4901
4902 default:
4903 return setError(VBOX_E_INVALID_OBJECT_STATE,
4904 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4905 aControllerPort,
4906 aDevice,
4907 aName.c_str());
4908 }
4909 }
4910
4911 i_setModified(IsModified_Storage);
4912 mMediumAttachments.backup();
4913
4914 {
4915 // The backup operation makes the pAttach reference point to the
4916 // old settings. Re-get the correct reference.
4917 pAttach = i_findAttachment(*mMediumAttachments.data(),
4918 aName,
4919 aControllerPort,
4920 aDevice);
4921 if (!oldmedium.isNull())
4922 oldmedium->i_removeBackReference(mData->mUuid);
4923 if (!pMedium.isNull())
4924 {
4925 pMedium->i_addBackReference(mData->mUuid);
4926
4927 mediumLock.release();
4928 multiLock.release();
4929 i_addMediumToRegistry(pMedium);
4930 multiLock.acquire();
4931 mediumLock.acquire();
4932 }
4933
4934 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4935 pAttach->i_updateMedium(pMedium);
4936 }
4937
4938 i_setModified(IsModified_Storage);
4939
4940 mediumLock.release();
4941 multiLock.release();
4942 rc = i_onMediumChange(pAttach, aForce);
4943 multiLock.acquire();
4944 mediumLock.acquire();
4945
4946 /* On error roll back this change only. */
4947 if (FAILED(rc))
4948 {
4949 if (!pMedium.isNull())
4950 pMedium->i_removeBackReference(mData->mUuid);
4951 pAttach = i_findAttachment(*mMediumAttachments.data(),
4952 aName,
4953 aControllerPort,
4954 aDevice);
4955 /* If the attachment is gone in the meantime, bail out. */
4956 if (pAttach.isNull())
4957 return rc;
4958 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4959 if (!oldmedium.isNull())
4960 oldmedium->i_addBackReference(mData->mUuid);
4961 pAttach->i_updateMedium(oldmedium);
4962 }
4963
4964 mediumLock.release();
4965 multiLock.release();
4966
4967 /* Save modified registries, but skip this machine as it's the caller's
4968 * job to save its settings like all other settings changes. */
4969 mParent->i_unmarkRegistryModified(i_getId());
4970 mParent->i_saveModifiedRegistries();
4971
4972 return rc;
4973}
4974HRESULT Machine::getMedium(const com::Utf8Str &aName,
4975 LONG aControllerPort,
4976 LONG aDevice,
4977 ComPtr<IMedium> &aMedium)
4978{
4979 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4980 aName.c_str(), aControllerPort, aDevice));
4981
4982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4983
4984 aMedium = NULL;
4985
4986 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4987 aName,
4988 aControllerPort,
4989 aDevice);
4990 if (pAttach.isNull())
4991 return setError(VBOX_E_OBJECT_NOT_FOUND,
4992 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4993 aDevice, aControllerPort, aName.c_str());
4994
4995 aMedium = pAttach->i_getMedium();
4996
4997 return S_OK;
4998}
4999
5000HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5001{
5002
5003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5004
5005 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5006
5007 return S_OK;
5008}
5009
5010HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5011{
5012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5013
5014 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5015
5016 return S_OK;
5017}
5018
5019HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5020{
5021 /* Do not assert if slot is out of range, just return the advertised
5022 status. testdriver/vbox.py triggers this in logVmInfo. */
5023 if (aSlot >= mNetworkAdapters.size())
5024 return setError(E_INVALIDARG,
5025 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5026 aSlot, mNetworkAdapters.size());
5027
5028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5029
5030 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5031
5032 return S_OK;
5033}
5034
5035HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5036{
5037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5038
5039 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5040 size_t i = 0;
5041 for (settings::StringsMap::const_iterator
5042 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5043 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5044 ++it, ++i)
5045 aKeys[i] = it->first;
5046
5047 return S_OK;
5048}
5049
5050 /**
5051 * @note Locks this object for reading.
5052 */
5053HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5054 com::Utf8Str &aValue)
5055{
5056 /* start with nothing found */
5057 aValue = "";
5058
5059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5060
5061 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5062 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5063 // found:
5064 aValue = it->second; // source is a Utf8Str
5065
5066 /* return the result to caller (may be empty) */
5067 return S_OK;
5068}
5069
5070 /**
5071 * @note Locks mParent for writing + this object for writing.
5072 */
5073HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5074{
5075 /* Because non-ASCII characters in aKey have caused problems in the settings
5076 * they are rejected unless the key should be deleted. */
5077 if (!aValue.isEmpty())
5078 {
5079 for (size_t i = 0; i < aKey.length(); ++i)
5080 {
5081 char ch = aKey[i];
5082 if (!RTLocCIsPrint(ch))
5083 return E_INVALIDARG;
5084 }
5085 }
5086
5087 Utf8Str strOldValue; // empty
5088
5089 // locking note: we only hold the read lock briefly to look up the old value,
5090 // then release it and call the onExtraCanChange callbacks. There is a small
5091 // chance of a race insofar as the callback might be called twice if two callers
5092 // change the same key at the same time, but that's a much better solution
5093 // than the deadlock we had here before. The actual changing of the extradata
5094 // is then performed under the write lock and race-free.
5095
5096 // look up the old value first; if nothing has changed then we need not do anything
5097 {
5098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5099
5100 // For snapshots don't even think about allowing changes, extradata
5101 // is global for a machine, so there is nothing snapshot specific.
5102 if (i_isSnapshotMachine())
5103 return setError(VBOX_E_INVALID_VM_STATE,
5104 tr("Cannot set extradata for a snapshot"));
5105
5106 // check if the right IMachine instance is used
5107 if (mData->mRegistered && !i_isSessionMachine())
5108 return setError(VBOX_E_INVALID_VM_STATE,
5109 tr("Cannot set extradata for an immutable machine"));
5110
5111 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5112 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5113 strOldValue = it->second;
5114 }
5115
5116 bool fChanged;
5117 if ((fChanged = (strOldValue != aValue)))
5118 {
5119 // ask for permission from all listeners outside the locks;
5120 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5121 // lock to copy the list of callbacks to invoke
5122 Bstr error;
5123 Bstr bstrValue(aValue);
5124
5125 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5126 {
5127 const char *sep = error.isEmpty() ? "" : ": ";
5128 CBSTR err = error.raw();
5129 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5130 return setError(E_ACCESSDENIED,
5131 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5132 aKey.c_str(),
5133 aValue.c_str(),
5134 sep,
5135 err);
5136 }
5137
5138 // data is changing and change not vetoed: then write it out under the lock
5139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5140
5141 if (aValue.isEmpty())
5142 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5143 else
5144 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5145 // creates a new key if needed
5146
5147 bool fNeedsGlobalSaveSettings = false;
5148 // This saving of settings is tricky: there is no "old state" for the
5149 // extradata items at all (unlike all other settings), so the old/new
5150 // settings comparison would give a wrong result!
5151 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5152
5153 if (fNeedsGlobalSaveSettings)
5154 {
5155 // save the global settings; for that we should hold only the VirtualBox lock
5156 alock.release();
5157 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5158 mParent->i_saveSettings();
5159 }
5160 }
5161
5162 // fire notification outside the lock
5163 if (fChanged)
5164 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5165
5166 return S_OK;
5167}
5168
5169HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5170{
5171 aProgress = NULL;
5172 NOREF(aSettingsFilePath);
5173 ReturnComNotImplemented();
5174}
5175
5176HRESULT Machine::saveSettings()
5177{
5178 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5179
5180 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5181 if (FAILED(rc)) return rc;
5182
5183 /* the settings file path may never be null */
5184 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5185
5186 /* save all VM data excluding snapshots */
5187 bool fNeedsGlobalSaveSettings = false;
5188 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5189 mlock.release();
5190
5191 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5192 {
5193 // save the global settings; for that we should hold only the VirtualBox lock
5194 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5195 rc = mParent->i_saveSettings();
5196 }
5197
5198 return rc;
5199}
5200
5201
5202HRESULT Machine::discardSettings()
5203{
5204 /*
5205 * We need to take the machine list lock here as well as the machine one
5206 * or we'll get into trouble should any media stuff require rolling back.
5207 *
5208 * Details:
5209 *
5210 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5211 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5212 * 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]
5213 * 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
5214 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5215 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5216 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5217 * 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
5218 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5219 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5220 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5221 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5222 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5223 * 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]
5224 * 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] (*)
5225 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5226 * 0:005> k
5227 * # Child-SP RetAddr Call Site
5228 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5229 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5230 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5231 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5232 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5233 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5234 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5235 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5236 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5237 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5238 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5239 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5240 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5241 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5242 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5243 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5244 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5245 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5246 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5247 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5248 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5249 *
5250 */
5251 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5253
5254 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5255 if (FAILED(rc)) return rc;
5256
5257 /*
5258 * during this rollback, the session will be notified if data has
5259 * been actually changed
5260 */
5261 i_rollback(true /* aNotify */);
5262
5263 return S_OK;
5264}
5265
5266/** @note Locks objects! */
5267HRESULT Machine::unregister(AutoCaller &autoCaller,
5268 CleanupMode_T aCleanupMode,
5269 std::vector<ComPtr<IMedium> > &aMedia)
5270{
5271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5272
5273 Guid id(i_getId());
5274
5275 if (mData->mSession.mState != SessionState_Unlocked)
5276 return setError(VBOX_E_INVALID_OBJECT_STATE,
5277 tr("Cannot unregister the machine '%s' while it is locked"),
5278 mUserData->s.strName.c_str());
5279
5280 // wait for state dependents to drop to zero
5281 i_ensureNoStateDependencies();
5282
5283 if (!mData->mAccessible)
5284 {
5285 // inaccessible maschines can only be unregistered; uninitialize ourselves
5286 // here because currently there may be no unregistered that are inaccessible
5287 // (this state combination is not supported). Note releasing the caller and
5288 // leaving the lock before calling uninit()
5289 alock.release();
5290 autoCaller.release();
5291
5292 uninit();
5293
5294 mParent->i_unregisterMachine(this, id);
5295 // calls VirtualBox::i_saveSettings()
5296
5297 return S_OK;
5298 }
5299
5300 HRESULT rc = S_OK;
5301
5302 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5303 // discard saved state
5304 if (mData->mMachineState == MachineState_Saved)
5305 {
5306 // add the saved state file to the list of files the caller should delete
5307 Assert(!mSSData->strStateFilePath.isEmpty());
5308 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5309
5310 mSSData->strStateFilePath.setNull();
5311
5312 // unconditionally set the machine state to powered off, we now
5313 // know no session has locked the machine
5314 mData->mMachineState = MachineState_PoweredOff;
5315 }
5316
5317 size_t cSnapshots = 0;
5318 if (mData->mFirstSnapshot)
5319 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5320 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5321 // fail now before we start detaching media
5322 return setError(VBOX_E_INVALID_OBJECT_STATE,
5323 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5324 mUserData->s.strName.c_str(), cSnapshots);
5325
5326 // This list collects the medium objects from all medium attachments
5327 // which we will detach from the machine and its snapshots, in a specific
5328 // order which allows for closing all media without getting "media in use"
5329 // errors, simply by going through the list from the front to the back:
5330 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5331 // and must be closed before the parent media from the snapshots, or closing the parents
5332 // will fail because they still have children);
5333 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5334 // the root ("first") snapshot of the machine.
5335 MediaList llMedia;
5336
5337 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5338 && mMediumAttachments->size()
5339 )
5340 {
5341 // we have media attachments: detach them all and add the Medium objects to our list
5342 if (aCleanupMode != CleanupMode_UnregisterOnly)
5343 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5344 else
5345 return setError(VBOX_E_INVALID_OBJECT_STATE,
5346 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5347 mUserData->s.strName.c_str(), mMediumAttachments->size());
5348 }
5349
5350 if (cSnapshots)
5351 {
5352 // add the media from the medium attachments of the snapshots to llMedia
5353 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5354 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5355 // into the children first
5356
5357 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5358 MachineState_T oldState = mData->mMachineState;
5359 mData->mMachineState = MachineState_DeletingSnapshot;
5360
5361 // make a copy of the first snapshot so the refcount does not drop to 0
5362 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5363 // because of the AutoCaller voodoo)
5364 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5365
5366 // GO!
5367 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5368
5369 mData->mMachineState = oldState;
5370 }
5371
5372 if (FAILED(rc))
5373 {
5374 i_rollbackMedia();
5375 return rc;
5376 }
5377
5378 // commit all the media changes made above
5379 i_commitMedia();
5380
5381 mData->mRegistered = false;
5382
5383 // machine lock no longer needed
5384 alock.release();
5385
5386 // return media to caller
5387 aMedia.resize(llMedia.size());
5388 size_t i = 0;
5389 for (MediaList::const_iterator
5390 it = llMedia.begin();
5391 it != llMedia.end();
5392 ++it, ++i)
5393 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5394
5395 mParent->i_unregisterMachine(this, id);
5396 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5397
5398 return S_OK;
5399}
5400
5401/**
5402 * Task record for deleting a machine config.
5403 */
5404class Machine::DeleteConfigTask
5405 : public Machine::Task
5406{
5407public:
5408 DeleteConfigTask(Machine *m,
5409 Progress *p,
5410 const Utf8Str &t,
5411 const RTCList<ComPtr<IMedium> > &llMediums,
5412 const StringsList &llFilesToDelete)
5413 : Task(m, p, t),
5414 m_llMediums(llMediums),
5415 m_llFilesToDelete(llFilesToDelete)
5416 {}
5417
5418private:
5419 void handler()
5420 {
5421 try
5422 {
5423 m_pMachine->i_deleteConfigHandler(*this);
5424 }
5425 catch (...)
5426 {
5427 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5428 }
5429 }
5430
5431 RTCList<ComPtr<IMedium> > m_llMediums;
5432 StringsList m_llFilesToDelete;
5433
5434 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5435};
5436
5437/**
5438 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5439 * SessionMachine::taskHandler().
5440 *
5441 * @note Locks this object for writing.
5442 *
5443 * @param task
5444 * @return
5445 */
5446void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5447{
5448 LogFlowThisFuncEnter();
5449
5450 AutoCaller autoCaller(this);
5451 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5452 if (FAILED(autoCaller.rc()))
5453 {
5454 /* we might have been uninitialized because the session was accidentally
5455 * closed by the client, so don't assert */
5456 HRESULT rc = setError(E_FAIL,
5457 tr("The session has been accidentally closed"));
5458 task.m_pProgress->i_notifyComplete(rc);
5459 LogFlowThisFuncLeave();
5460 return;
5461 }
5462
5463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5464
5465 HRESULT rc = S_OK;
5466
5467 try
5468 {
5469 ULONG uLogHistoryCount = 3;
5470 ComPtr<ISystemProperties> systemProperties;
5471 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5472 if (FAILED(rc)) throw rc;
5473
5474 if (!systemProperties.isNull())
5475 {
5476 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5477 if (FAILED(rc)) throw rc;
5478 }
5479
5480 MachineState_T oldState = mData->mMachineState;
5481 i_setMachineState(MachineState_SettingUp);
5482 alock.release();
5483 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5484 {
5485 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5486 {
5487 AutoCaller mac(pMedium);
5488 if (FAILED(mac.rc())) throw mac.rc();
5489 Utf8Str strLocation = pMedium->i_getLocationFull();
5490 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5491 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5492 if (FAILED(rc)) throw rc;
5493 }
5494 if (pMedium->i_isMediumFormatFile())
5495 {
5496 ComPtr<IProgress> pProgress2;
5497 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5498 if (FAILED(rc)) throw rc;
5499 rc = task.m_pProgress->i_waitForOtherProgressCompletion(pProgress2);
5500 if (FAILED(rc)) throw rc;
5501 }
5502
5503 /* Close the medium, deliberately without checking the return
5504 * code, and without leaving any trace in the error info, as
5505 * a failure here is a very minor issue, which shouldn't happen
5506 * as above we even managed to delete the medium. */
5507 {
5508 ErrorInfoKeeper eik;
5509 pMedium->Close();
5510 }
5511 }
5512 i_setMachineState(oldState);
5513 alock.acquire();
5514
5515 // delete the files pushed on the task list by Machine::Delete()
5516 // (this includes saved states of the machine and snapshots and
5517 // medium storage files from the IMedium list passed in, and the
5518 // machine XML file)
5519 for (StringsList::const_iterator
5520 it = task.m_llFilesToDelete.begin();
5521 it != task.m_llFilesToDelete.end();
5522 ++it)
5523 {
5524 const Utf8Str &strFile = *it;
5525 LogFunc(("Deleting file %s\n", strFile.c_str()));
5526 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5527 if (FAILED(rc)) throw rc;
5528
5529 int vrc = RTFileDelete(strFile.c_str());
5530 if (RT_FAILURE(vrc))
5531 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5532 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5533 }
5534
5535 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5536 if (FAILED(rc)) throw rc;
5537
5538 /* delete the settings only when the file actually exists */
5539 if (mData->pMachineConfigFile->fileExists())
5540 {
5541 /* Delete any backup or uncommitted XML files. Ignore failures.
5542 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5543 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5544 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5545 RTFileDelete(otherXml.c_str());
5546 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5547 RTFileDelete(otherXml.c_str());
5548
5549 /* delete the Logs folder, nothing important should be left
5550 * there (we don't check for errors because the user might have
5551 * some private files there that we don't want to delete) */
5552 Utf8Str logFolder;
5553 getLogFolder(logFolder);
5554 Assert(logFolder.length());
5555 if (RTDirExists(logFolder.c_str()))
5556 {
5557 /* Delete all VBox.log[.N] files from the Logs folder
5558 * (this must be in sync with the rotation logic in
5559 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5560 * files that may have been created by the GUI. */
5561 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5562 logFolder.c_str(), RTPATH_DELIMITER);
5563 RTFileDelete(log.c_str());
5564 log = Utf8StrFmt("%s%cVBox.png",
5565 logFolder.c_str(), RTPATH_DELIMITER);
5566 RTFileDelete(log.c_str());
5567 for (int i = uLogHistoryCount; i > 0; i--)
5568 {
5569 log = Utf8StrFmt("%s%cVBox.log.%d",
5570 logFolder.c_str(), RTPATH_DELIMITER, i);
5571 RTFileDelete(log.c_str());
5572 log = Utf8StrFmt("%s%cVBox.png.%d",
5573 logFolder.c_str(), RTPATH_DELIMITER, i);
5574 RTFileDelete(log.c_str());
5575 }
5576#if defined(RT_OS_WINDOWS)
5577 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5578 RTFileDelete(log.c_str());
5579 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5580 RTFileDelete(log.c_str());
5581#endif
5582
5583 RTDirRemove(logFolder.c_str());
5584 }
5585
5586 /* delete the Snapshots folder, nothing important should be left
5587 * there (we don't check for errors because the user might have
5588 * some private files there that we don't want to delete) */
5589 Utf8Str strFullSnapshotFolder;
5590 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5591 Assert(!strFullSnapshotFolder.isEmpty());
5592 if (RTDirExists(strFullSnapshotFolder.c_str()))
5593 RTDirRemove(strFullSnapshotFolder.c_str());
5594
5595 // delete the directory that contains the settings file, but only
5596 // if it matches the VM name
5597 Utf8Str settingsDir;
5598 if (i_isInOwnDir(&settingsDir))
5599 RTDirRemove(settingsDir.c_str());
5600 }
5601
5602 alock.release();
5603
5604 mParent->i_saveModifiedRegistries();
5605 }
5606 catch (HRESULT aRC) { rc = aRC; }
5607
5608 task.m_pProgress->i_notifyComplete(rc);
5609
5610 LogFlowThisFuncLeave();
5611}
5612
5613HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5614{
5615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5616
5617 HRESULT rc = i_checkStateDependency(MutableStateDep);
5618 if (FAILED(rc)) return rc;
5619
5620 if (mData->mRegistered)
5621 return setError(VBOX_E_INVALID_VM_STATE,
5622 tr("Cannot delete settings of a registered machine"));
5623
5624 // collect files to delete
5625 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5626 if (mData->pMachineConfigFile->fileExists())
5627 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5628
5629 RTCList<ComPtr<IMedium> > llMediums;
5630 for (size_t i = 0; i < aMedia.size(); ++i)
5631 {
5632 IMedium *pIMedium(aMedia[i]);
5633 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5634 if (pMedium.isNull())
5635 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5636 SafeArray<BSTR> ids;
5637 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5638 if (FAILED(rc)) return rc;
5639 /* At this point the medium should not have any back references
5640 * anymore. If it has it is attached to another VM and *must* not
5641 * deleted. */
5642 if (ids.size() < 1)
5643 llMediums.append(pMedium);
5644 }
5645
5646 ComObjPtr<Progress> pProgress;
5647 pProgress.createObject();
5648 rc = pProgress->init(i_getVirtualBox(),
5649 static_cast<IMachine*>(this) /* aInitiator */,
5650 tr("Deleting files"),
5651 true /* fCancellable */,
5652 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5653 tr("Collecting file inventory"));
5654 if (FAILED(rc))
5655 return rc;
5656
5657 /* create and start the task on a separate thread (note that it will not
5658 * start working until we release alock) */
5659 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5660 rc = pTask->createThread();
5661 if (FAILED(rc))
5662 return rc;
5663
5664 pProgress.queryInterfaceTo(aProgress.asOutParam());
5665
5666 LogFlowFuncLeave();
5667
5668 return S_OK;
5669}
5670
5671HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5672{
5673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5674
5675 ComObjPtr<Snapshot> pSnapshot;
5676 HRESULT rc;
5677
5678 if (aNameOrId.isEmpty())
5679 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5680 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5681 else
5682 {
5683 Guid uuid(aNameOrId);
5684 if (uuid.isValid())
5685 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5686 else
5687 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5688 }
5689 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5690
5691 return rc;
5692}
5693
5694HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5695{
5696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5697
5698 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5699 if (FAILED(rc)) return rc;
5700
5701 ComObjPtr<SharedFolder> sharedFolder;
5702 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5703 if (SUCCEEDED(rc))
5704 return setError(VBOX_E_OBJECT_IN_USE,
5705 tr("Shared folder named '%s' already exists"),
5706 aName.c_str());
5707
5708 sharedFolder.createObject();
5709 rc = sharedFolder->init(i_getMachine(),
5710 aName,
5711 aHostPath,
5712 !!aWritable,
5713 !!aAutomount,
5714 true /* fFailOnError */);
5715 if (FAILED(rc)) return rc;
5716
5717 i_setModified(IsModified_SharedFolders);
5718 mHWData.backup();
5719 mHWData->mSharedFolders.push_back(sharedFolder);
5720
5721 /* inform the direct session if any */
5722 alock.release();
5723 i_onSharedFolderChange();
5724
5725 return S_OK;
5726}
5727
5728HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5729{
5730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5731
5732 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5733 if (FAILED(rc)) return rc;
5734
5735 ComObjPtr<SharedFolder> sharedFolder;
5736 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5737 if (FAILED(rc)) return rc;
5738
5739 i_setModified(IsModified_SharedFolders);
5740 mHWData.backup();
5741 mHWData->mSharedFolders.remove(sharedFolder);
5742
5743 /* inform the direct session if any */
5744 alock.release();
5745 i_onSharedFolderChange();
5746
5747 return S_OK;
5748}
5749
5750HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5751{
5752 /* start with No */
5753 *aCanShow = FALSE;
5754
5755 ComPtr<IInternalSessionControl> directControl;
5756 {
5757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5758
5759 if (mData->mSession.mState != SessionState_Locked)
5760 return setError(VBOX_E_INVALID_VM_STATE,
5761 tr("Machine is not locked for session (session state: %s)"),
5762 Global::stringifySessionState(mData->mSession.mState));
5763
5764 if (mData->mSession.mLockType == LockType_VM)
5765 directControl = mData->mSession.mDirectControl;
5766 }
5767
5768 /* ignore calls made after #OnSessionEnd() is called */
5769 if (!directControl)
5770 return S_OK;
5771
5772 LONG64 dummy;
5773 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5774}
5775
5776HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5777{
5778 ComPtr<IInternalSessionControl> directControl;
5779 {
5780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5781
5782 if (mData->mSession.mState != SessionState_Locked)
5783 return setError(E_FAIL,
5784 tr("Machine is not locked for session (session state: %s)"),
5785 Global::stringifySessionState(mData->mSession.mState));
5786
5787 if (mData->mSession.mLockType == LockType_VM)
5788 directControl = mData->mSession.mDirectControl;
5789 }
5790
5791 /* ignore calls made after #OnSessionEnd() is called */
5792 if (!directControl)
5793 return S_OK;
5794
5795 BOOL dummy;
5796 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5797}
5798
5799#ifdef VBOX_WITH_GUEST_PROPS
5800/**
5801 * Look up a guest property in VBoxSVC's internal structures.
5802 */
5803HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5804 com::Utf8Str &aValue,
5805 LONG64 *aTimestamp,
5806 com::Utf8Str &aFlags) const
5807{
5808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5809
5810 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5811 if (it != mHWData->mGuestProperties.end())
5812 {
5813 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5814 aValue = it->second.strValue;
5815 *aTimestamp = it->second.mTimestamp;
5816 GuestPropWriteFlags(it->second.mFlags, szFlags);
5817 aFlags = Utf8Str(szFlags);
5818 }
5819
5820 return S_OK;
5821}
5822
5823/**
5824 * Query the VM that a guest property belongs to for the property.
5825 * @returns E_ACCESSDENIED if the VM process is not available or not
5826 * currently handling queries and the lookup should then be done in
5827 * VBoxSVC.
5828 */
5829HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5830 com::Utf8Str &aValue,
5831 LONG64 *aTimestamp,
5832 com::Utf8Str &aFlags) const
5833{
5834 HRESULT rc = S_OK;
5835 BSTR bValue = NULL;
5836 BSTR bFlags = NULL;
5837
5838 ComPtr<IInternalSessionControl> directControl;
5839 {
5840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5841 if (mData->mSession.mLockType == LockType_VM)
5842 directControl = mData->mSession.mDirectControl;
5843 }
5844
5845 /* ignore calls made after #OnSessionEnd() is called */
5846 if (!directControl)
5847 rc = E_ACCESSDENIED;
5848 else
5849 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5850 0 /* accessMode */,
5851 &bValue, aTimestamp, &bFlags);
5852
5853 aValue = bValue;
5854 aFlags = bFlags;
5855
5856 return rc;
5857}
5858#endif // VBOX_WITH_GUEST_PROPS
5859
5860HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5861 com::Utf8Str &aValue,
5862 LONG64 *aTimestamp,
5863 com::Utf8Str &aFlags)
5864{
5865#ifndef VBOX_WITH_GUEST_PROPS
5866 ReturnComNotImplemented();
5867#else // VBOX_WITH_GUEST_PROPS
5868
5869 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5870
5871 if (rc == E_ACCESSDENIED)
5872 /* The VM is not running or the service is not (yet) accessible */
5873 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5874 return rc;
5875#endif // VBOX_WITH_GUEST_PROPS
5876}
5877
5878HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5879{
5880 LONG64 dummyTimestamp;
5881 com::Utf8Str dummyFlags;
5882 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5883 return rc;
5884
5885}
5886HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5887{
5888 com::Utf8Str dummyFlags;
5889 com::Utf8Str dummyValue;
5890 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5891 return rc;
5892}
5893
5894#ifdef VBOX_WITH_GUEST_PROPS
5895/**
5896 * Set a guest property in VBoxSVC's internal structures.
5897 */
5898HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5899 const com::Utf8Str &aFlags, bool fDelete)
5900{
5901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5902 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5903 if (FAILED(rc)) return rc;
5904
5905 try
5906 {
5907 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5908 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5909 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5910
5911 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5912 if (it == mHWData->mGuestProperties.end())
5913 {
5914 if (!fDelete)
5915 {
5916 i_setModified(IsModified_MachineData);
5917 mHWData.backupEx();
5918
5919 RTTIMESPEC time;
5920 HWData::GuestProperty prop;
5921 prop.strValue = Bstr(aValue).raw();
5922 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5923 prop.mFlags = fFlags;
5924 mHWData->mGuestProperties[aName] = prop;
5925 }
5926 }
5927 else
5928 {
5929 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5930 {
5931 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5932 }
5933 else
5934 {
5935 i_setModified(IsModified_MachineData);
5936 mHWData.backupEx();
5937
5938 /* The backupEx() operation invalidates our iterator,
5939 * so get a new one. */
5940 it = mHWData->mGuestProperties.find(aName);
5941 Assert(it != mHWData->mGuestProperties.end());
5942
5943 if (!fDelete)
5944 {
5945 RTTIMESPEC time;
5946 it->second.strValue = aValue;
5947 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5948 it->second.mFlags = fFlags;
5949 }
5950 else
5951 mHWData->mGuestProperties.erase(it);
5952 }
5953 }
5954
5955 if (SUCCEEDED(rc))
5956 {
5957 alock.release();
5958
5959 mParent->i_onGuestPropertyChange(mData->mUuid,
5960 Bstr(aName).raw(),
5961 Bstr(aValue).raw(),
5962 Bstr(aFlags).raw());
5963 }
5964 }
5965 catch (std::bad_alloc &)
5966 {
5967 rc = E_OUTOFMEMORY;
5968 }
5969
5970 return rc;
5971}
5972
5973/**
5974 * Set a property on the VM that that property belongs to.
5975 * @returns E_ACCESSDENIED if the VM process is not available or not
5976 * currently handling queries and the setting should then be done in
5977 * VBoxSVC.
5978 */
5979HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5980 const com::Utf8Str &aFlags, bool fDelete)
5981{
5982 HRESULT rc;
5983
5984 try
5985 {
5986 ComPtr<IInternalSessionControl> directControl;
5987 {
5988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5989 if (mData->mSession.mLockType == LockType_VM)
5990 directControl = mData->mSession.mDirectControl;
5991 }
5992
5993 BSTR dummy = NULL; /* will not be changed (setter) */
5994 LONG64 dummy64;
5995 if (!directControl)
5996 rc = E_ACCESSDENIED;
5997 else
5998 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5999 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
6000 fDelete? 2: 1 /* accessMode */,
6001 &dummy, &dummy64, &dummy);
6002 }
6003 catch (std::bad_alloc &)
6004 {
6005 rc = E_OUTOFMEMORY;
6006 }
6007
6008 return rc;
6009}
6010#endif // VBOX_WITH_GUEST_PROPS
6011
6012HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6013 const com::Utf8Str &aFlags)
6014{
6015#ifndef VBOX_WITH_GUEST_PROPS
6016 ReturnComNotImplemented();
6017#else // VBOX_WITH_GUEST_PROPS
6018 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6019 if (rc == E_ACCESSDENIED)
6020 /* The VM is not running or the service is not (yet) accessible */
6021 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6022 return rc;
6023#endif // VBOX_WITH_GUEST_PROPS
6024}
6025
6026HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6027{
6028 return setGuestProperty(aProperty, aValue, "");
6029}
6030
6031HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6032{
6033#ifndef VBOX_WITH_GUEST_PROPS
6034 ReturnComNotImplemented();
6035#else // VBOX_WITH_GUEST_PROPS
6036 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6037 if (rc == E_ACCESSDENIED)
6038 /* The VM is not running or the service is not (yet) accessible */
6039 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6040 return rc;
6041#endif // VBOX_WITH_GUEST_PROPS
6042}
6043
6044#ifdef VBOX_WITH_GUEST_PROPS
6045/**
6046 * Enumerate the guest properties in VBoxSVC's internal structures.
6047 */
6048HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6049 std::vector<com::Utf8Str> &aNames,
6050 std::vector<com::Utf8Str> &aValues,
6051 std::vector<LONG64> &aTimestamps,
6052 std::vector<com::Utf8Str> &aFlags)
6053{
6054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6055 Utf8Str strPatterns(aPatterns);
6056
6057 /*
6058 * Look for matching patterns and build up a list.
6059 */
6060 HWData::GuestPropertyMap propMap;
6061 for (HWData::GuestPropertyMap::const_iterator
6062 it = mHWData->mGuestProperties.begin();
6063 it != mHWData->mGuestProperties.end();
6064 ++it)
6065 {
6066 if ( strPatterns.isEmpty()
6067 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6068 RTSTR_MAX,
6069 it->first.c_str(),
6070 RTSTR_MAX,
6071 NULL)
6072 )
6073 propMap.insert(*it);
6074 }
6075
6076 alock.release();
6077
6078 /*
6079 * And build up the arrays for returning the property information.
6080 */
6081 size_t cEntries = propMap.size();
6082
6083 aNames.resize(cEntries);
6084 aValues.resize(cEntries);
6085 aTimestamps.resize(cEntries);
6086 aFlags.resize(cEntries);
6087
6088 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6089 size_t i = 0;
6090 for (HWData::GuestPropertyMap::const_iterator
6091 it = propMap.begin();
6092 it != propMap.end();
6093 ++it, ++i)
6094 {
6095 aNames[i] = it->first;
6096 aValues[i] = it->second.strValue;
6097 aTimestamps[i] = it->second.mTimestamp;
6098 GuestPropWriteFlags(it->second.mFlags, szFlags);
6099 aFlags[i] = Utf8Str(szFlags);
6100 }
6101
6102 return S_OK;
6103}
6104
6105/**
6106 * Enumerate the properties managed by a VM.
6107 * @returns E_ACCESSDENIED if the VM process is not available or not
6108 * currently handling queries and the setting should then be done in
6109 * VBoxSVC.
6110 */
6111HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6112 std::vector<com::Utf8Str> &aNames,
6113 std::vector<com::Utf8Str> &aValues,
6114 std::vector<LONG64> &aTimestamps,
6115 std::vector<com::Utf8Str> &aFlags)
6116{
6117 HRESULT rc;
6118 ComPtr<IInternalSessionControl> directControl;
6119 {
6120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6121 if (mData->mSession.mLockType == LockType_VM)
6122 directControl = mData->mSession.mDirectControl;
6123 }
6124
6125 com::SafeArray<BSTR> bNames;
6126 com::SafeArray<BSTR> bValues;
6127 com::SafeArray<LONG64> bTimestamps;
6128 com::SafeArray<BSTR> bFlags;
6129
6130 if (!directControl)
6131 rc = E_ACCESSDENIED;
6132 else
6133 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6134 ComSafeArrayAsOutParam(bNames),
6135 ComSafeArrayAsOutParam(bValues),
6136 ComSafeArrayAsOutParam(bTimestamps),
6137 ComSafeArrayAsOutParam(bFlags));
6138 size_t i;
6139 aNames.resize(bNames.size());
6140 for (i = 0; i < bNames.size(); ++i)
6141 aNames[i] = Utf8Str(bNames[i]);
6142 aValues.resize(bValues.size());
6143 for (i = 0; i < bValues.size(); ++i)
6144 aValues[i] = Utf8Str(bValues[i]);
6145 aTimestamps.resize(bTimestamps.size());
6146 for (i = 0; i < bTimestamps.size(); ++i)
6147 aTimestamps[i] = bTimestamps[i];
6148 aFlags.resize(bFlags.size());
6149 for (i = 0; i < bFlags.size(); ++i)
6150 aFlags[i] = Utf8Str(bFlags[i]);
6151
6152 return rc;
6153}
6154#endif // VBOX_WITH_GUEST_PROPS
6155HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6156 std::vector<com::Utf8Str> &aNames,
6157 std::vector<com::Utf8Str> &aValues,
6158 std::vector<LONG64> &aTimestamps,
6159 std::vector<com::Utf8Str> &aFlags)
6160{
6161#ifndef VBOX_WITH_GUEST_PROPS
6162 ReturnComNotImplemented();
6163#else // VBOX_WITH_GUEST_PROPS
6164
6165 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6166
6167 if (rc == E_ACCESSDENIED)
6168 /* The VM is not running or the service is not (yet) accessible */
6169 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6170 return rc;
6171#endif // VBOX_WITH_GUEST_PROPS
6172}
6173
6174HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6175 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6176{
6177 MediumAttachmentList atts;
6178
6179 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6180 if (FAILED(rc)) return rc;
6181
6182 aMediumAttachments.resize(atts.size());
6183 size_t i = 0;
6184 for (MediumAttachmentList::const_iterator
6185 it = atts.begin();
6186 it != atts.end();
6187 ++it, ++i)
6188 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6189
6190 return S_OK;
6191}
6192
6193HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6194 LONG aControllerPort,
6195 LONG aDevice,
6196 ComPtr<IMediumAttachment> &aAttachment)
6197{
6198 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6199 aName.c_str(), aControllerPort, aDevice));
6200
6201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6202
6203 aAttachment = NULL;
6204
6205 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6206 aName,
6207 aControllerPort,
6208 aDevice);
6209 if (pAttach.isNull())
6210 return setError(VBOX_E_OBJECT_NOT_FOUND,
6211 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6212 aDevice, aControllerPort, aName.c_str());
6213
6214 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6215
6216 return S_OK;
6217}
6218
6219
6220HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6221 StorageBus_T aConnectionType,
6222 ComPtr<IStorageController> &aController)
6223{
6224 if ( (aConnectionType <= StorageBus_Null)
6225 || (aConnectionType > StorageBus_PCIe))
6226 return setError(E_INVALIDARG,
6227 tr("Invalid connection type: %d"),
6228 aConnectionType);
6229
6230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6231
6232 HRESULT rc = i_checkStateDependency(MutableStateDep);
6233 if (FAILED(rc)) return rc;
6234
6235 /* try to find one with the name first. */
6236 ComObjPtr<StorageController> ctrl;
6237
6238 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6239 if (SUCCEEDED(rc))
6240 return setError(VBOX_E_OBJECT_IN_USE,
6241 tr("Storage controller named '%s' already exists"),
6242 aName.c_str());
6243
6244 ctrl.createObject();
6245
6246 /* get a new instance number for the storage controller */
6247 ULONG ulInstance = 0;
6248 bool fBootable = true;
6249 for (StorageControllerList::const_iterator
6250 it = mStorageControllers->begin();
6251 it != mStorageControllers->end();
6252 ++it)
6253 {
6254 if ((*it)->i_getStorageBus() == aConnectionType)
6255 {
6256 ULONG ulCurInst = (*it)->i_getInstance();
6257
6258 if (ulCurInst >= ulInstance)
6259 ulInstance = ulCurInst + 1;
6260
6261 /* Only one controller of each type can be marked as bootable. */
6262 if ((*it)->i_getBootable())
6263 fBootable = false;
6264 }
6265 }
6266
6267 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6268 if (FAILED(rc)) return rc;
6269
6270 i_setModified(IsModified_Storage);
6271 mStorageControllers.backup();
6272 mStorageControllers->push_back(ctrl);
6273
6274 ctrl.queryInterfaceTo(aController.asOutParam());
6275
6276 /* inform the direct session if any */
6277 alock.release();
6278 i_onStorageControllerChange();
6279
6280 return S_OK;
6281}
6282
6283HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6284 ComPtr<IStorageController> &aStorageController)
6285{
6286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6287
6288 ComObjPtr<StorageController> ctrl;
6289
6290 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6291 if (SUCCEEDED(rc))
6292 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6293
6294 return rc;
6295}
6296
6297HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6298 ULONG aInstance,
6299 ComPtr<IStorageController> &aStorageController)
6300{
6301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6302
6303 for (StorageControllerList::const_iterator
6304 it = mStorageControllers->begin();
6305 it != mStorageControllers->end();
6306 ++it)
6307 {
6308 if ( (*it)->i_getStorageBus() == aConnectionType
6309 && (*it)->i_getInstance() == aInstance)
6310 {
6311 (*it).queryInterfaceTo(aStorageController.asOutParam());
6312 return S_OK;
6313 }
6314 }
6315
6316 return setError(VBOX_E_OBJECT_NOT_FOUND,
6317 tr("Could not find a storage controller with instance number '%lu'"),
6318 aInstance);
6319}
6320
6321HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6322{
6323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 HRESULT rc = i_checkStateDependency(MutableStateDep);
6326 if (FAILED(rc)) return rc;
6327
6328 ComObjPtr<StorageController> ctrl;
6329
6330 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6331 if (SUCCEEDED(rc))
6332 {
6333 /* Ensure that only one controller of each type is marked as bootable. */
6334 if (aBootable == TRUE)
6335 {
6336 for (StorageControllerList::const_iterator
6337 it = mStorageControllers->begin();
6338 it != mStorageControllers->end();
6339 ++it)
6340 {
6341 ComObjPtr<StorageController> aCtrl = (*it);
6342
6343 if ( (aCtrl->i_getName() != aName)
6344 && aCtrl->i_getBootable() == TRUE
6345 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6346 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6347 {
6348 aCtrl->i_setBootable(FALSE);
6349 break;
6350 }
6351 }
6352 }
6353
6354 if (SUCCEEDED(rc))
6355 {
6356 ctrl->i_setBootable(aBootable);
6357 i_setModified(IsModified_Storage);
6358 }
6359 }
6360
6361 if (SUCCEEDED(rc))
6362 {
6363 /* inform the direct session if any */
6364 alock.release();
6365 i_onStorageControllerChange();
6366 }
6367
6368 return rc;
6369}
6370
6371HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6372{
6373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6374
6375 HRESULT rc = i_checkStateDependency(MutableStateDep);
6376 if (FAILED(rc)) return rc;
6377
6378 ComObjPtr<StorageController> ctrl;
6379 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6380 if (FAILED(rc)) return rc;
6381
6382 {
6383 /* find all attached devices to the appropriate storage controller and detach them all */
6384 // make a temporary list because detachDevice invalidates iterators into
6385 // mMediumAttachments
6386 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6387
6388 for (MediumAttachmentList::const_iterator
6389 it = llAttachments2.begin();
6390 it != llAttachments2.end();
6391 ++it)
6392 {
6393 MediumAttachment *pAttachTemp = *it;
6394
6395 AutoCaller localAutoCaller(pAttachTemp);
6396 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6397
6398 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6399
6400 if (pAttachTemp->i_getControllerName() == aName)
6401 {
6402 rc = i_detachDevice(pAttachTemp, alock, NULL);
6403 if (FAILED(rc)) return rc;
6404 }
6405 }
6406 }
6407
6408 /* We can remove it now. */
6409 i_setModified(IsModified_Storage);
6410 mStorageControllers.backup();
6411
6412 ctrl->i_unshare();
6413
6414 mStorageControllers->remove(ctrl);
6415
6416 /* inform the direct session if any */
6417 alock.release();
6418 i_onStorageControllerChange();
6419
6420 return S_OK;
6421}
6422
6423HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6424 ComPtr<IUSBController> &aController)
6425{
6426 if ( (aType <= USBControllerType_Null)
6427 || (aType >= USBControllerType_Last))
6428 return setError(E_INVALIDARG,
6429 tr("Invalid USB controller type: %d"),
6430 aType);
6431
6432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6433
6434 HRESULT rc = i_checkStateDependency(MutableStateDep);
6435 if (FAILED(rc)) return rc;
6436
6437 /* try to find one with the same type first. */
6438 ComObjPtr<USBController> ctrl;
6439
6440 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6441 if (SUCCEEDED(rc))
6442 return setError(VBOX_E_OBJECT_IN_USE,
6443 tr("USB controller named '%s' already exists"),
6444 aName.c_str());
6445
6446 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6447 ULONG maxInstances;
6448 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6449 if (FAILED(rc))
6450 return rc;
6451
6452 ULONG cInstances = i_getUSBControllerCountByType(aType);
6453 if (cInstances >= maxInstances)
6454 return setError(E_INVALIDARG,
6455 tr("Too many USB controllers of this type"));
6456
6457 ctrl.createObject();
6458
6459 rc = ctrl->init(this, aName, aType);
6460 if (FAILED(rc)) return rc;
6461
6462 i_setModified(IsModified_USB);
6463 mUSBControllers.backup();
6464 mUSBControllers->push_back(ctrl);
6465
6466 ctrl.queryInterfaceTo(aController.asOutParam());
6467
6468 /* inform the direct session if any */
6469 alock.release();
6470 i_onUSBControllerChange();
6471
6472 return S_OK;
6473}
6474
6475HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6476{
6477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6478
6479 ComObjPtr<USBController> ctrl;
6480
6481 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6482 if (SUCCEEDED(rc))
6483 ctrl.queryInterfaceTo(aController.asOutParam());
6484
6485 return rc;
6486}
6487
6488HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6489 ULONG *aControllers)
6490{
6491 if ( (aType <= USBControllerType_Null)
6492 || (aType >= USBControllerType_Last))
6493 return setError(E_INVALIDARG,
6494 tr("Invalid USB controller type: %d"),
6495 aType);
6496
6497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6498
6499 ComObjPtr<USBController> ctrl;
6500
6501 *aControllers = i_getUSBControllerCountByType(aType);
6502
6503 return S_OK;
6504}
6505
6506HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6507{
6508
6509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6510
6511 HRESULT rc = i_checkStateDependency(MutableStateDep);
6512 if (FAILED(rc)) return rc;
6513
6514 ComObjPtr<USBController> ctrl;
6515 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6516 if (FAILED(rc)) return rc;
6517
6518 i_setModified(IsModified_USB);
6519 mUSBControllers.backup();
6520
6521 ctrl->i_unshare();
6522
6523 mUSBControllers->remove(ctrl);
6524
6525 /* inform the direct session if any */
6526 alock.release();
6527 i_onUSBControllerChange();
6528
6529 return S_OK;
6530}
6531
6532HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6533 ULONG *aOriginX,
6534 ULONG *aOriginY,
6535 ULONG *aWidth,
6536 ULONG *aHeight,
6537 BOOL *aEnabled)
6538{
6539 uint32_t u32OriginX= 0;
6540 uint32_t u32OriginY= 0;
6541 uint32_t u32Width = 0;
6542 uint32_t u32Height = 0;
6543 uint16_t u16Flags = 0;
6544
6545 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6546 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6547 if (RT_FAILURE(vrc))
6548 {
6549#ifdef RT_OS_WINDOWS
6550 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6551 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6552 * So just assign fEnable to TRUE again.
6553 * The right fix would be to change GUI API wrappers to make sure that parameters
6554 * are changed only if API succeeds.
6555 */
6556 *aEnabled = TRUE;
6557#endif
6558 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6559 tr("Saved guest size is not available (%Rrc)"),
6560 vrc);
6561 }
6562
6563 *aOriginX = u32OriginX;
6564 *aOriginY = u32OriginY;
6565 *aWidth = u32Width;
6566 *aHeight = u32Height;
6567 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6568
6569 return S_OK;
6570}
6571
6572HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6573 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6574{
6575 if (aScreenId != 0)
6576 return E_NOTIMPL;
6577
6578 if ( aBitmapFormat != BitmapFormat_BGR0
6579 && aBitmapFormat != BitmapFormat_BGRA
6580 && aBitmapFormat != BitmapFormat_RGBA
6581 && aBitmapFormat != BitmapFormat_PNG)
6582 return setError(E_NOTIMPL,
6583 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6584
6585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6586
6587 uint8_t *pu8Data = NULL;
6588 uint32_t cbData = 0;
6589 uint32_t u32Width = 0;
6590 uint32_t u32Height = 0;
6591
6592 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6593
6594 if (RT_FAILURE(vrc))
6595 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6596 tr("Saved thumbnail data is not available (%Rrc)"),
6597 vrc);
6598
6599 HRESULT hr = S_OK;
6600
6601 *aWidth = u32Width;
6602 *aHeight = u32Height;
6603
6604 if (cbData > 0)
6605 {
6606 /* Convert pixels to the format expected by the API caller. */
6607 if (aBitmapFormat == BitmapFormat_BGR0)
6608 {
6609 /* [0] B, [1] G, [2] R, [3] 0. */
6610 aData.resize(cbData);
6611 memcpy(&aData.front(), pu8Data, cbData);
6612 }
6613 else if (aBitmapFormat == BitmapFormat_BGRA)
6614 {
6615 /* [0] B, [1] G, [2] R, [3] A. */
6616 aData.resize(cbData);
6617 for (uint32_t i = 0; i < cbData; i += 4)
6618 {
6619 aData[i] = pu8Data[i];
6620 aData[i + 1] = pu8Data[i + 1];
6621 aData[i + 2] = pu8Data[i + 2];
6622 aData[i + 3] = 0xff;
6623 }
6624 }
6625 else if (aBitmapFormat == BitmapFormat_RGBA)
6626 {
6627 /* [0] R, [1] G, [2] B, [3] A. */
6628 aData.resize(cbData);
6629 for (uint32_t i = 0; i < cbData; i += 4)
6630 {
6631 aData[i] = pu8Data[i + 2];
6632 aData[i + 1] = pu8Data[i + 1];
6633 aData[i + 2] = pu8Data[i];
6634 aData[i + 3] = 0xff;
6635 }
6636 }
6637 else if (aBitmapFormat == BitmapFormat_PNG)
6638 {
6639 uint8_t *pu8PNG = NULL;
6640 uint32_t cbPNG = 0;
6641 uint32_t cxPNG = 0;
6642 uint32_t cyPNG = 0;
6643
6644 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6645
6646 if (RT_SUCCESS(vrc))
6647 {
6648 aData.resize(cbPNG);
6649 if (cbPNG)
6650 memcpy(&aData.front(), pu8PNG, cbPNG);
6651 }
6652 else
6653 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6654 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6655 vrc);
6656
6657 RTMemFree(pu8PNG);
6658 }
6659 }
6660
6661 freeSavedDisplayScreenshot(pu8Data);
6662
6663 return hr;
6664}
6665
6666HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6667 ULONG *aWidth,
6668 ULONG *aHeight,
6669 std::vector<BitmapFormat_T> &aBitmapFormats)
6670{
6671 if (aScreenId != 0)
6672 return E_NOTIMPL;
6673
6674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6675
6676 uint8_t *pu8Data = NULL;
6677 uint32_t cbData = 0;
6678 uint32_t u32Width = 0;
6679 uint32_t u32Height = 0;
6680
6681 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6682
6683 if (RT_FAILURE(vrc))
6684 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6685 tr("Saved screenshot data is not available (%Rrc)"),
6686 vrc);
6687
6688 *aWidth = u32Width;
6689 *aHeight = u32Height;
6690 aBitmapFormats.resize(1);
6691 aBitmapFormats[0] = BitmapFormat_PNG;
6692
6693 freeSavedDisplayScreenshot(pu8Data);
6694
6695 return S_OK;
6696}
6697
6698HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6699 BitmapFormat_T aBitmapFormat,
6700 ULONG *aWidth,
6701 ULONG *aHeight,
6702 std::vector<BYTE> &aData)
6703{
6704 if (aScreenId != 0)
6705 return E_NOTIMPL;
6706
6707 if (aBitmapFormat != BitmapFormat_PNG)
6708 return E_NOTIMPL;
6709
6710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6711
6712 uint8_t *pu8Data = NULL;
6713 uint32_t cbData = 0;
6714 uint32_t u32Width = 0;
6715 uint32_t u32Height = 0;
6716
6717 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6718
6719 if (RT_FAILURE(vrc))
6720 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6721 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6722 vrc);
6723
6724 *aWidth = u32Width;
6725 *aHeight = u32Height;
6726
6727 aData.resize(cbData);
6728 if (cbData)
6729 memcpy(&aData.front(), pu8Data, cbData);
6730
6731 freeSavedDisplayScreenshot(pu8Data);
6732
6733 return S_OK;
6734}
6735
6736HRESULT Machine::hotPlugCPU(ULONG aCpu)
6737{
6738 HRESULT rc = S_OK;
6739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6740
6741 if (!mHWData->mCPUHotPlugEnabled)
6742 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6743
6744 if (aCpu >= mHWData->mCPUCount)
6745 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6746
6747 if (mHWData->mCPUAttached[aCpu])
6748 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6749
6750 alock.release();
6751 rc = i_onCPUChange(aCpu, false);
6752 alock.acquire();
6753 if (FAILED(rc)) return rc;
6754
6755 i_setModified(IsModified_MachineData);
6756 mHWData.backup();
6757 mHWData->mCPUAttached[aCpu] = true;
6758
6759 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6760 if (Global::IsOnline(mData->mMachineState))
6761 i_saveSettings(NULL);
6762
6763 return S_OK;
6764}
6765
6766HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6767{
6768 HRESULT rc = S_OK;
6769
6770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6771
6772 if (!mHWData->mCPUHotPlugEnabled)
6773 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6774
6775 if (aCpu >= SchemaDefs::MaxCPUCount)
6776 return setError(E_INVALIDARG,
6777 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6778 SchemaDefs::MaxCPUCount);
6779
6780 if (!mHWData->mCPUAttached[aCpu])
6781 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6782
6783 /* CPU 0 can't be detached */
6784 if (aCpu == 0)
6785 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6786
6787 alock.release();
6788 rc = i_onCPUChange(aCpu, true);
6789 alock.acquire();
6790 if (FAILED(rc)) return rc;
6791
6792 i_setModified(IsModified_MachineData);
6793 mHWData.backup();
6794 mHWData->mCPUAttached[aCpu] = false;
6795
6796 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6797 if (Global::IsOnline(mData->mMachineState))
6798 i_saveSettings(NULL);
6799
6800 return S_OK;
6801}
6802
6803HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6804{
6805 *aAttached = false;
6806
6807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6808
6809 /* If hotplug is enabled the CPU is always enabled. */
6810 if (!mHWData->mCPUHotPlugEnabled)
6811 {
6812 if (aCpu < mHWData->mCPUCount)
6813 *aAttached = true;
6814 }
6815 else
6816 {
6817 if (aCpu < SchemaDefs::MaxCPUCount)
6818 *aAttached = mHWData->mCPUAttached[aCpu];
6819 }
6820
6821 return S_OK;
6822}
6823
6824HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6825{
6826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6827
6828 Utf8Str log = i_getLogFilename(aIdx);
6829 if (!RTFileExists(log.c_str()))
6830 log.setNull();
6831 aFilename = log;
6832
6833 return S_OK;
6834}
6835
6836HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6837{
6838 if (aSize < 0)
6839 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6840
6841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6842
6843 HRESULT rc = S_OK;
6844 Utf8Str log = i_getLogFilename(aIdx);
6845
6846 /* do not unnecessarily hold the lock while doing something which does
6847 * not need the lock and potentially takes a long time. */
6848 alock.release();
6849
6850 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6851 * keeps the SOAP reply size under 1M for the webservice (we're using
6852 * base64 encoded strings for binary data for years now, avoiding the
6853 * expansion of each byte array element to approx. 25 bytes of XML. */
6854 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6855 aData.resize(cbData);
6856
6857 RTFILE LogFile;
6858 int vrc = RTFileOpen(&LogFile, log.c_str(),
6859 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6860 if (RT_SUCCESS(vrc))
6861 {
6862 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6863 if (RT_SUCCESS(vrc))
6864 aData.resize(cbData);
6865 else
6866 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6867 tr("Could not read log file '%s' (%Rrc)"),
6868 log.c_str(), vrc);
6869 RTFileClose(LogFile);
6870 }
6871 else
6872 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6873 tr("Could not open log file '%s' (%Rrc)"),
6874 log.c_str(), vrc);
6875
6876 if (FAILED(rc))
6877 aData.resize(0);
6878
6879 return rc;
6880}
6881
6882
6883/**
6884 * Currently this method doesn't attach device to the running VM,
6885 * just makes sure it's plugged on next VM start.
6886 */
6887HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6888{
6889 // lock scope
6890 {
6891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6892
6893 HRESULT rc = i_checkStateDependency(MutableStateDep);
6894 if (FAILED(rc)) return rc;
6895
6896 ChipsetType_T aChipset = ChipsetType_PIIX3;
6897 COMGETTER(ChipsetType)(&aChipset);
6898
6899 if (aChipset != ChipsetType_ICH9)
6900 {
6901 return setError(E_INVALIDARG,
6902 tr("Host PCI attachment only supported with ICH9 chipset"));
6903 }
6904
6905 // check if device with this host PCI address already attached
6906 for (HWData::PCIDeviceAssignmentList::const_iterator
6907 it = mHWData->mPCIDeviceAssignments.begin();
6908 it != mHWData->mPCIDeviceAssignments.end();
6909 ++it)
6910 {
6911 LONG iHostAddress = -1;
6912 ComPtr<PCIDeviceAttachment> pAttach;
6913 pAttach = *it;
6914 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6915 if (iHostAddress == aHostAddress)
6916 return setError(E_INVALIDARG,
6917 tr("Device with host PCI address already attached to this VM"));
6918 }
6919
6920 ComObjPtr<PCIDeviceAttachment> pda;
6921 char name[32];
6922
6923 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6924 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6925 pda.createObject();
6926 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6927 i_setModified(IsModified_MachineData);
6928 mHWData.backup();
6929 mHWData->mPCIDeviceAssignments.push_back(pda);
6930 }
6931
6932 return S_OK;
6933}
6934
6935/**
6936 * Currently this method doesn't detach device from the running VM,
6937 * just makes sure it's not plugged on next VM start.
6938 */
6939HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6940{
6941 ComObjPtr<PCIDeviceAttachment> pAttach;
6942 bool fRemoved = false;
6943 HRESULT rc;
6944
6945 // lock scope
6946 {
6947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6948
6949 rc = i_checkStateDependency(MutableStateDep);
6950 if (FAILED(rc)) return rc;
6951
6952 for (HWData::PCIDeviceAssignmentList::const_iterator
6953 it = mHWData->mPCIDeviceAssignments.begin();
6954 it != mHWData->mPCIDeviceAssignments.end();
6955 ++it)
6956 {
6957 LONG iHostAddress = -1;
6958 pAttach = *it;
6959 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6960 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6961 {
6962 i_setModified(IsModified_MachineData);
6963 mHWData.backup();
6964 mHWData->mPCIDeviceAssignments.remove(pAttach);
6965 fRemoved = true;
6966 break;
6967 }
6968 }
6969 }
6970
6971
6972 /* Fire event outside of the lock */
6973 if (fRemoved)
6974 {
6975 Assert(!pAttach.isNull());
6976 ComPtr<IEventSource> es;
6977 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6978 Assert(SUCCEEDED(rc));
6979 Bstr mid;
6980 rc = this->COMGETTER(Id)(mid.asOutParam());
6981 Assert(SUCCEEDED(rc));
6982 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6983 }
6984
6985 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6986 tr("No host PCI device %08x attached"),
6987 aHostAddress
6988 );
6989}
6990
6991HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6992{
6993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6994
6995 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6996 size_t i = 0;
6997 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6998 it = mHWData->mPCIDeviceAssignments.begin();
6999 it != mHWData->mPCIDeviceAssignments.end();
7000 ++it, ++i)
7001 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7002
7003 return S_OK;
7004}
7005
7006HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7007{
7008 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7009
7010 return S_OK;
7011}
7012
7013HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7014{
7015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7016
7017 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7018
7019 return S_OK;
7020}
7021
7022HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7023{
7024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7025 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7026 if (SUCCEEDED(hrc))
7027 {
7028 hrc = mHWData.backupEx();
7029 if (SUCCEEDED(hrc))
7030 {
7031 i_setModified(IsModified_MachineData);
7032 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7033 }
7034 }
7035 return hrc;
7036}
7037
7038HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7039{
7040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7041 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7042 return S_OK;
7043}
7044
7045HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7046{
7047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7048 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7049 if (SUCCEEDED(hrc))
7050 {
7051 hrc = mHWData.backupEx();
7052 if (SUCCEEDED(hrc))
7053 {
7054 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7055 if (SUCCEEDED(hrc))
7056 i_setModified(IsModified_MachineData);
7057 }
7058 }
7059 return hrc;
7060}
7061
7062HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7063{
7064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7065
7066 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7067
7068 return S_OK;
7069}
7070
7071HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7072{
7073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7074 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7075 if (SUCCEEDED(hrc))
7076 {
7077 hrc = mHWData.backupEx();
7078 if (SUCCEEDED(hrc))
7079 {
7080 i_setModified(IsModified_MachineData);
7081 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7082 }
7083 }
7084 return hrc;
7085}
7086
7087HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7088{
7089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7090
7091 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7092
7093 return S_OK;
7094}
7095
7096HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7097{
7098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7099
7100 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7101 if ( SUCCEEDED(hrc)
7102 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7103 {
7104 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7105 int vrc;
7106
7107 if (aAutostartEnabled)
7108 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7109 else
7110 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7111
7112 if (RT_SUCCESS(vrc))
7113 {
7114 hrc = mHWData.backupEx();
7115 if (SUCCEEDED(hrc))
7116 {
7117 i_setModified(IsModified_MachineData);
7118 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7119 }
7120 }
7121 else if (vrc == VERR_NOT_SUPPORTED)
7122 hrc = setError(VBOX_E_NOT_SUPPORTED,
7123 tr("The VM autostart feature is not supported on this platform"));
7124 else if (vrc == VERR_PATH_NOT_FOUND)
7125 hrc = setError(E_FAIL,
7126 tr("The path to the autostart database is not set"));
7127 else
7128 hrc = setError(E_UNEXPECTED,
7129 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7130 aAutostartEnabled ? "Adding" : "Removing",
7131 mUserData->s.strName.c_str(), vrc);
7132 }
7133 return hrc;
7134}
7135
7136HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7137{
7138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7139
7140 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7141
7142 return S_OK;
7143}
7144
7145HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7146{
7147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7148 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7149 if (SUCCEEDED(hrc))
7150 {
7151 hrc = mHWData.backupEx();
7152 if (SUCCEEDED(hrc))
7153 {
7154 i_setModified(IsModified_MachineData);
7155 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7156 }
7157 }
7158 return hrc;
7159}
7160
7161HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7162{
7163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7164
7165 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7166
7167 return S_OK;
7168}
7169
7170HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7171{
7172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7173 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7174 if ( SUCCEEDED(hrc)
7175 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7176 {
7177 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7178 int vrc;
7179
7180 if (aAutostopType != AutostopType_Disabled)
7181 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7182 else
7183 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7184
7185 if (RT_SUCCESS(vrc))
7186 {
7187 hrc = mHWData.backupEx();
7188 if (SUCCEEDED(hrc))
7189 {
7190 i_setModified(IsModified_MachineData);
7191 mHWData->mAutostart.enmAutostopType = aAutostopType;
7192 }
7193 }
7194 else if (vrc == VERR_NOT_SUPPORTED)
7195 hrc = setError(VBOX_E_NOT_SUPPORTED,
7196 tr("The VM autostop feature is not supported on this platform"));
7197 else if (vrc == VERR_PATH_NOT_FOUND)
7198 hrc = setError(E_FAIL,
7199 tr("The path to the autostart database is not set"));
7200 else
7201 hrc = setError(E_UNEXPECTED,
7202 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7203 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7204 mUserData->s.strName.c_str(), vrc);
7205 }
7206 return hrc;
7207}
7208
7209HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7210{
7211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7212
7213 aDefaultFrontend = mHWData->mDefaultFrontend;
7214
7215 return S_OK;
7216}
7217
7218HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7219{
7220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7221 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7222 if (SUCCEEDED(hrc))
7223 {
7224 hrc = mHWData.backupEx();
7225 if (SUCCEEDED(hrc))
7226 {
7227 i_setModified(IsModified_MachineData);
7228 mHWData->mDefaultFrontend = aDefaultFrontend;
7229 }
7230 }
7231 return hrc;
7232}
7233
7234HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7235{
7236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7237 size_t cbIcon = mUserData->s.ovIcon.size();
7238 aIcon.resize(cbIcon);
7239 if (cbIcon)
7240 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7241 return S_OK;
7242}
7243
7244HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7245{
7246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7247 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7248 if (SUCCEEDED(hrc))
7249 {
7250 i_setModified(IsModified_MachineData);
7251 mUserData.backup();
7252 size_t cbIcon = aIcon.size();
7253 mUserData->s.ovIcon.resize(cbIcon);
7254 if (cbIcon)
7255 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7256 }
7257 return hrc;
7258}
7259
7260HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7261{
7262#ifdef VBOX_WITH_USB
7263 *aUSBProxyAvailable = true;
7264#else
7265 *aUSBProxyAvailable = false;
7266#endif
7267 return S_OK;
7268}
7269
7270HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7271{
7272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7273
7274 aVMProcessPriority = mUserData->s.strVMPriority;
7275
7276 return S_OK;
7277}
7278
7279HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7280{
7281 RT_NOREF(aVMProcessPriority);
7282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7283 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7284 if (SUCCEEDED(hrc))
7285 {
7286 /** @todo r=klaus: currently this is marked as not implemented, as
7287 * the code for setting the priority of the process is not there
7288 * (neither when starting the VM nor at runtime). */
7289 ReturnComNotImplemented();
7290#if 0
7291 hrc = mUserData.backupEx();
7292 if (SUCCEEDED(hrc))
7293 {
7294 i_setModified(IsModified_MachineData);
7295 mUserData->s.strVMPriority = aVMProcessPriority;
7296 }
7297#endif
7298 }
7299 return hrc;
7300}
7301
7302HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7303 ComPtr<IProgress> &aProgress)
7304{
7305 ComObjPtr<Progress> pP;
7306 Progress *ppP = pP;
7307 IProgress *iP = static_cast<IProgress *>(ppP);
7308 IProgress **pProgress = &iP;
7309
7310 IMachine *pTarget = aTarget;
7311
7312 /* Convert the options. */
7313 RTCList<CloneOptions_T> optList;
7314 if (aOptions.size())
7315 for (size_t i = 0; i < aOptions.size(); ++i)
7316 optList.append(aOptions[i]);
7317
7318 if (optList.contains(CloneOptions_Link))
7319 {
7320 if (!i_isSnapshotMachine())
7321 return setError(E_INVALIDARG,
7322 tr("Linked clone can only be created from a snapshot"));
7323 if (aMode != CloneMode_MachineState)
7324 return setError(E_INVALIDARG,
7325 tr("Linked clone can only be created for a single machine state"));
7326 }
7327 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7328
7329 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7330
7331 HRESULT rc = pWorker->start(pProgress);
7332
7333 pP = static_cast<Progress *>(*pProgress);
7334 pP.queryInterfaceTo(aProgress.asOutParam());
7335
7336 return rc;
7337
7338}
7339
7340HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7341 const com::Utf8Str &aType,
7342 ComPtr<IProgress> &aProgress)
7343{
7344 LogFlowThisFuncEnter();
7345
7346 ComObjPtr<Progress> progress;
7347
7348 progress.createObject();
7349
7350 HRESULT rc = S_OK;
7351 Utf8Str targetPath = aTargetPath;
7352 Utf8Str type = aType;
7353
7354 /* Initialize our worker task */
7355 MachineMoveVM* task = NULL;
7356 try
7357 {
7358 task = new MachineMoveVM(this, targetPath, type, progress);
7359 }
7360 catch(...)
7361 {
7362 delete task;
7363 return rc;
7364 }
7365
7366 /*
7367 * task pointer will be owned by the ThreadTask class.
7368 * There is no need to call operator "delete" in the end.
7369 */
7370 rc = task->init();
7371 if (SUCCEEDED(rc))
7372 {
7373 rc = task->createThread();
7374 if (FAILED(rc))
7375 {
7376 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7377 }
7378
7379 /* Return progress to the caller */
7380 progress.queryInterfaceTo(aProgress.asOutParam());
7381 }
7382
7383 LogFlowThisFuncLeave();
7384 return rc;
7385
7386}
7387
7388HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7389{
7390 NOREF(aProgress);
7391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7392
7393 // This check should always fail.
7394 HRESULT rc = i_checkStateDependency(MutableStateDep);
7395 if (FAILED(rc)) return rc;
7396
7397 AssertFailedReturn(E_NOTIMPL);
7398}
7399
7400HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7401{
7402 NOREF(aSavedStateFile);
7403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7404
7405 // This check should always fail.
7406 HRESULT rc = i_checkStateDependency(MutableStateDep);
7407 if (FAILED(rc)) return rc;
7408
7409 AssertFailedReturn(E_NOTIMPL);
7410}
7411
7412HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7413{
7414 NOREF(aFRemoveFile);
7415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7416
7417 // This check should always fail.
7418 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7419 if (FAILED(rc)) return rc;
7420
7421 AssertFailedReturn(E_NOTIMPL);
7422}
7423
7424// public methods for internal purposes
7425/////////////////////////////////////////////////////////////////////////////
7426
7427/**
7428 * Adds the given IsModified_* flag to the dirty flags of the machine.
7429 * This must be called either during i_loadSettings or under the machine write lock.
7430 * @param fl Flag
7431 * @param fAllowStateModification If state modifications are allowed.
7432 */
7433void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7434{
7435 mData->flModifications |= fl;
7436 if (fAllowStateModification && i_isStateModificationAllowed())
7437 mData->mCurrentStateModified = true;
7438}
7439
7440/**
7441 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7442 * care of the write locking.
7443 *
7444 * @param fModification The flag to add.
7445 * @param fAllowStateModification If state modifications are allowed.
7446 */
7447void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7448{
7449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7450 i_setModified(fModification, fAllowStateModification);
7451}
7452
7453/**
7454 * Saves the registry entry of this machine to the given configuration node.
7455 *
7456 * @param data Machine registry data.
7457 *
7458 * @note locks this object for reading.
7459 */
7460HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7461{
7462 AutoLimitedCaller autoCaller(this);
7463 AssertComRCReturnRC(autoCaller.rc());
7464
7465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7466
7467 data.uuid = mData->mUuid;
7468 data.strSettingsFile = mData->m_strConfigFile;
7469
7470 return S_OK;
7471}
7472
7473/**
7474 * Calculates the absolute path of the given path taking the directory of the
7475 * machine settings file as the current directory.
7476 *
7477 * @param strPath Path to calculate the absolute path for.
7478 * @param aResult Where to put the result (used only on success, can be the
7479 * same Utf8Str instance as passed in @a aPath).
7480 * @return IPRT result.
7481 *
7482 * @note Locks this object for reading.
7483 */
7484int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7485{
7486 AutoCaller autoCaller(this);
7487 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7488
7489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7490
7491 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7492
7493 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7494
7495 strSettingsDir.stripFilename();
7496 char folder[RTPATH_MAX];
7497 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7498 if (RT_SUCCESS(vrc))
7499 aResult = folder;
7500
7501 return vrc;
7502}
7503
7504/**
7505 * Copies strSource to strTarget, making it relative to the machine folder
7506 * if it is a subdirectory thereof, or simply copying it otherwise.
7507 *
7508 * @param strSource Path to evaluate and copy.
7509 * @param strTarget Buffer to receive target path.
7510 *
7511 * @note Locks this object for reading.
7512 */
7513void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7514 Utf8Str &strTarget)
7515{
7516 AutoCaller autoCaller(this);
7517 AssertComRCReturn(autoCaller.rc(), (void)0);
7518
7519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7520
7521 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7522 // use strTarget as a temporary buffer to hold the machine settings dir
7523 strTarget = mData->m_strConfigFileFull;
7524 strTarget.stripFilename();
7525 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7526 {
7527 // is relative: then append what's left
7528 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7529 // for empty paths (only possible for subdirs) use "." to avoid
7530 // triggering default settings for not present config attributes.
7531 if (strTarget.isEmpty())
7532 strTarget = ".";
7533 }
7534 else
7535 // is not relative: then overwrite
7536 strTarget = strSource;
7537}
7538
7539/**
7540 * Returns the full path to the machine's log folder in the
7541 * \a aLogFolder argument.
7542 */
7543void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7544{
7545 AutoCaller autoCaller(this);
7546 AssertComRCReturnVoid(autoCaller.rc());
7547
7548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7549
7550 char szTmp[RTPATH_MAX];
7551 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7552 if (RT_SUCCESS(vrc))
7553 {
7554 if (szTmp[0] && !mUserData.isNull())
7555 {
7556 char szTmp2[RTPATH_MAX];
7557 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7558 if (RT_SUCCESS(vrc))
7559 aLogFolder = Utf8StrFmt("%s%c%s",
7560 szTmp2,
7561 RTPATH_DELIMITER,
7562 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7563 }
7564 else
7565 vrc = VERR_PATH_IS_RELATIVE;
7566 }
7567
7568 if (RT_FAILURE(vrc))
7569 {
7570 // fallback if VBOX_USER_LOGHOME is not set or invalid
7571 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7572 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7573 aLogFolder.append(RTPATH_DELIMITER);
7574 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7575 }
7576}
7577
7578/**
7579 * Returns the full path to the machine's log file for an given index.
7580 */
7581Utf8Str Machine::i_getLogFilename(ULONG idx)
7582{
7583 Utf8Str logFolder;
7584 getLogFolder(logFolder);
7585 Assert(logFolder.length());
7586
7587 Utf8Str log;
7588 if (idx == 0)
7589 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7590#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7591 else if (idx == 1)
7592 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7593 else
7594 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7595#else
7596 else
7597 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7598#endif
7599 return log;
7600}
7601
7602/**
7603 * Returns the full path to the machine's hardened log file.
7604 */
7605Utf8Str Machine::i_getHardeningLogFilename(void)
7606{
7607 Utf8Str strFilename;
7608 getLogFolder(strFilename);
7609 Assert(strFilename.length());
7610 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7611 return strFilename;
7612}
7613
7614
7615/**
7616 * Composes a unique saved state filename based on the current system time. The filename is
7617 * granular to the second so this will work so long as no more than one snapshot is taken on
7618 * a machine per second.
7619 *
7620 * Before version 4.1, we used this formula for saved state files:
7621 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7622 * which no longer works because saved state files can now be shared between the saved state of the
7623 * "saved" machine and an online snapshot, and the following would cause problems:
7624 * 1) save machine
7625 * 2) create online snapshot from that machine state --> reusing saved state file
7626 * 3) save machine again --> filename would be reused, breaking the online snapshot
7627 *
7628 * So instead we now use a timestamp.
7629 *
7630 * @param strStateFilePath
7631 */
7632
7633void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7634{
7635 AutoCaller autoCaller(this);
7636 AssertComRCReturnVoid(autoCaller.rc());
7637
7638 {
7639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7640 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7641 }
7642
7643 RTTIMESPEC ts;
7644 RTTimeNow(&ts);
7645 RTTIME time;
7646 RTTimeExplode(&time, &ts);
7647
7648 strStateFilePath += RTPATH_DELIMITER;
7649 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7650 time.i32Year, time.u8Month, time.u8MonthDay,
7651 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7652}
7653
7654/**
7655 * Returns the full path to the default video capture file.
7656 */
7657void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7658{
7659 AutoCaller autoCaller(this);
7660 AssertComRCReturnVoid(autoCaller.rc());
7661
7662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7663
7664 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7665 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7666 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7667}
7668
7669/**
7670 * Returns whether at least one USB controller is present for the VM.
7671 */
7672bool Machine::i_isUSBControllerPresent()
7673{
7674 AutoCaller autoCaller(this);
7675 AssertComRCReturn(autoCaller.rc(), false);
7676
7677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7678
7679 return (mUSBControllers->size() > 0);
7680}
7681
7682/**
7683 * @note Locks this object for writing, calls the client process
7684 * (inside the lock).
7685 */
7686HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7687 const Utf8Str &strFrontend,
7688 const Utf8Str &strEnvironment,
7689 ProgressProxy *aProgress)
7690{
7691 LogFlowThisFuncEnter();
7692
7693 AssertReturn(aControl, E_FAIL);
7694 AssertReturn(aProgress, E_FAIL);
7695 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7696
7697 AutoCaller autoCaller(this);
7698 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7699
7700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7701
7702 if (!mData->mRegistered)
7703 return setError(E_UNEXPECTED,
7704 tr("The machine '%s' is not registered"),
7705 mUserData->s.strName.c_str());
7706
7707 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7708
7709 /* The process started when launching a VM with separate UI/VM processes is always
7710 * the UI process, i.e. needs special handling as it won't claim the session. */
7711 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7712
7713 if (fSeparate)
7714 {
7715 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7716 return setError(VBOX_E_INVALID_OBJECT_STATE,
7717 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7718 mUserData->s.strName.c_str());
7719 }
7720 else
7721 {
7722 if ( mData->mSession.mState == SessionState_Locked
7723 || mData->mSession.mState == SessionState_Spawning
7724 || mData->mSession.mState == SessionState_Unlocking)
7725 return setError(VBOX_E_INVALID_OBJECT_STATE,
7726 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7727 mUserData->s.strName.c_str());
7728
7729 /* may not be busy */
7730 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7731 }
7732
7733 /* get the path to the executable */
7734 char szPath[RTPATH_MAX];
7735 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7736 size_t cchBufLeft = strlen(szPath);
7737 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7738 szPath[cchBufLeft] = 0;
7739 char *pszNamePart = szPath + cchBufLeft;
7740 cchBufLeft = sizeof(szPath) - cchBufLeft;
7741
7742 int vrc = VINF_SUCCESS;
7743 RTPROCESS pid = NIL_RTPROCESS;
7744
7745 RTENV env = RTENV_DEFAULT;
7746
7747 if (!strEnvironment.isEmpty())
7748 {
7749 char *newEnvStr = NULL;
7750
7751 do
7752 {
7753 /* clone the current environment */
7754 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7755 AssertRCBreakStmt(vrc2, vrc = vrc2);
7756
7757 newEnvStr = RTStrDup(strEnvironment.c_str());
7758 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7759
7760 /* put new variables to the environment
7761 * (ignore empty variable names here since RTEnv API
7762 * intentionally doesn't do that) */
7763 char *var = newEnvStr;
7764 for (char *p = newEnvStr; *p; ++p)
7765 {
7766 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7767 {
7768 *p = '\0';
7769 if (*var)
7770 {
7771 char *val = strchr(var, '=');
7772 if (val)
7773 {
7774 *val++ = '\0';
7775 vrc2 = RTEnvSetEx(env, var, val);
7776 }
7777 else
7778 vrc2 = RTEnvUnsetEx(env, var);
7779 if (RT_FAILURE(vrc2))
7780 break;
7781 }
7782 var = p + 1;
7783 }
7784 }
7785 if (RT_SUCCESS(vrc2) && *var)
7786 vrc2 = RTEnvPutEx(env, var);
7787
7788 AssertRCBreakStmt(vrc2, vrc = vrc2);
7789 }
7790 while (0);
7791
7792 if (newEnvStr != NULL)
7793 RTStrFree(newEnvStr);
7794 }
7795
7796 /* Hardening logging */
7797#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7798 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7799 {
7800 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7801 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7802 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7803 {
7804 Utf8Str strStartupLogDir = strHardeningLogFile;
7805 strStartupLogDir.stripFilename();
7806 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7807 file without stripping the file. */
7808 }
7809 strSupHardeningLogArg.append(strHardeningLogFile);
7810
7811 /* Remove legacy log filename to avoid confusion. */
7812 Utf8Str strOldStartupLogFile;
7813 getLogFolder(strOldStartupLogFile);
7814 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7815 RTFileDelete(strOldStartupLogFile.c_str());
7816 }
7817 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7818#else
7819 const char *pszSupHardeningLogArg = NULL;
7820#endif
7821
7822 Utf8Str strCanonicalName;
7823
7824#ifdef VBOX_WITH_QTGUI
7825 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7826 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7827 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7828 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7829 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7830 {
7831 strCanonicalName = "GUI/Qt";
7832# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7833 /* Modify the base path so that we don't need to use ".." below. */
7834 RTPathStripTrailingSlash(szPath);
7835 RTPathStripFilename(szPath);
7836 cchBufLeft = strlen(szPath);
7837 pszNamePart = szPath + cchBufLeft;
7838 cchBufLeft = sizeof(szPath) - cchBufLeft;
7839
7840# define OSX_APP_NAME "VirtualBoxVM"
7841# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7842
7843 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7844 if ( strAppOverride.contains(".")
7845 || strAppOverride.contains("/")
7846 || strAppOverride.contains("\\")
7847 || strAppOverride.contains(":"))
7848 strAppOverride.setNull();
7849 Utf8Str strAppPath;
7850 if (!strAppOverride.isEmpty())
7851 {
7852 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7853 Utf8Str strFullPath(szPath);
7854 strFullPath.append(strAppPath);
7855 /* there is a race, but people using this deserve the failure */
7856 if (!RTFileExists(strFullPath.c_str()))
7857 strAppOverride.setNull();
7858 }
7859 if (strAppOverride.isEmpty())
7860 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7861 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7862 strcpy(pszNamePart, strAppPath.c_str());
7863# else
7864# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7865 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7866# else
7867 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7868# endif
7869 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7870 strcpy(pszNamePart, s_szVirtualBox_exe);
7871# endif
7872
7873 Utf8Str idStr = mData->mUuid.toString();
7874 const char *apszArgs[] =
7875 {
7876 szPath,
7877 "--comment", mUserData->s.strName.c_str(),
7878 "--startvm", idStr.c_str(),
7879 "--no-startvm-errormsgbox",
7880 NULL, /* For "--separate". */
7881 NULL, /* For "--sup-startup-log". */
7882 NULL
7883 };
7884 unsigned iArg = 6;
7885 if (fSeparate)
7886 apszArgs[iArg++] = "--separate";
7887 apszArgs[iArg++] = pszSupHardeningLogArg;
7888
7889 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7890 }
7891#else /* !VBOX_WITH_QTGUI */
7892 if (0)
7893 ;
7894#endif /* VBOX_WITH_QTGUI */
7895
7896 else
7897
7898#ifdef VBOX_WITH_VBOXSDL
7899 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7900 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7901 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7902 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7903 {
7904 strCanonicalName = "GUI/SDL";
7905 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7906 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7907 strcpy(pszNamePart, s_szVBoxSDL_exe);
7908
7909 Utf8Str idStr = mData->mUuid.toString();
7910 const char *apszArgs[] =
7911 {
7912 szPath,
7913 "--comment", mUserData->s.strName.c_str(),
7914 "--startvm", idStr.c_str(),
7915 NULL, /* For "--separate". */
7916 NULL, /* For "--sup-startup-log". */
7917 NULL
7918 };
7919 unsigned iArg = 5;
7920 if (fSeparate)
7921 apszArgs[iArg++] = "--separate";
7922 apszArgs[iArg++] = pszSupHardeningLogArg;
7923
7924 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7925 }
7926#else /* !VBOX_WITH_VBOXSDL */
7927 if (0)
7928 ;
7929#endif /* !VBOX_WITH_VBOXSDL */
7930
7931 else
7932
7933#ifdef VBOX_WITH_HEADLESS
7934 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7935 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7936 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7937 )
7938 {
7939 strCanonicalName = "headless";
7940 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7941 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7942 * and a VM works even if the server has not been installed.
7943 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7944 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7945 * differently in 4.0 and 3.x.
7946 */
7947 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7948 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7949 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7950
7951 Utf8Str idStr = mData->mUuid.toString();
7952 const char *apszArgs[] =
7953 {
7954 szPath,
7955 "--comment", mUserData->s.strName.c_str(),
7956 "--startvm", idStr.c_str(),
7957 "--vrde", "config",
7958 NULL, /* For "--capture". */
7959 NULL, /* For "--sup-startup-log". */
7960 NULL
7961 };
7962 unsigned iArg = 7;
7963 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7964 apszArgs[iArg++] = "--capture";
7965 apszArgs[iArg++] = pszSupHardeningLogArg;
7966
7967# ifdef RT_OS_WINDOWS
7968 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7969# else
7970 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7971# endif
7972 }
7973#else /* !VBOX_WITH_HEADLESS */
7974 if (0)
7975 ;
7976#endif /* !VBOX_WITH_HEADLESS */
7977 else
7978 {
7979 RTEnvDestroy(env);
7980 return setError(E_INVALIDARG,
7981 tr("Invalid frontend name: '%s'"),
7982 strFrontend.c_str());
7983 }
7984
7985 RTEnvDestroy(env);
7986
7987 if (RT_FAILURE(vrc))
7988 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7989 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7990 mUserData->s.strName.c_str(), vrc);
7991
7992 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7993
7994 if (!fSeparate)
7995 {
7996 /*
7997 * Note that we don't release the lock here before calling the client,
7998 * because it doesn't need to call us back if called with a NULL argument.
7999 * Releasing the lock here is dangerous because we didn't prepare the
8000 * launch data yet, but the client we've just started may happen to be
8001 * too fast and call LockMachine() that will fail (because of PID, etc.),
8002 * so that the Machine will never get out of the Spawning session state.
8003 */
8004
8005 /* inform the session that it will be a remote one */
8006 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8007#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8008 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8009#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8010 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8011#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8012 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8013
8014 if (FAILED(rc))
8015 {
8016 /* restore the session state */
8017 mData->mSession.mState = SessionState_Unlocked;
8018 alock.release();
8019 mParent->i_addProcessToReap(pid);
8020 /* The failure may occur w/o any error info (from RPC), so provide one */
8021 return setError(VBOX_E_VM_ERROR,
8022 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8023 }
8024
8025 /* attach launch data to the machine */
8026 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8027 mData->mSession.mRemoteControls.push_back(aControl);
8028 mData->mSession.mProgress = aProgress;
8029 mData->mSession.mPID = pid;
8030 mData->mSession.mState = SessionState_Spawning;
8031 Assert(strCanonicalName.isNotEmpty());
8032 mData->mSession.mName = strCanonicalName;
8033 }
8034 else
8035 {
8036 /* For separate UI process we declare the launch as completed instantly, as the
8037 * actual headless VM start may or may not come. No point in remembering anything
8038 * yet, as what matters for us is when the headless VM gets started. */
8039 aProgress->i_notifyComplete(S_OK);
8040 }
8041
8042 alock.release();
8043 mParent->i_addProcessToReap(pid);
8044
8045 LogFlowThisFuncLeave();
8046 return S_OK;
8047}
8048
8049/**
8050 * Returns @c true if the given session machine instance has an open direct
8051 * session (and optionally also for direct sessions which are closing) and
8052 * returns the session control machine instance if so.
8053 *
8054 * Note that when the method returns @c false, the arguments remain unchanged.
8055 *
8056 * @param aMachine Session machine object.
8057 * @param aControl Direct session control object (optional).
8058 * @param aRequireVM If true then only allow VM sessions.
8059 * @param aAllowClosing If true then additionally a session which is currently
8060 * being closed will also be allowed.
8061 *
8062 * @note locks this object for reading.
8063 */
8064bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8065 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8066 bool aRequireVM /*= false*/,
8067 bool aAllowClosing /*= false*/)
8068{
8069 AutoLimitedCaller autoCaller(this);
8070 AssertComRCReturn(autoCaller.rc(), false);
8071
8072 /* just return false for inaccessible machines */
8073 if (getObjectState().getState() != ObjectState::Ready)
8074 return false;
8075
8076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8077
8078 if ( ( mData->mSession.mState == SessionState_Locked
8079 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8080 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8081 )
8082 {
8083 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8084
8085 aMachine = mData->mSession.mMachine;
8086
8087 if (aControl != NULL)
8088 *aControl = mData->mSession.mDirectControl;
8089
8090 return true;
8091 }
8092
8093 return false;
8094}
8095
8096/**
8097 * Returns @c true if the given machine has an spawning direct session.
8098 *
8099 * @note locks this object for reading.
8100 */
8101bool Machine::i_isSessionSpawning()
8102{
8103 AutoLimitedCaller autoCaller(this);
8104 AssertComRCReturn(autoCaller.rc(), false);
8105
8106 /* just return false for inaccessible machines */
8107 if (getObjectState().getState() != ObjectState::Ready)
8108 return false;
8109
8110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8111
8112 if (mData->mSession.mState == SessionState_Spawning)
8113 return true;
8114
8115 return false;
8116}
8117
8118/**
8119 * Called from the client watcher thread to check for unexpected client process
8120 * death during Session_Spawning state (e.g. before it successfully opened a
8121 * direct session).
8122 *
8123 * On Win32 and on OS/2, this method is called only when we've got the
8124 * direct client's process termination notification, so it always returns @c
8125 * true.
8126 *
8127 * On other platforms, this method returns @c true if the client process is
8128 * terminated and @c false if it's still alive.
8129 *
8130 * @note Locks this object for writing.
8131 */
8132bool Machine::i_checkForSpawnFailure()
8133{
8134 AutoCaller autoCaller(this);
8135 if (!autoCaller.isOk())
8136 {
8137 /* nothing to do */
8138 LogFlowThisFunc(("Already uninitialized!\n"));
8139 return true;
8140 }
8141
8142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8143
8144 if (mData->mSession.mState != SessionState_Spawning)
8145 {
8146 /* nothing to do */
8147 LogFlowThisFunc(("Not spawning any more!\n"));
8148 return true;
8149 }
8150
8151 HRESULT rc = S_OK;
8152
8153 /* PID not yet initialized, skip check. */
8154 if (mData->mSession.mPID == NIL_RTPROCESS)
8155 return false;
8156
8157 RTPROCSTATUS status;
8158 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8159
8160 if (vrc != VERR_PROCESS_RUNNING)
8161 {
8162 Utf8Str strExtraInfo;
8163
8164#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8165 /* If the startup logfile exists and is of non-zero length, tell the
8166 user to look there for more details to encourage them to attach it
8167 when reporting startup issues. */
8168 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8169 uint64_t cbStartupLogFile = 0;
8170 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8171 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8172 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8173#endif
8174
8175 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8176 rc = setError(E_FAIL,
8177 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8178 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8179 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8180 rc = setError(E_FAIL,
8181 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8182 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8183 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8184 rc = setError(E_FAIL,
8185 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8186 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8187 else
8188 rc = setErrorBoth(E_FAIL, vrc,
8189 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8190 i_getName().c_str(), vrc, strExtraInfo.c_str());
8191 }
8192
8193 if (FAILED(rc))
8194 {
8195 /* Close the remote session, remove the remote control from the list
8196 * and reset session state to Closed (@note keep the code in sync with
8197 * the relevant part in LockMachine()). */
8198
8199 Assert(mData->mSession.mRemoteControls.size() == 1);
8200 if (mData->mSession.mRemoteControls.size() == 1)
8201 {
8202 ErrorInfoKeeper eik;
8203 mData->mSession.mRemoteControls.front()->Uninitialize();
8204 }
8205
8206 mData->mSession.mRemoteControls.clear();
8207 mData->mSession.mState = SessionState_Unlocked;
8208
8209 /* finalize the progress after setting the state */
8210 if (!mData->mSession.mProgress.isNull())
8211 {
8212 mData->mSession.mProgress->notifyComplete(rc);
8213 mData->mSession.mProgress.setNull();
8214 }
8215
8216 mData->mSession.mPID = NIL_RTPROCESS;
8217
8218 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8219 return true;
8220 }
8221
8222 return false;
8223}
8224
8225/**
8226 * Checks whether the machine can be registered. If so, commits and saves
8227 * all settings.
8228 *
8229 * @note Must be called from mParent's write lock. Locks this object and
8230 * children for writing.
8231 */
8232HRESULT Machine::i_prepareRegister()
8233{
8234 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8235
8236 AutoLimitedCaller autoCaller(this);
8237 AssertComRCReturnRC(autoCaller.rc());
8238
8239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8240
8241 /* wait for state dependents to drop to zero */
8242 i_ensureNoStateDependencies();
8243
8244 if (!mData->mAccessible)
8245 return setError(VBOX_E_INVALID_OBJECT_STATE,
8246 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8247 mUserData->s.strName.c_str(),
8248 mData->mUuid.toString().c_str());
8249
8250 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8251
8252 if (mData->mRegistered)
8253 return setError(VBOX_E_INVALID_OBJECT_STATE,
8254 tr("The machine '%s' with UUID {%s} is already registered"),
8255 mUserData->s.strName.c_str(),
8256 mData->mUuid.toString().c_str());
8257
8258 HRESULT rc = S_OK;
8259
8260 // Ensure the settings are saved. If we are going to be registered and
8261 // no config file exists yet, create it by calling i_saveSettings() too.
8262 if ( (mData->flModifications)
8263 || (!mData->pMachineConfigFile->fileExists())
8264 )
8265 {
8266 rc = i_saveSettings(NULL);
8267 // no need to check whether VirtualBox.xml needs saving too since
8268 // we can't have a machine XML file rename pending
8269 if (FAILED(rc)) return rc;
8270 }
8271
8272 /* more config checking goes here */
8273
8274 if (SUCCEEDED(rc))
8275 {
8276 /* we may have had implicit modifications we want to fix on success */
8277 i_commit();
8278
8279 mData->mRegistered = true;
8280 }
8281 else
8282 {
8283 /* we may have had implicit modifications we want to cancel on failure*/
8284 i_rollback(false /* aNotify */);
8285 }
8286
8287 return rc;
8288}
8289
8290/**
8291 * Increases the number of objects dependent on the machine state or on the
8292 * registered state. Guarantees that these two states will not change at least
8293 * until #i_releaseStateDependency() is called.
8294 *
8295 * Depending on the @a aDepType value, additional state checks may be made.
8296 * These checks will set extended error info on failure. See
8297 * #i_checkStateDependency() for more info.
8298 *
8299 * If this method returns a failure, the dependency is not added and the caller
8300 * is not allowed to rely on any particular machine state or registration state
8301 * value and may return the failed result code to the upper level.
8302 *
8303 * @param aDepType Dependency type to add.
8304 * @param aState Current machine state (NULL if not interested).
8305 * @param aRegistered Current registered state (NULL if not interested).
8306 *
8307 * @note Locks this object for writing.
8308 */
8309HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8310 MachineState_T *aState /* = NULL */,
8311 BOOL *aRegistered /* = NULL */)
8312{
8313 AutoCaller autoCaller(this);
8314 AssertComRCReturnRC(autoCaller.rc());
8315
8316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8317
8318 HRESULT rc = i_checkStateDependency(aDepType);
8319 if (FAILED(rc)) return rc;
8320
8321 {
8322 if (mData->mMachineStateChangePending != 0)
8323 {
8324 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8325 * drop to zero so don't add more. It may make sense to wait a bit
8326 * and retry before reporting an error (since the pending state
8327 * transition should be really quick) but let's just assert for
8328 * now to see if it ever happens on practice. */
8329
8330 AssertFailed();
8331
8332 return setError(E_ACCESSDENIED,
8333 tr("Machine state change is in progress. Please retry the operation later."));
8334 }
8335
8336 ++mData->mMachineStateDeps;
8337 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8338 }
8339
8340 if (aState)
8341 *aState = mData->mMachineState;
8342 if (aRegistered)
8343 *aRegistered = mData->mRegistered;
8344
8345 return S_OK;
8346}
8347
8348/**
8349 * Decreases the number of objects dependent on the machine state.
8350 * Must always complete the #i_addStateDependency() call after the state
8351 * dependency is no more necessary.
8352 */
8353void Machine::i_releaseStateDependency()
8354{
8355 AutoCaller autoCaller(this);
8356 AssertComRCReturnVoid(autoCaller.rc());
8357
8358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8359
8360 /* releaseStateDependency() w/o addStateDependency()? */
8361 AssertReturnVoid(mData->mMachineStateDeps != 0);
8362 -- mData->mMachineStateDeps;
8363
8364 if (mData->mMachineStateDeps == 0)
8365 {
8366 /* inform i_ensureNoStateDependencies() that there are no more deps */
8367 if (mData->mMachineStateChangePending != 0)
8368 {
8369 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8370 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8371 }
8372 }
8373}
8374
8375Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8376{
8377 /* start with nothing found */
8378 Utf8Str strResult("");
8379
8380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8381
8382 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8383 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8384 // found:
8385 strResult = it->second; // source is a Utf8Str
8386
8387 return strResult;
8388}
8389
8390// protected methods
8391/////////////////////////////////////////////////////////////////////////////
8392
8393/**
8394 * Performs machine state checks based on the @a aDepType value. If a check
8395 * fails, this method will set extended error info, otherwise it will return
8396 * S_OK. It is supposed, that on failure, the caller will immediately return
8397 * the return value of this method to the upper level.
8398 *
8399 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8400 *
8401 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8402 * current state of this machine object allows to change settings of the
8403 * machine (i.e. the machine is not registered, or registered but not running
8404 * and not saved). It is useful to call this method from Machine setters
8405 * before performing any change.
8406 *
8407 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8408 * as for MutableStateDep except that if the machine is saved, S_OK is also
8409 * returned. This is useful in setters which allow changing machine
8410 * properties when it is in the saved state.
8411 *
8412 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8413 * if the current state of this machine object allows to change runtime
8414 * changeable settings of the machine (i.e. the machine is not registered, or
8415 * registered but either running or not running and not saved). It is useful
8416 * to call this method from Machine setters before performing any changes to
8417 * runtime changeable settings.
8418 *
8419 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8420 * the same as for MutableOrRunningStateDep except that if the machine is
8421 * saved, S_OK is also returned. This is useful in setters which allow
8422 * changing runtime and saved state changeable machine properties.
8423 *
8424 * @param aDepType Dependency type to check.
8425 *
8426 * @note Non Machine based classes should use #i_addStateDependency() and
8427 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8428 * template.
8429 *
8430 * @note This method must be called from under this object's read or write
8431 * lock.
8432 */
8433HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8434{
8435 switch (aDepType)
8436 {
8437 case AnyStateDep:
8438 {
8439 break;
8440 }
8441 case MutableStateDep:
8442 {
8443 if ( mData->mRegistered
8444 && ( !i_isSessionMachine()
8445 || ( mData->mMachineState != MachineState_Aborted
8446 && mData->mMachineState != MachineState_Teleported
8447 && mData->mMachineState != MachineState_PoweredOff
8448 )
8449 )
8450 )
8451 return setError(VBOX_E_INVALID_VM_STATE,
8452 tr("The machine is not mutable (state is %s)"),
8453 Global::stringifyMachineState(mData->mMachineState));
8454 break;
8455 }
8456 case MutableOrSavedStateDep:
8457 {
8458 if ( mData->mRegistered
8459 && ( !i_isSessionMachine()
8460 || ( mData->mMachineState != MachineState_Aborted
8461 && mData->mMachineState != MachineState_Teleported
8462 && mData->mMachineState != MachineState_Saved
8463 && mData->mMachineState != MachineState_PoweredOff
8464 )
8465 )
8466 )
8467 return setError(VBOX_E_INVALID_VM_STATE,
8468 tr("The machine is not mutable or saved (state is %s)"),
8469 Global::stringifyMachineState(mData->mMachineState));
8470 break;
8471 }
8472 case MutableOrRunningStateDep:
8473 {
8474 if ( mData->mRegistered
8475 && ( !i_isSessionMachine()
8476 || ( mData->mMachineState != MachineState_Aborted
8477 && mData->mMachineState != MachineState_Teleported
8478 && mData->mMachineState != MachineState_PoweredOff
8479 && !Global::IsOnline(mData->mMachineState)
8480 )
8481 )
8482 )
8483 return setError(VBOX_E_INVALID_VM_STATE,
8484 tr("The machine is not mutable or running (state is %s)"),
8485 Global::stringifyMachineState(mData->mMachineState));
8486 break;
8487 }
8488 case MutableOrSavedOrRunningStateDep:
8489 {
8490 if ( mData->mRegistered
8491 && ( !i_isSessionMachine()
8492 || ( mData->mMachineState != MachineState_Aborted
8493 && mData->mMachineState != MachineState_Teleported
8494 && mData->mMachineState != MachineState_Saved
8495 && mData->mMachineState != MachineState_PoweredOff
8496 && !Global::IsOnline(mData->mMachineState)
8497 )
8498 )
8499 )
8500 return setError(VBOX_E_INVALID_VM_STATE,
8501 tr("The machine is not mutable, saved or running (state is %s)"),
8502 Global::stringifyMachineState(mData->mMachineState));
8503 break;
8504 }
8505 }
8506
8507 return S_OK;
8508}
8509
8510/**
8511 * Helper to initialize all associated child objects and allocate data
8512 * structures.
8513 *
8514 * This method must be called as a part of the object's initialization procedure
8515 * (usually done in the #init() method).
8516 *
8517 * @note Must be called only from #init() or from #i_registeredInit().
8518 */
8519HRESULT Machine::initDataAndChildObjects()
8520{
8521 AutoCaller autoCaller(this);
8522 AssertComRCReturnRC(autoCaller.rc());
8523 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8524 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8525
8526 AssertReturn(!mData->mAccessible, E_FAIL);
8527
8528 /* allocate data structures */
8529 mSSData.allocate();
8530 mUserData.allocate();
8531 mHWData.allocate();
8532 mMediumAttachments.allocate();
8533 mStorageControllers.allocate();
8534 mUSBControllers.allocate();
8535
8536 /* initialize mOSTypeId */
8537 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8538
8539/** @todo r=bird: init() methods never fails, right? Why don't we make them
8540 * return void then! */
8541
8542 /* create associated BIOS settings object */
8543 unconst(mBIOSSettings).createObject();
8544 mBIOSSettings->init(this);
8545
8546 /* create an associated VRDE object (default is disabled) */
8547 unconst(mVRDEServer).createObject();
8548 mVRDEServer->init(this);
8549
8550 /* create associated serial port objects */
8551 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8552 {
8553 unconst(mSerialPorts[slot]).createObject();
8554 mSerialPorts[slot]->init(this, slot);
8555 }
8556
8557 /* create associated parallel port objects */
8558 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8559 {
8560 unconst(mParallelPorts[slot]).createObject();
8561 mParallelPorts[slot]->init(this, slot);
8562 }
8563
8564 /* create the audio adapter object (always present, default is disabled) */
8565 unconst(mAudioAdapter).createObject();
8566 mAudioAdapter->init(this);
8567
8568 /* create the USB device filters object (always present) */
8569 unconst(mUSBDeviceFilters).createObject();
8570 mUSBDeviceFilters->init(this);
8571
8572 /* create associated network adapter objects */
8573 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8574 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8575 {
8576 unconst(mNetworkAdapters[slot]).createObject();
8577 mNetworkAdapters[slot]->init(this, slot);
8578 }
8579
8580 /* create the bandwidth control */
8581 unconst(mBandwidthControl).createObject();
8582 mBandwidthControl->init(this);
8583
8584 return S_OK;
8585}
8586
8587/**
8588 * Helper to uninitialize all associated child objects and to free all data
8589 * structures.
8590 *
8591 * This method must be called as a part of the object's uninitialization
8592 * procedure (usually done in the #uninit() method).
8593 *
8594 * @note Must be called only from #uninit() or from #i_registeredInit().
8595 */
8596void Machine::uninitDataAndChildObjects()
8597{
8598 AutoCaller autoCaller(this);
8599 AssertComRCReturnVoid(autoCaller.rc());
8600 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8601 || getObjectState().getState() == ObjectState::Limited);
8602
8603 /* tell all our other child objects we've been uninitialized */
8604 if (mBandwidthControl)
8605 {
8606 mBandwidthControl->uninit();
8607 unconst(mBandwidthControl).setNull();
8608 }
8609
8610 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8611 {
8612 if (mNetworkAdapters[slot])
8613 {
8614 mNetworkAdapters[slot]->uninit();
8615 unconst(mNetworkAdapters[slot]).setNull();
8616 }
8617 }
8618
8619 if (mUSBDeviceFilters)
8620 {
8621 mUSBDeviceFilters->uninit();
8622 unconst(mUSBDeviceFilters).setNull();
8623 }
8624
8625 if (mAudioAdapter)
8626 {
8627 mAudioAdapter->uninit();
8628 unconst(mAudioAdapter).setNull();
8629 }
8630
8631 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8632 {
8633 if (mParallelPorts[slot])
8634 {
8635 mParallelPorts[slot]->uninit();
8636 unconst(mParallelPorts[slot]).setNull();
8637 }
8638 }
8639
8640 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8641 {
8642 if (mSerialPorts[slot])
8643 {
8644 mSerialPorts[slot]->uninit();
8645 unconst(mSerialPorts[slot]).setNull();
8646 }
8647 }
8648
8649 if (mVRDEServer)
8650 {
8651 mVRDEServer->uninit();
8652 unconst(mVRDEServer).setNull();
8653 }
8654
8655 if (mBIOSSettings)
8656 {
8657 mBIOSSettings->uninit();
8658 unconst(mBIOSSettings).setNull();
8659 }
8660
8661 /* Deassociate media (only when a real Machine or a SnapshotMachine
8662 * instance is uninitialized; SessionMachine instances refer to real
8663 * Machine media). This is necessary for a clean re-initialization of
8664 * the VM after successfully re-checking the accessibility state. Note
8665 * that in case of normal Machine or SnapshotMachine uninitialization (as
8666 * a result of unregistering or deleting the snapshot), outdated media
8667 * attachments will already be uninitialized and deleted, so this
8668 * code will not affect them. */
8669 if ( !mMediumAttachments.isNull()
8670 && !i_isSessionMachine()
8671 )
8672 {
8673 for (MediumAttachmentList::const_iterator
8674 it = mMediumAttachments->begin();
8675 it != mMediumAttachments->end();
8676 ++it)
8677 {
8678 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8679 if (pMedium.isNull())
8680 continue;
8681 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8682 AssertComRC(rc);
8683 }
8684 }
8685
8686 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8687 {
8688 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8689 if (mData->mFirstSnapshot)
8690 {
8691 // snapshots tree is protected by machine write lock; strictly
8692 // this isn't necessary here since we're deleting the entire
8693 // machine, but otherwise we assert in Snapshot::uninit()
8694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8695 mData->mFirstSnapshot->uninit();
8696 mData->mFirstSnapshot.setNull();
8697 }
8698
8699 mData->mCurrentSnapshot.setNull();
8700 }
8701
8702 /* free data structures (the essential mData structure is not freed here
8703 * since it may be still in use) */
8704 mMediumAttachments.free();
8705 mStorageControllers.free();
8706 mUSBControllers.free();
8707 mHWData.free();
8708 mUserData.free();
8709 mSSData.free();
8710}
8711
8712/**
8713 * Returns a pointer to the Machine object for this machine that acts like a
8714 * parent for complex machine data objects such as shared folders, etc.
8715 *
8716 * For primary Machine objects and for SnapshotMachine objects, returns this
8717 * object's pointer itself. For SessionMachine objects, returns the peer
8718 * (primary) machine pointer.
8719 */
8720Machine *Machine::i_getMachine()
8721{
8722 if (i_isSessionMachine())
8723 return (Machine*)mPeer;
8724 return this;
8725}
8726
8727/**
8728 * Makes sure that there are no machine state dependents. If necessary, waits
8729 * for the number of dependents to drop to zero.
8730 *
8731 * Make sure this method is called from under this object's write lock to
8732 * guarantee that no new dependents may be added when this method returns
8733 * control to the caller.
8734 *
8735 * @note Locks this object for writing. The lock will be released while waiting
8736 * (if necessary).
8737 *
8738 * @warning To be used only in methods that change the machine state!
8739 */
8740void Machine::i_ensureNoStateDependencies()
8741{
8742 AssertReturnVoid(isWriteLockOnCurrentThread());
8743
8744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8745
8746 /* Wait for all state dependents if necessary */
8747 if (mData->mMachineStateDeps != 0)
8748 {
8749 /* lazy semaphore creation */
8750 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8751 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8752
8753 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8754 mData->mMachineStateDeps));
8755
8756 ++mData->mMachineStateChangePending;
8757
8758 /* reset the semaphore before waiting, the last dependent will signal
8759 * it */
8760 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8761
8762 alock.release();
8763
8764 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8765
8766 alock.acquire();
8767
8768 -- mData->mMachineStateChangePending;
8769 }
8770}
8771
8772/**
8773 * Changes the machine state and informs callbacks.
8774 *
8775 * This method is not intended to fail so it either returns S_OK or asserts (and
8776 * returns a failure).
8777 *
8778 * @note Locks this object for writing.
8779 */
8780HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8781{
8782 LogFlowThisFuncEnter();
8783 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8784 Assert(aMachineState != MachineState_Null);
8785
8786 AutoCaller autoCaller(this);
8787 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8788
8789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8790
8791 /* wait for state dependents to drop to zero */
8792 i_ensureNoStateDependencies();
8793
8794 MachineState_T const enmOldState = mData->mMachineState;
8795 if (enmOldState != aMachineState)
8796 {
8797 mData->mMachineState = aMachineState;
8798 RTTimeNow(&mData->mLastStateChange);
8799
8800#ifdef VBOX_WITH_DTRACE_R3_MAIN
8801 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8802#endif
8803 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8804 }
8805
8806 LogFlowThisFuncLeave();
8807 return S_OK;
8808}
8809
8810/**
8811 * Searches for a shared folder with the given logical name
8812 * in the collection of shared folders.
8813 *
8814 * @param aName logical name of the shared folder
8815 * @param aSharedFolder where to return the found object
8816 * @param aSetError whether to set the error info if the folder is
8817 * not found
8818 * @return
8819 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8820 *
8821 * @note
8822 * must be called from under the object's lock!
8823 */
8824HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8825 ComObjPtr<SharedFolder> &aSharedFolder,
8826 bool aSetError /* = false */)
8827{
8828 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8829 for (HWData::SharedFolderList::const_iterator
8830 it = mHWData->mSharedFolders.begin();
8831 it != mHWData->mSharedFolders.end();
8832 ++it)
8833 {
8834 SharedFolder *pSF = *it;
8835 AutoCaller autoCaller(pSF);
8836 if (pSF->i_getName() == aName)
8837 {
8838 aSharedFolder = pSF;
8839 rc = S_OK;
8840 break;
8841 }
8842 }
8843
8844 if (aSetError && FAILED(rc))
8845 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8846
8847 return rc;
8848}
8849
8850/**
8851 * Initializes all machine instance data from the given settings structures
8852 * from XML. The exception is the machine UUID which needs special handling
8853 * depending on the caller's use case, so the caller needs to set that herself.
8854 *
8855 * This gets called in several contexts during machine initialization:
8856 *
8857 * -- When machine XML exists on disk already and needs to be loaded into memory,
8858 * for example, from #i_registeredInit() to load all registered machines on
8859 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8860 * attached to the machine should be part of some media registry already.
8861 *
8862 * -- During OVF import, when a machine config has been constructed from an
8863 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8864 * ensure that the media listed as attachments in the config (which have
8865 * been imported from the OVF) receive the correct registry ID.
8866 *
8867 * -- During VM cloning.
8868 *
8869 * @param config Machine settings from XML.
8870 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8871 * for each attached medium in the config.
8872 * @return
8873 */
8874HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8875 const Guid *puuidRegistry)
8876{
8877 // copy name, description, OS type, teleporter, UTC etc.
8878 mUserData->s = config.machineUserData;
8879
8880 // look up the object by Id to check it is valid
8881 ComObjPtr<GuestOSType> pGuestOSType;
8882 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8883 if (!pGuestOSType.isNull())
8884 mUserData->s.strOsType = pGuestOSType->i_id();
8885
8886 // stateFile (optional)
8887 if (config.strStateFile.isEmpty())
8888 mSSData->strStateFilePath.setNull();
8889 else
8890 {
8891 Utf8Str stateFilePathFull(config.strStateFile);
8892 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8893 if (RT_FAILURE(vrc))
8894 return setErrorBoth(E_FAIL, vrc,
8895 tr("Invalid saved state file path '%s' (%Rrc)"),
8896 config.strStateFile.c_str(),
8897 vrc);
8898 mSSData->strStateFilePath = stateFilePathFull;
8899 }
8900
8901 // snapshot folder needs special processing so set it again
8902 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8903 if (FAILED(rc)) return rc;
8904
8905 /* Copy the extra data items (config may or may not be the same as
8906 * mData->pMachineConfigFile) if necessary. When loading the XML files
8907 * from disk they are the same, but not for OVF import. */
8908 if (mData->pMachineConfigFile != &config)
8909 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8910
8911 /* currentStateModified (optional, default is true) */
8912 mData->mCurrentStateModified = config.fCurrentStateModified;
8913
8914 mData->mLastStateChange = config.timeLastStateChange;
8915
8916 /*
8917 * note: all mUserData members must be assigned prior this point because
8918 * we need to commit changes in order to let mUserData be shared by all
8919 * snapshot machine instances.
8920 */
8921 mUserData.commitCopy();
8922
8923 // machine registry, if present (must be loaded before snapshots)
8924 if (config.canHaveOwnMediaRegistry())
8925 {
8926 // determine machine folder
8927 Utf8Str strMachineFolder = i_getSettingsFileFull();
8928 strMachineFolder.stripFilename();
8929 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8930 config.mediaRegistry,
8931 strMachineFolder);
8932 if (FAILED(rc)) return rc;
8933 }
8934
8935 /* Snapshot node (optional) */
8936 size_t cRootSnapshots;
8937 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8938 {
8939 // there must be only one root snapshot
8940 Assert(cRootSnapshots == 1);
8941
8942 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8943
8944 rc = i_loadSnapshot(snap,
8945 config.uuidCurrentSnapshot,
8946 NULL); // no parent == first snapshot
8947 if (FAILED(rc)) return rc;
8948 }
8949
8950 // hardware data
8951 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8952 if (FAILED(rc)) return rc;
8953
8954 /*
8955 * NOTE: the assignment below must be the last thing to do,
8956 * otherwise it will be not possible to change the settings
8957 * somewhere in the code above because all setters will be
8958 * blocked by i_checkStateDependency(MutableStateDep).
8959 */
8960
8961 /* set the machine state to Aborted or Saved when appropriate */
8962 if (config.fAborted)
8963 {
8964 mSSData->strStateFilePath.setNull();
8965
8966 /* no need to use i_setMachineState() during init() */
8967 mData->mMachineState = MachineState_Aborted;
8968 }
8969 else if (!mSSData->strStateFilePath.isEmpty())
8970 {
8971 /* no need to use i_setMachineState() during init() */
8972 mData->mMachineState = MachineState_Saved;
8973 }
8974
8975 // after loading settings, we are no longer different from the XML on disk
8976 mData->flModifications = 0;
8977
8978 return S_OK;
8979}
8980
8981/**
8982 * Recursively loads all snapshots starting from the given.
8983 *
8984 * @param data snapshot settings.
8985 * @param aCurSnapshotId Current snapshot ID from the settings file.
8986 * @param aParentSnapshot Parent snapshot.
8987 */
8988HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8989 const Guid &aCurSnapshotId,
8990 Snapshot *aParentSnapshot)
8991{
8992 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8993 AssertReturn(!i_isSessionMachine(), E_FAIL);
8994
8995 HRESULT rc = S_OK;
8996
8997 Utf8Str strStateFile;
8998 if (!data.strStateFile.isEmpty())
8999 {
9000 /* optional */
9001 strStateFile = data.strStateFile;
9002 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9003 if (RT_FAILURE(vrc))
9004 return setErrorBoth(E_FAIL, vrc,
9005 tr("Invalid saved state file path '%s' (%Rrc)"),
9006 strStateFile.c_str(),
9007 vrc);
9008 }
9009
9010 /* create a snapshot machine object */
9011 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9012 pSnapshotMachine.createObject();
9013 rc = pSnapshotMachine->initFromSettings(this,
9014 data.hardware,
9015 &data.debugging,
9016 &data.autostart,
9017 data.uuid.ref(),
9018 strStateFile);
9019 if (FAILED(rc)) return rc;
9020
9021 /* create a snapshot object */
9022 ComObjPtr<Snapshot> pSnapshot;
9023 pSnapshot.createObject();
9024 /* initialize the snapshot */
9025 rc = pSnapshot->init(mParent, // VirtualBox object
9026 data.uuid,
9027 data.strName,
9028 data.strDescription,
9029 data.timestamp,
9030 pSnapshotMachine,
9031 aParentSnapshot);
9032 if (FAILED(rc)) return rc;
9033
9034 /* memorize the first snapshot if necessary */
9035 if (!mData->mFirstSnapshot)
9036 mData->mFirstSnapshot = pSnapshot;
9037
9038 /* memorize the current snapshot when appropriate */
9039 if ( !mData->mCurrentSnapshot
9040 && pSnapshot->i_getId() == aCurSnapshotId
9041 )
9042 mData->mCurrentSnapshot = pSnapshot;
9043
9044 // now create the children
9045 for (settings::SnapshotsList::const_iterator
9046 it = data.llChildSnapshots.begin();
9047 it != data.llChildSnapshots.end();
9048 ++it)
9049 {
9050 const settings::Snapshot &childData = *it;
9051 // recurse
9052 rc = i_loadSnapshot(childData,
9053 aCurSnapshotId,
9054 pSnapshot); // parent = the one we created above
9055 if (FAILED(rc)) return rc;
9056 }
9057
9058 return rc;
9059}
9060
9061/**
9062 * Loads settings into mHWData.
9063 *
9064 * @param puuidRegistry Registry ID.
9065 * @param puuidSnapshot Snapshot ID
9066 * @param data Reference to the hardware settings.
9067 * @param pDbg Pointer to the debugging settings.
9068 * @param pAutostart Pointer to the autostart settings.
9069 */
9070HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9071 const Guid *puuidSnapshot,
9072 const settings::Hardware &data,
9073 const settings::Debugging *pDbg,
9074 const settings::Autostart *pAutostart)
9075{
9076 AssertReturn(!i_isSessionMachine(), E_FAIL);
9077
9078 HRESULT rc = S_OK;
9079
9080 try
9081 {
9082 ComObjPtr<GuestOSType> pGuestOSType;
9083 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9084
9085 /* The hardware version attribute (optional). */
9086 mHWData->mHWVersion = data.strVersion;
9087 mHWData->mHardwareUUID = data.uuid;
9088
9089 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9090 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9091 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9092 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9093 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9094 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9095 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9096 mHWData->mPAEEnabled = data.fPAE;
9097 mHWData->mLongMode = data.enmLongMode;
9098 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9099 mHWData->mAPIC = data.fAPIC;
9100 mHWData->mX2APIC = data.fX2APIC;
9101 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9102 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9103 mHWData->mSpecCtrl = data.fSpecCtrl;
9104 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9105 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9106 mHWData->mCPUCount = data.cCPUs;
9107 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9108 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9109 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9110 mHWData->mCpuProfile = data.strCpuProfile;
9111
9112 // cpu
9113 if (mHWData->mCPUHotPlugEnabled)
9114 {
9115 for (settings::CpuList::const_iterator
9116 it = data.llCpus.begin();
9117 it != data.llCpus.end();
9118 ++it)
9119 {
9120 const settings::Cpu &cpu = *it;
9121
9122 mHWData->mCPUAttached[cpu.ulId] = true;
9123 }
9124 }
9125
9126 // cpuid leafs
9127 for (settings::CpuIdLeafsList::const_iterator
9128 it = data.llCpuIdLeafs.begin();
9129 it != data.llCpuIdLeafs.end();
9130 ++it)
9131 {
9132 const settings::CpuIdLeaf &rLeaf= *it;
9133 if ( rLeaf.idx < UINT32_C(0x20)
9134 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9135 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9136 mHWData->mCpuIdLeafList.push_back(rLeaf);
9137 /* else: just ignore */
9138 }
9139
9140 mHWData->mMemorySize = data.ulMemorySizeMB;
9141 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9142
9143 // boot order
9144 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9145 {
9146 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9147 if (it == data.mapBootOrder.end())
9148 mHWData->mBootOrder[i] = DeviceType_Null;
9149 else
9150 mHWData->mBootOrder[i] = it->second;
9151 }
9152
9153 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9154 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9155 mHWData->mMonitorCount = data.cMonitors;
9156 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9157 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9158 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9159 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9160 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9161 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9162 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9163 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9164 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9165 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9166 if (!data.strVideoCaptureFile.isEmpty())
9167 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9168 else
9169 mHWData->mVideoCaptureFile.setNull();
9170 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9171 mHWData->mFirmwareType = data.firmwareType;
9172 mHWData->mPointingHIDType = data.pointingHIDType;
9173 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9174 mHWData->mChipsetType = data.chipsetType;
9175 mHWData->mParavirtProvider = data.paravirtProvider;
9176 mHWData->mParavirtDebug = data.strParavirtDebug;
9177 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9178 mHWData->mHPETEnabled = data.fHPETEnabled;
9179
9180 /* VRDEServer */
9181 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9182 if (FAILED(rc)) return rc;
9183
9184 /* BIOS */
9185 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9186 if (FAILED(rc)) return rc;
9187
9188 // Bandwidth control (must come before network adapters)
9189 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9190 if (FAILED(rc)) return rc;
9191
9192 /* Shared folders */
9193 for (settings::USBControllerList::const_iterator
9194 it = data.usbSettings.llUSBControllers.begin();
9195 it != data.usbSettings.llUSBControllers.end();
9196 ++it)
9197 {
9198 const settings::USBController &settingsCtrl = *it;
9199 ComObjPtr<USBController> newCtrl;
9200
9201 newCtrl.createObject();
9202 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9203 mUSBControllers->push_back(newCtrl);
9204 }
9205
9206 /* USB device filters */
9207 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9208 if (FAILED(rc)) return rc;
9209
9210 // network adapters (establish array size first and apply defaults, to
9211 // ensure reading the same settings as we saved, since the list skips
9212 // adapters having defaults)
9213 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9214 size_t oldCount = mNetworkAdapters.size();
9215 if (newCount > oldCount)
9216 {
9217 mNetworkAdapters.resize(newCount);
9218 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9219 {
9220 unconst(mNetworkAdapters[slot]).createObject();
9221 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9222 }
9223 }
9224 else if (newCount < oldCount)
9225 mNetworkAdapters.resize(newCount);
9226 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9227 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9228 for (settings::NetworkAdaptersList::const_iterator
9229 it = data.llNetworkAdapters.begin();
9230 it != data.llNetworkAdapters.end();
9231 ++it)
9232 {
9233 const settings::NetworkAdapter &nic = *it;
9234
9235 /* slot uniqueness is guaranteed by XML Schema */
9236 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9237 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9238 if (FAILED(rc)) return rc;
9239 }
9240
9241 // serial ports (establish defaults first, to ensure reading the same
9242 // settings as we saved, since the list skips ports having defaults)
9243 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9244 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9245 for (settings::SerialPortsList::const_iterator
9246 it = data.llSerialPorts.begin();
9247 it != data.llSerialPorts.end();
9248 ++it)
9249 {
9250 const settings::SerialPort &s = *it;
9251
9252 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9253 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9254 if (FAILED(rc)) return rc;
9255 }
9256
9257 // parallel ports (establish defaults first, to ensure reading the same
9258 // settings as we saved, since the list skips ports having defaults)
9259 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9260 mParallelPorts[i]->i_applyDefaults();
9261 for (settings::ParallelPortsList::const_iterator
9262 it = data.llParallelPorts.begin();
9263 it != data.llParallelPorts.end();
9264 ++it)
9265 {
9266 const settings::ParallelPort &p = *it;
9267
9268 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9269 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9270 if (FAILED(rc)) return rc;
9271 }
9272
9273 /* AudioAdapter */
9274 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9275 if (FAILED(rc)) return rc;
9276
9277 /* storage controllers */
9278 rc = i_loadStorageControllers(data.storage,
9279 puuidRegistry,
9280 puuidSnapshot);
9281 if (FAILED(rc)) return rc;
9282
9283 /* Shared folders */
9284 for (settings::SharedFoldersList::const_iterator
9285 it = data.llSharedFolders.begin();
9286 it != data.llSharedFolders.end();
9287 ++it)
9288 {
9289 const settings::SharedFolder &sf = *it;
9290
9291 ComObjPtr<SharedFolder> sharedFolder;
9292 /* Check for double entries. Not allowed! */
9293 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9294 if (SUCCEEDED(rc))
9295 return setError(VBOX_E_OBJECT_IN_USE,
9296 tr("Shared folder named '%s' already exists"),
9297 sf.strName.c_str());
9298
9299 /* Create the new shared folder. Don't break on error. This will be
9300 * reported when the machine starts. */
9301 sharedFolder.createObject();
9302 rc = sharedFolder->init(i_getMachine(),
9303 sf.strName,
9304 sf.strHostPath,
9305 RT_BOOL(sf.fWritable),
9306 RT_BOOL(sf.fAutoMount),
9307 false /* fFailOnError */);
9308 if (FAILED(rc)) return rc;
9309 mHWData->mSharedFolders.push_back(sharedFolder);
9310 }
9311
9312 // Clipboard
9313 mHWData->mClipboardMode = data.clipboardMode;
9314
9315 // drag'n'drop
9316 mHWData->mDnDMode = data.dndMode;
9317
9318 // guest settings
9319 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9320
9321 // IO settings
9322 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9323 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9324
9325 // Host PCI devices
9326 for (settings::HostPCIDeviceAttachmentList::const_iterator
9327 it = data.pciAttachments.begin();
9328 it != data.pciAttachments.end();
9329 ++it)
9330 {
9331 const settings::HostPCIDeviceAttachment &hpda = *it;
9332 ComObjPtr<PCIDeviceAttachment> pda;
9333
9334 pda.createObject();
9335 pda->i_loadSettings(this, hpda);
9336 mHWData->mPCIDeviceAssignments.push_back(pda);
9337 }
9338
9339 /*
9340 * (The following isn't really real hardware, but it lives in HWData
9341 * for reasons of convenience.)
9342 */
9343
9344#ifdef VBOX_WITH_GUEST_PROPS
9345 /* Guest properties (optional) */
9346
9347 /* Only load transient guest properties for configs which have saved
9348 * state, because there shouldn't be any for powered off VMs. The same
9349 * logic applies for snapshots, as offline snapshots shouldn't have
9350 * any such properties. They confuse the code in various places.
9351 * Note: can't rely on the machine state, as it isn't set yet. */
9352 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9353 /* apologies for the hacky unconst() usage, but this needs hacking
9354 * actually inconsistent settings into consistency, otherwise there
9355 * will be some corner cases where the inconsistency survives
9356 * surprisingly long without getting fixed, especially for snapshots
9357 * as there are no config changes. */
9358 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9359 for (settings::GuestPropertiesList::iterator
9360 it = llGuestProperties.begin();
9361 it != llGuestProperties.end();
9362 /*nothing*/)
9363 {
9364 const settings::GuestProperty &prop = *it;
9365 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9366 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9367 if ( fSkipTransientGuestProperties
9368 && ( fFlags & GUEST_PROP_F_TRANSIENT
9369 || fFlags & GUEST_PROP_F_TRANSRESET))
9370 {
9371 it = llGuestProperties.erase(it);
9372 continue;
9373 }
9374 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9375 mHWData->mGuestProperties[prop.strName] = property;
9376 ++it;
9377 }
9378#endif /* VBOX_WITH_GUEST_PROPS defined */
9379
9380 rc = i_loadDebugging(pDbg);
9381 if (FAILED(rc))
9382 return rc;
9383
9384 mHWData->mAutostart = *pAutostart;
9385
9386 /* default frontend */
9387 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9388 }
9389 catch (std::bad_alloc &)
9390 {
9391 return E_OUTOFMEMORY;
9392 }
9393
9394 AssertComRC(rc);
9395 return rc;
9396}
9397
9398/**
9399 * Called from i_loadHardware() to load the debugging settings of the
9400 * machine.
9401 *
9402 * @param pDbg Pointer to the settings.
9403 */
9404HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9405{
9406 mHWData->mDebugging = *pDbg;
9407 /* no more processing currently required, this will probably change. */
9408 return S_OK;
9409}
9410
9411/**
9412 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9413 *
9414 * @param data storage settings.
9415 * @param puuidRegistry media registry ID to set media to or NULL;
9416 * see Machine::i_loadMachineDataFromSettings()
9417 * @param puuidSnapshot snapshot ID
9418 * @return
9419 */
9420HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9421 const Guid *puuidRegistry,
9422 const Guid *puuidSnapshot)
9423{
9424 AssertReturn(!i_isSessionMachine(), E_FAIL);
9425
9426 HRESULT rc = S_OK;
9427
9428 for (settings::StorageControllersList::const_iterator
9429 it = data.llStorageControllers.begin();
9430 it != data.llStorageControllers.end();
9431 ++it)
9432 {
9433 const settings::StorageController &ctlData = *it;
9434
9435 ComObjPtr<StorageController> pCtl;
9436 /* Try to find one with the name first. */
9437 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9438 if (SUCCEEDED(rc))
9439 return setError(VBOX_E_OBJECT_IN_USE,
9440 tr("Storage controller named '%s' already exists"),
9441 ctlData.strName.c_str());
9442
9443 pCtl.createObject();
9444 rc = pCtl->init(this,
9445 ctlData.strName,
9446 ctlData.storageBus,
9447 ctlData.ulInstance,
9448 ctlData.fBootable);
9449 if (FAILED(rc)) return rc;
9450
9451 mStorageControllers->push_back(pCtl);
9452
9453 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9454 if (FAILED(rc)) return rc;
9455
9456 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9457 if (FAILED(rc)) return rc;
9458
9459 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9460 if (FAILED(rc)) return rc;
9461
9462 /* Load the attached devices now. */
9463 rc = i_loadStorageDevices(pCtl,
9464 ctlData,
9465 puuidRegistry,
9466 puuidSnapshot);
9467 if (FAILED(rc)) return rc;
9468 }
9469
9470 return S_OK;
9471}
9472
9473/**
9474 * Called from i_loadStorageControllers for a controller's devices.
9475 *
9476 * @param aStorageController
9477 * @param data
9478 * @param puuidRegistry media registry ID to set media to or NULL; see
9479 * Machine::i_loadMachineDataFromSettings()
9480 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9481 * @return
9482 */
9483HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9484 const settings::StorageController &data,
9485 const Guid *puuidRegistry,
9486 const Guid *puuidSnapshot)
9487{
9488 HRESULT rc = S_OK;
9489
9490 /* paranoia: detect duplicate attachments */
9491 for (settings::AttachedDevicesList::const_iterator
9492 it = data.llAttachedDevices.begin();
9493 it != data.llAttachedDevices.end();
9494 ++it)
9495 {
9496 const settings::AttachedDevice &ad = *it;
9497
9498 for (settings::AttachedDevicesList::const_iterator it2 = it;
9499 it2 != data.llAttachedDevices.end();
9500 ++it2)
9501 {
9502 if (it == it2)
9503 continue;
9504
9505 const settings::AttachedDevice &ad2 = *it2;
9506
9507 if ( ad.lPort == ad2.lPort
9508 && ad.lDevice == ad2.lDevice)
9509 {
9510 return setError(E_FAIL,
9511 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9512 aStorageController->i_getName().c_str(),
9513 ad.lPort,
9514 ad.lDevice,
9515 mUserData->s.strName.c_str());
9516 }
9517 }
9518 }
9519
9520 for (settings::AttachedDevicesList::const_iterator
9521 it = data.llAttachedDevices.begin();
9522 it != data.llAttachedDevices.end();
9523 ++it)
9524 {
9525 const settings::AttachedDevice &dev = *it;
9526 ComObjPtr<Medium> medium;
9527
9528 switch (dev.deviceType)
9529 {
9530 case DeviceType_Floppy:
9531 case DeviceType_DVD:
9532 if (dev.strHostDriveSrc.isNotEmpty())
9533 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9534 false /* fRefresh */, medium);
9535 else
9536 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9537 dev.uuid,
9538 false /* fRefresh */,
9539 false /* aSetError */,
9540 medium);
9541 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9542 // This is not an error. The host drive or UUID might have vanished, so just go
9543 // ahead without this removeable medium attachment
9544 rc = S_OK;
9545 break;
9546
9547 case DeviceType_HardDisk:
9548 {
9549 /* find a hard disk by UUID */
9550 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9551 if (FAILED(rc))
9552 {
9553 if (i_isSnapshotMachine())
9554 {
9555 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9556 // so the user knows that the bad disk is in a snapshot somewhere
9557 com::ErrorInfo info;
9558 return setError(E_FAIL,
9559 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9560 puuidSnapshot->raw(),
9561 info.getText().raw());
9562 }
9563 else
9564 return rc;
9565 }
9566
9567 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9568
9569 if (medium->i_getType() == MediumType_Immutable)
9570 {
9571 if (i_isSnapshotMachine())
9572 return setError(E_FAIL,
9573 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9574 "of the virtual machine '%s' ('%s')"),
9575 medium->i_getLocationFull().c_str(),
9576 dev.uuid.raw(),
9577 puuidSnapshot->raw(),
9578 mUserData->s.strName.c_str(),
9579 mData->m_strConfigFileFull.c_str());
9580
9581 return setError(E_FAIL,
9582 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9583 medium->i_getLocationFull().c_str(),
9584 dev.uuid.raw(),
9585 mUserData->s.strName.c_str(),
9586 mData->m_strConfigFileFull.c_str());
9587 }
9588
9589 if (medium->i_getType() == MediumType_MultiAttach)
9590 {
9591 if (i_isSnapshotMachine())
9592 return setError(E_FAIL,
9593 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9594 "of the virtual machine '%s' ('%s')"),
9595 medium->i_getLocationFull().c_str(),
9596 dev.uuid.raw(),
9597 puuidSnapshot->raw(),
9598 mUserData->s.strName.c_str(),
9599 mData->m_strConfigFileFull.c_str());
9600
9601 return setError(E_FAIL,
9602 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9603 medium->i_getLocationFull().c_str(),
9604 dev.uuid.raw(),
9605 mUserData->s.strName.c_str(),
9606 mData->m_strConfigFileFull.c_str());
9607 }
9608
9609 if ( !i_isSnapshotMachine()
9610 && medium->i_getChildren().size() != 0
9611 )
9612 return setError(E_FAIL,
9613 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9614 "because it has %d differencing child hard disks"),
9615 medium->i_getLocationFull().c_str(),
9616 dev.uuid.raw(),
9617 mUserData->s.strName.c_str(),
9618 mData->m_strConfigFileFull.c_str(),
9619 medium->i_getChildren().size());
9620
9621 if (i_findAttachment(*mMediumAttachments.data(),
9622 medium))
9623 return setError(E_FAIL,
9624 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9625 medium->i_getLocationFull().c_str(),
9626 dev.uuid.raw(),
9627 mUserData->s.strName.c_str(),
9628 mData->m_strConfigFileFull.c_str());
9629
9630 break;
9631 }
9632
9633 default:
9634 return setError(E_FAIL,
9635 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9636 medium->i_getLocationFull().c_str(),
9637 mUserData->s.strName.c_str(),
9638 mData->m_strConfigFileFull.c_str());
9639 }
9640
9641 if (FAILED(rc))
9642 break;
9643
9644 /* Bandwidth groups are loaded at this point. */
9645 ComObjPtr<BandwidthGroup> pBwGroup;
9646
9647 if (!dev.strBwGroup.isEmpty())
9648 {
9649 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9650 if (FAILED(rc))
9651 return setError(E_FAIL,
9652 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9653 medium->i_getLocationFull().c_str(),
9654 dev.strBwGroup.c_str(),
9655 mUserData->s.strName.c_str(),
9656 mData->m_strConfigFileFull.c_str());
9657 pBwGroup->i_reference();
9658 }
9659
9660 const Utf8Str controllerName = aStorageController->i_getName();
9661 ComObjPtr<MediumAttachment> pAttachment;
9662 pAttachment.createObject();
9663 rc = pAttachment->init(this,
9664 medium,
9665 controllerName,
9666 dev.lPort,
9667 dev.lDevice,
9668 dev.deviceType,
9669 false,
9670 dev.fPassThrough,
9671 dev.fTempEject,
9672 dev.fNonRotational,
9673 dev.fDiscard,
9674 dev.fHotPluggable,
9675 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9676 if (FAILED(rc)) break;
9677
9678 /* associate the medium with this machine and snapshot */
9679 if (!medium.isNull())
9680 {
9681 AutoCaller medCaller(medium);
9682 if (FAILED(medCaller.rc())) return medCaller.rc();
9683 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9684
9685 if (i_isSnapshotMachine())
9686 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9687 else
9688 rc = medium->i_addBackReference(mData->mUuid);
9689 /* If the medium->addBackReference fails it sets an appropriate
9690 * error message, so no need to do any guesswork here. */
9691
9692 if (puuidRegistry)
9693 // caller wants registry ID to be set on all attached media (OVF import case)
9694 medium->i_addRegistry(*puuidRegistry);
9695 }
9696
9697 if (FAILED(rc))
9698 break;
9699
9700 /* back up mMediumAttachments to let registeredInit() properly rollback
9701 * on failure (= limited accessibility) */
9702 i_setModified(IsModified_Storage);
9703 mMediumAttachments.backup();
9704 mMediumAttachments->push_back(pAttachment);
9705 }
9706
9707 return rc;
9708}
9709
9710/**
9711 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9712 *
9713 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9714 * @param aSnapshot where to return the found snapshot
9715 * @param aSetError true to set extended error info on failure
9716 */
9717HRESULT Machine::i_findSnapshotById(const Guid &aId,
9718 ComObjPtr<Snapshot> &aSnapshot,
9719 bool aSetError /* = false */)
9720{
9721 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9722
9723 if (!mData->mFirstSnapshot)
9724 {
9725 if (aSetError)
9726 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9727 return E_FAIL;
9728 }
9729
9730 if (aId.isZero())
9731 aSnapshot = mData->mFirstSnapshot;
9732 else
9733 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9734
9735 if (!aSnapshot)
9736 {
9737 if (aSetError)
9738 return setError(E_FAIL,
9739 tr("Could not find a snapshot with UUID {%s}"),
9740 aId.toString().c_str());
9741 return E_FAIL;
9742 }
9743
9744 return S_OK;
9745}
9746
9747/**
9748 * Returns the snapshot with the given name or fails of no such snapshot.
9749 *
9750 * @param strName snapshot name to find
9751 * @param aSnapshot where to return the found snapshot
9752 * @param aSetError true to set extended error info on failure
9753 */
9754HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9755 ComObjPtr<Snapshot> &aSnapshot,
9756 bool aSetError /* = false */)
9757{
9758 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9759
9760 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9761
9762 if (!mData->mFirstSnapshot)
9763 {
9764 if (aSetError)
9765 return setError(VBOX_E_OBJECT_NOT_FOUND,
9766 tr("This machine does not have any snapshots"));
9767 return VBOX_E_OBJECT_NOT_FOUND;
9768 }
9769
9770 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9771
9772 if (!aSnapshot)
9773 {
9774 if (aSetError)
9775 return setError(VBOX_E_OBJECT_NOT_FOUND,
9776 tr("Could not find a snapshot named '%s'"), strName.c_str());
9777 return VBOX_E_OBJECT_NOT_FOUND;
9778 }
9779
9780 return S_OK;
9781}
9782
9783/**
9784 * Returns a storage controller object with the given name.
9785 *
9786 * @param aName storage controller name to find
9787 * @param aStorageController where to return the found storage controller
9788 * @param aSetError true to set extended error info on failure
9789 */
9790HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9791 ComObjPtr<StorageController> &aStorageController,
9792 bool aSetError /* = false */)
9793{
9794 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9795
9796 for (StorageControllerList::const_iterator
9797 it = mStorageControllers->begin();
9798 it != mStorageControllers->end();
9799 ++it)
9800 {
9801 if ((*it)->i_getName() == aName)
9802 {
9803 aStorageController = (*it);
9804 return S_OK;
9805 }
9806 }
9807
9808 if (aSetError)
9809 return setError(VBOX_E_OBJECT_NOT_FOUND,
9810 tr("Could not find a storage controller named '%s'"),
9811 aName.c_str());
9812 return VBOX_E_OBJECT_NOT_FOUND;
9813}
9814
9815/**
9816 * Returns a USB controller object with the given name.
9817 *
9818 * @param aName USB controller name to find
9819 * @param aUSBController where to return the found USB controller
9820 * @param aSetError true to set extended error info on failure
9821 */
9822HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9823 ComObjPtr<USBController> &aUSBController,
9824 bool aSetError /* = false */)
9825{
9826 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9827
9828 for (USBControllerList::const_iterator
9829 it = mUSBControllers->begin();
9830 it != mUSBControllers->end();
9831 ++it)
9832 {
9833 if ((*it)->i_getName() == aName)
9834 {
9835 aUSBController = (*it);
9836 return S_OK;
9837 }
9838 }
9839
9840 if (aSetError)
9841 return setError(VBOX_E_OBJECT_NOT_FOUND,
9842 tr("Could not find a storage controller named '%s'"),
9843 aName.c_str());
9844 return VBOX_E_OBJECT_NOT_FOUND;
9845}
9846
9847/**
9848 * Returns the number of USB controller instance of the given type.
9849 *
9850 * @param enmType USB controller type.
9851 */
9852ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9853{
9854 ULONG cCtrls = 0;
9855
9856 for (USBControllerList::const_iterator
9857 it = mUSBControllers->begin();
9858 it != mUSBControllers->end();
9859 ++it)
9860 {
9861 if ((*it)->i_getControllerType() == enmType)
9862 cCtrls++;
9863 }
9864
9865 return cCtrls;
9866}
9867
9868HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9869 MediumAttachmentList &atts)
9870{
9871 AutoCaller autoCaller(this);
9872 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9873
9874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9875
9876 for (MediumAttachmentList::const_iterator
9877 it = mMediumAttachments->begin();
9878 it != mMediumAttachments->end();
9879 ++it)
9880 {
9881 const ComObjPtr<MediumAttachment> &pAtt = *it;
9882 // should never happen, but deal with NULL pointers in the list.
9883 AssertContinue(!pAtt.isNull());
9884
9885 // getControllerName() needs caller+read lock
9886 AutoCaller autoAttCaller(pAtt);
9887 if (FAILED(autoAttCaller.rc()))
9888 {
9889 atts.clear();
9890 return autoAttCaller.rc();
9891 }
9892 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9893
9894 if (pAtt->i_getControllerName() == aName)
9895 atts.push_back(pAtt);
9896 }
9897
9898 return S_OK;
9899}
9900
9901
9902/**
9903 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9904 * file if the machine name was changed and about creating a new settings file
9905 * if this is a new machine.
9906 *
9907 * @note Must be never called directly but only from #saveSettings().
9908 */
9909HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9910{
9911 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9912
9913 HRESULT rc = S_OK;
9914
9915 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9916
9917 /// @todo need to handle primary group change, too
9918
9919 /* attempt to rename the settings file if machine name is changed */
9920 if ( mUserData->s.fNameSync
9921 && mUserData.isBackedUp()
9922 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9923 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9924 )
9925 {
9926 bool dirRenamed = false;
9927 bool fileRenamed = false;
9928
9929 Utf8Str configFile, newConfigFile;
9930 Utf8Str configFilePrev, newConfigFilePrev;
9931 Utf8Str configDir, newConfigDir;
9932
9933 do
9934 {
9935 int vrc = VINF_SUCCESS;
9936
9937 Utf8Str name = mUserData.backedUpData()->s.strName;
9938 Utf8Str newName = mUserData->s.strName;
9939 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9940 if (group == "/")
9941 group.setNull();
9942 Utf8Str newGroup = mUserData->s.llGroups.front();
9943 if (newGroup == "/")
9944 newGroup.setNull();
9945
9946 configFile = mData->m_strConfigFileFull;
9947
9948 /* first, rename the directory if it matches the group and machine name */
9949 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9950 group.c_str(), RTPATH_DELIMITER, name.c_str());
9951 /** @todo hack, make somehow use of ComposeMachineFilename */
9952 if (mUserData->s.fDirectoryIncludesUUID)
9953 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9954 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9955 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9956 /** @todo hack, make somehow use of ComposeMachineFilename */
9957 if (mUserData->s.fDirectoryIncludesUUID)
9958 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9959 configDir = configFile;
9960 configDir.stripFilename();
9961 newConfigDir = configDir;
9962 if ( configDir.length() >= groupPlusName.length()
9963 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9964 groupPlusName.c_str()))
9965 {
9966 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9967 Utf8Str newConfigBaseDir(newConfigDir);
9968 newConfigDir.append(newGroupPlusName);
9969 /* consistency: use \ if appropriate on the platform */
9970 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9971 /* new dir and old dir cannot be equal here because of 'if'
9972 * above and because name != newName */
9973 Assert(configDir != newConfigDir);
9974 if (!fSettingsFileIsNew)
9975 {
9976 /* perform real rename only if the machine is not new */
9977 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9978 if ( vrc == VERR_FILE_NOT_FOUND
9979 || vrc == VERR_PATH_NOT_FOUND)
9980 {
9981 /* create the parent directory, then retry renaming */
9982 Utf8Str parent(newConfigDir);
9983 parent.stripFilename();
9984 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9985 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9986 }
9987 if (RT_FAILURE(vrc))
9988 {
9989 rc = setErrorBoth(E_FAIL, vrc,
9990 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9991 configDir.c_str(),
9992 newConfigDir.c_str(),
9993 vrc);
9994 break;
9995 }
9996 /* delete subdirectories which are no longer needed */
9997 Utf8Str dir(configDir);
9998 dir.stripFilename();
9999 while (dir != newConfigBaseDir && dir != ".")
10000 {
10001 vrc = RTDirRemove(dir.c_str());
10002 if (RT_FAILURE(vrc))
10003 break;
10004 dir.stripFilename();
10005 }
10006 dirRenamed = true;
10007 }
10008 }
10009
10010 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10011 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10012
10013 /* then try to rename the settings file itself */
10014 if (newConfigFile != configFile)
10015 {
10016 /* get the path to old settings file in renamed directory */
10017 configFile = Utf8StrFmt("%s%c%s",
10018 newConfigDir.c_str(),
10019 RTPATH_DELIMITER,
10020 RTPathFilename(configFile.c_str()));
10021 if (!fSettingsFileIsNew)
10022 {
10023 /* perform real rename only if the machine is not new */
10024 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10025 if (RT_FAILURE(vrc))
10026 {
10027 rc = setErrorBoth(E_FAIL, vrc,
10028 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10029 configFile.c_str(),
10030 newConfigFile.c_str(),
10031 vrc);
10032 break;
10033 }
10034 fileRenamed = true;
10035 configFilePrev = configFile;
10036 configFilePrev += "-prev";
10037 newConfigFilePrev = newConfigFile;
10038 newConfigFilePrev += "-prev";
10039 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10040 }
10041 }
10042
10043 // update m_strConfigFileFull amd mConfigFile
10044 mData->m_strConfigFileFull = newConfigFile;
10045 // compute the relative path too
10046 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10047
10048 // store the old and new so that VirtualBox::i_saveSettings() can update
10049 // the media registry
10050 if ( mData->mRegistered
10051 && (configDir != newConfigDir || configFile != newConfigFile))
10052 {
10053 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10054
10055 if (pfNeedsGlobalSaveSettings)
10056 *pfNeedsGlobalSaveSettings = true;
10057 }
10058
10059 // in the saved state file path, replace the old directory with the new directory
10060 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10061 {
10062 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10063 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10064 }
10065
10066 // and do the same thing for the saved state file paths of all the online snapshots
10067 if (mData->mFirstSnapshot)
10068 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10069 newConfigDir.c_str());
10070 }
10071 while (0);
10072
10073 if (FAILED(rc))
10074 {
10075 /* silently try to rename everything back */
10076 if (fileRenamed)
10077 {
10078 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10079 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10080 }
10081 if (dirRenamed)
10082 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10083 }
10084
10085 if (FAILED(rc)) return rc;
10086 }
10087
10088 if (fSettingsFileIsNew)
10089 {
10090 /* create a virgin config file */
10091 int vrc = VINF_SUCCESS;
10092
10093 /* ensure the settings directory exists */
10094 Utf8Str path(mData->m_strConfigFileFull);
10095 path.stripFilename();
10096 if (!RTDirExists(path.c_str()))
10097 {
10098 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10099 if (RT_FAILURE(vrc))
10100 {
10101 return setErrorBoth(E_FAIL, vrc,
10102 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10103 path.c_str(),
10104 vrc);
10105 }
10106 }
10107
10108 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10109 path = Utf8Str(mData->m_strConfigFileFull);
10110 RTFILE f = NIL_RTFILE;
10111 vrc = RTFileOpen(&f, path.c_str(),
10112 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10113 if (RT_FAILURE(vrc))
10114 return setErrorBoth(E_FAIL, vrc,
10115 tr("Could not create the settings file '%s' (%Rrc)"),
10116 path.c_str(),
10117 vrc);
10118 RTFileClose(f);
10119 }
10120
10121 return rc;
10122}
10123
10124/**
10125 * Saves and commits machine data, user data and hardware data.
10126 *
10127 * Note that on failure, the data remains uncommitted.
10128 *
10129 * @a aFlags may combine the following flags:
10130 *
10131 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10132 * Used when saving settings after an operation that makes them 100%
10133 * correspond to the settings from the current snapshot.
10134 * - SaveS_Force: settings will be saved without doing a deep compare of the
10135 * settings structures. This is used when this is called because snapshots
10136 * have changed to avoid the overhead of the deep compare.
10137 *
10138 * @note Must be called from under this object's write lock. Locks children for
10139 * writing.
10140 *
10141 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10142 * initialized to false and that will be set to true by this function if
10143 * the caller must invoke VirtualBox::i_saveSettings() because the global
10144 * settings have changed. This will happen if a machine rename has been
10145 * saved and the global machine and media registries will therefore need
10146 * updating.
10147 * @param aFlags Flags.
10148 */
10149HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10150 int aFlags /*= 0*/)
10151{
10152 LogFlowThisFuncEnter();
10153
10154 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10155
10156 /* make sure child objects are unable to modify the settings while we are
10157 * saving them */
10158 i_ensureNoStateDependencies();
10159
10160 AssertReturn(!i_isSnapshotMachine(),
10161 E_FAIL);
10162
10163 HRESULT rc = S_OK;
10164 bool fNeedsWrite = false;
10165
10166 /* First, prepare to save settings. It will care about renaming the
10167 * settings directory and file if the machine name was changed and about
10168 * creating a new settings file if this is a new machine. */
10169 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10170 if (FAILED(rc)) return rc;
10171
10172 // keep a pointer to the current settings structures
10173 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10174 settings::MachineConfigFile *pNewConfig = NULL;
10175
10176 try
10177 {
10178 // make a fresh one to have everyone write stuff into
10179 pNewConfig = new settings::MachineConfigFile(NULL);
10180 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10181
10182 // now go and copy all the settings data from COM to the settings structures
10183 // (this calls i_saveSettings() on all the COM objects in the machine)
10184 i_copyMachineDataToSettings(*pNewConfig);
10185
10186 if (aFlags & SaveS_ResetCurStateModified)
10187 {
10188 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10189 mData->mCurrentStateModified = FALSE;
10190 fNeedsWrite = true; // always, no need to compare
10191 }
10192 else if (aFlags & SaveS_Force)
10193 {
10194 fNeedsWrite = true; // always, no need to compare
10195 }
10196 else
10197 {
10198 if (!mData->mCurrentStateModified)
10199 {
10200 // do a deep compare of the settings that we just saved with the settings
10201 // previously stored in the config file; this invokes MachineConfigFile::operator==
10202 // which does a deep compare of all the settings, which is expensive but less expensive
10203 // than writing out XML in vain
10204 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10205
10206 // could still be modified if any settings changed
10207 mData->mCurrentStateModified = fAnySettingsChanged;
10208
10209 fNeedsWrite = fAnySettingsChanged;
10210 }
10211 else
10212 fNeedsWrite = true;
10213 }
10214
10215 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10216
10217 if (fNeedsWrite)
10218 // now spit it all out!
10219 pNewConfig->write(mData->m_strConfigFileFull);
10220
10221 mData->pMachineConfigFile = pNewConfig;
10222 delete pOldConfig;
10223 i_commit();
10224
10225 // after saving settings, we are no longer different from the XML on disk
10226 mData->flModifications = 0;
10227 }
10228 catch (HRESULT err)
10229 {
10230 // we assume that error info is set by the thrower
10231 rc = err;
10232
10233 // restore old config
10234 delete pNewConfig;
10235 mData->pMachineConfigFile = pOldConfig;
10236 }
10237 catch (...)
10238 {
10239 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10240 }
10241
10242 if (fNeedsWrite)
10243 {
10244 /* Fire the data change event, even on failure (since we've already
10245 * committed all data). This is done only for SessionMachines because
10246 * mutable Machine instances are always not registered (i.e. private
10247 * to the client process that creates them) and thus don't need to
10248 * inform callbacks. */
10249 if (i_isSessionMachine())
10250 mParent->i_onMachineDataChange(mData->mUuid);
10251 }
10252
10253 LogFlowThisFunc(("rc=%08X\n", rc));
10254 LogFlowThisFuncLeave();
10255 return rc;
10256}
10257
10258/**
10259 * Implementation for saving the machine settings into the given
10260 * settings::MachineConfigFile instance. This copies machine extradata
10261 * from the previous machine config file in the instance data, if any.
10262 *
10263 * This gets called from two locations:
10264 *
10265 * -- Machine::i_saveSettings(), during the regular XML writing;
10266 *
10267 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10268 * exported to OVF and we write the VirtualBox proprietary XML
10269 * into a <vbox:Machine> tag.
10270 *
10271 * This routine fills all the fields in there, including snapshots, *except*
10272 * for the following:
10273 *
10274 * -- fCurrentStateModified. There is some special logic associated with that.
10275 *
10276 * The caller can then call MachineConfigFile::write() or do something else
10277 * with it.
10278 *
10279 * Caller must hold the machine lock!
10280 *
10281 * This throws XML errors and HRESULT, so the caller must have a catch block!
10282 */
10283void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10284{
10285 // deep copy extradata, being extra careful with self assignment (the STL
10286 // map assignment on Mac OS X clang based Xcode isn't checking)
10287 if (&config != mData->pMachineConfigFile)
10288 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10289
10290 config.uuid = mData->mUuid;
10291
10292 // copy name, description, OS type, teleport, UTC etc.
10293 config.machineUserData = mUserData->s;
10294
10295 if ( mData->mMachineState == MachineState_Saved
10296 || mData->mMachineState == MachineState_Restoring
10297 // when doing certain snapshot operations we may or may not have
10298 // a saved state in the current state, so keep everything as is
10299 || ( ( mData->mMachineState == MachineState_Snapshotting
10300 || mData->mMachineState == MachineState_DeletingSnapshot
10301 || mData->mMachineState == MachineState_RestoringSnapshot)
10302 && (!mSSData->strStateFilePath.isEmpty())
10303 )
10304 )
10305 {
10306 Assert(!mSSData->strStateFilePath.isEmpty());
10307 /* try to make the file name relative to the settings file dir */
10308 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10309 }
10310 else
10311 {
10312 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10313 config.strStateFile.setNull();
10314 }
10315
10316 if (mData->mCurrentSnapshot)
10317 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10318 else
10319 config.uuidCurrentSnapshot.clear();
10320
10321 config.timeLastStateChange = mData->mLastStateChange;
10322 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10323 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10324
10325 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10326 if (FAILED(rc)) throw rc;
10327
10328 // save machine's media registry if this is VirtualBox 4.0 or later
10329 if (config.canHaveOwnMediaRegistry())
10330 {
10331 // determine machine folder
10332 Utf8Str strMachineFolder = i_getSettingsFileFull();
10333 strMachineFolder.stripFilename();
10334 mParent->i_saveMediaRegistry(config.mediaRegistry,
10335 i_getId(), // only media with registry ID == machine UUID
10336 strMachineFolder);
10337 // this throws HRESULT
10338 }
10339
10340 // save snapshots
10341 rc = i_saveAllSnapshots(config);
10342 if (FAILED(rc)) throw rc;
10343}
10344
10345/**
10346 * Saves all snapshots of the machine into the given machine config file. Called
10347 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10348 * @param config
10349 * @return
10350 */
10351HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10352{
10353 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10354
10355 HRESULT rc = S_OK;
10356
10357 try
10358 {
10359 config.llFirstSnapshot.clear();
10360
10361 if (mData->mFirstSnapshot)
10362 {
10363 // the settings use a list for "the first snapshot"
10364 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10365
10366 // get reference to the snapshot on the list and work on that
10367 // element straight in the list to avoid excessive copying later
10368 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10369 if (FAILED(rc)) throw rc;
10370 }
10371
10372// if (mType == IsSessionMachine)
10373// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10374
10375 }
10376 catch (HRESULT err)
10377 {
10378 /* we assume that error info is set by the thrower */
10379 rc = err;
10380 }
10381 catch (...)
10382 {
10383 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10384 }
10385
10386 return rc;
10387}
10388
10389/**
10390 * Saves the VM hardware configuration. It is assumed that the
10391 * given node is empty.
10392 *
10393 * @param data Reference to the settings object for the hardware config.
10394 * @param pDbg Pointer to the settings object for the debugging config
10395 * which happens to live in mHWData.
10396 * @param pAutostart Pointer to the settings object for the autostart config
10397 * which happens to live in mHWData.
10398 */
10399HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10400 settings::Autostart *pAutostart)
10401{
10402 HRESULT rc = S_OK;
10403
10404 try
10405 {
10406 /* The hardware version attribute (optional).
10407 Automatically upgrade from 1 to current default hardware version
10408 when there is no saved state. (ugly!) */
10409 if ( mHWData->mHWVersion == "1"
10410 && mSSData->strStateFilePath.isEmpty()
10411 )
10412 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10413
10414 data.strVersion = mHWData->mHWVersion;
10415 data.uuid = mHWData->mHardwareUUID;
10416
10417 // CPU
10418 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10419 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10420 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10421 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10422 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10423 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10424 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10425 data.fPAE = !!mHWData->mPAEEnabled;
10426 data.enmLongMode = mHWData->mLongMode;
10427 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10428 data.fAPIC = !!mHWData->mAPIC;
10429 data.fX2APIC = !!mHWData->mX2APIC;
10430 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10431 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10432 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10433 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10434 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10435 data.cCPUs = mHWData->mCPUCount;
10436 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10437 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10438 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10439 data.strCpuProfile = mHWData->mCpuProfile;
10440
10441 data.llCpus.clear();
10442 if (data.fCpuHotPlug)
10443 {
10444 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10445 {
10446 if (mHWData->mCPUAttached[idx])
10447 {
10448 settings::Cpu cpu;
10449 cpu.ulId = idx;
10450 data.llCpus.push_back(cpu);
10451 }
10452 }
10453 }
10454
10455 /* Standard and Extended CPUID leafs. */
10456 data.llCpuIdLeafs.clear();
10457 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10458
10459 // memory
10460 data.ulMemorySizeMB = mHWData->mMemorySize;
10461 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10462
10463 // firmware
10464 data.firmwareType = mHWData->mFirmwareType;
10465
10466 // HID
10467 data.pointingHIDType = mHWData->mPointingHIDType;
10468 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10469
10470 // chipset
10471 data.chipsetType = mHWData->mChipsetType;
10472
10473 // paravirt
10474 data.paravirtProvider = mHWData->mParavirtProvider;
10475 data.strParavirtDebug = mHWData->mParavirtDebug;
10476
10477 // emulated USB card reader
10478 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10479
10480 // HPET
10481 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10482
10483 // boot order
10484 data.mapBootOrder.clear();
10485 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10486 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10487
10488 // display
10489 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10490 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10491 data.cMonitors = mHWData->mMonitorCount;
10492 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10493 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10494 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10495 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10496 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10497 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10498 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10499 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10500 {
10501 if (mHWData->maVideoCaptureScreens[i])
10502 ASMBitSet(&data.u64VideoCaptureScreens, i);
10503 else
10504 ASMBitClear(&data.u64VideoCaptureScreens, i);
10505 }
10506 /* store relative video capture file if possible */
10507 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10508 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10509
10510 /* VRDEServer settings (optional) */
10511 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10512 if (FAILED(rc)) throw rc;
10513
10514 /* BIOS (required) */
10515 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10516 if (FAILED(rc)) throw rc;
10517
10518 /* USB Controller (required) */
10519 data.usbSettings.llUSBControllers.clear();
10520 for (USBControllerList::const_iterator
10521 it = mUSBControllers->begin();
10522 it != mUSBControllers->end();
10523 ++it)
10524 {
10525 ComObjPtr<USBController> ctrl = *it;
10526 settings::USBController settingsCtrl;
10527
10528 settingsCtrl.strName = ctrl->i_getName();
10529 settingsCtrl.enmType = ctrl->i_getControllerType();
10530
10531 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10532 }
10533
10534 /* USB device filters (required) */
10535 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10536 if (FAILED(rc)) throw rc;
10537
10538 /* Network adapters (required) */
10539 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10540 data.llNetworkAdapters.clear();
10541 /* Write out only the nominal number of network adapters for this
10542 * chipset type. Since Machine::commit() hasn't been called there
10543 * may be extra NIC settings in the vector. */
10544 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10545 {
10546 settings::NetworkAdapter nic;
10547 nic.ulSlot = (uint32_t)slot;
10548 /* paranoia check... must not be NULL, but must not crash either. */
10549 if (mNetworkAdapters[slot])
10550 {
10551 if (mNetworkAdapters[slot]->i_hasDefaults())
10552 continue;
10553
10554 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10555 if (FAILED(rc)) throw rc;
10556
10557 data.llNetworkAdapters.push_back(nic);
10558 }
10559 }
10560
10561 /* Serial ports */
10562 data.llSerialPorts.clear();
10563 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10564 {
10565 if (mSerialPorts[slot]->i_hasDefaults())
10566 continue;
10567
10568 settings::SerialPort s;
10569 s.ulSlot = slot;
10570 rc = mSerialPorts[slot]->i_saveSettings(s);
10571 if (FAILED(rc)) return rc;
10572
10573 data.llSerialPorts.push_back(s);
10574 }
10575
10576 /* Parallel ports */
10577 data.llParallelPorts.clear();
10578 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10579 {
10580 if (mParallelPorts[slot]->i_hasDefaults())
10581 continue;
10582
10583 settings::ParallelPort p;
10584 p.ulSlot = slot;
10585 rc = mParallelPorts[slot]->i_saveSettings(p);
10586 if (FAILED(rc)) return rc;
10587
10588 data.llParallelPorts.push_back(p);
10589 }
10590
10591 /* Audio adapter */
10592 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10593 if (FAILED(rc)) return rc;
10594
10595 rc = i_saveStorageControllers(data.storage);
10596 if (FAILED(rc)) return rc;
10597
10598 /* Shared folders */
10599 data.llSharedFolders.clear();
10600 for (HWData::SharedFolderList::const_iterator
10601 it = mHWData->mSharedFolders.begin();
10602 it != mHWData->mSharedFolders.end();
10603 ++it)
10604 {
10605 SharedFolder *pSF = *it;
10606 AutoCaller sfCaller(pSF);
10607 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10608 settings::SharedFolder sf;
10609 sf.strName = pSF->i_getName();
10610 sf.strHostPath = pSF->i_getHostPath();
10611 sf.fWritable = !!pSF->i_isWritable();
10612 sf.fAutoMount = !!pSF->i_isAutoMounted();
10613
10614 data.llSharedFolders.push_back(sf);
10615 }
10616
10617 // clipboard
10618 data.clipboardMode = mHWData->mClipboardMode;
10619
10620 // drag'n'drop
10621 data.dndMode = mHWData->mDnDMode;
10622
10623 /* Guest */
10624 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10625
10626 // IO settings
10627 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10628 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10629
10630 /* BandwidthControl (required) */
10631 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10632 if (FAILED(rc)) throw rc;
10633
10634 /* Host PCI devices */
10635 data.pciAttachments.clear();
10636 for (HWData::PCIDeviceAssignmentList::const_iterator
10637 it = mHWData->mPCIDeviceAssignments.begin();
10638 it != mHWData->mPCIDeviceAssignments.end();
10639 ++it)
10640 {
10641 ComObjPtr<PCIDeviceAttachment> pda = *it;
10642 settings::HostPCIDeviceAttachment hpda;
10643
10644 rc = pda->i_saveSettings(hpda);
10645 if (FAILED(rc)) throw rc;
10646
10647 data.pciAttachments.push_back(hpda);
10648 }
10649
10650 // guest properties
10651 data.llGuestProperties.clear();
10652#ifdef VBOX_WITH_GUEST_PROPS
10653 for (HWData::GuestPropertyMap::const_iterator
10654 it = mHWData->mGuestProperties.begin();
10655 it != mHWData->mGuestProperties.end();
10656 ++it)
10657 {
10658 HWData::GuestProperty property = it->second;
10659
10660 /* Remove transient guest properties at shutdown unless we
10661 * are saving state. Note that restoring snapshot intentionally
10662 * keeps them, they will be removed if appropriate once the final
10663 * machine state is set (as crashes etc. need to work). */
10664 if ( ( mData->mMachineState == MachineState_PoweredOff
10665 || mData->mMachineState == MachineState_Aborted
10666 || mData->mMachineState == MachineState_Teleported)
10667 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10668 continue;
10669 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10670 prop.strName = it->first;
10671 prop.strValue = property.strValue;
10672 prop.timestamp = property.mTimestamp;
10673 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10674 GuestPropWriteFlags(property.mFlags, szFlags);
10675 prop.strFlags = szFlags;
10676
10677 data.llGuestProperties.push_back(prop);
10678 }
10679
10680 /* I presume this doesn't require a backup(). */
10681 mData->mGuestPropertiesModified = FALSE;
10682#endif /* VBOX_WITH_GUEST_PROPS defined */
10683
10684 *pDbg = mHWData->mDebugging;
10685 *pAutostart = mHWData->mAutostart;
10686
10687 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10688 }
10689 catch (std::bad_alloc &)
10690 {
10691 return E_OUTOFMEMORY;
10692 }
10693
10694 AssertComRC(rc);
10695 return rc;
10696}
10697
10698/**
10699 * Saves the storage controller configuration.
10700 *
10701 * @param data storage settings.
10702 */
10703HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10704{
10705 data.llStorageControllers.clear();
10706
10707 for (StorageControllerList::const_iterator
10708 it = mStorageControllers->begin();
10709 it != mStorageControllers->end();
10710 ++it)
10711 {
10712 HRESULT rc;
10713 ComObjPtr<StorageController> pCtl = *it;
10714
10715 settings::StorageController ctl;
10716 ctl.strName = pCtl->i_getName();
10717 ctl.controllerType = pCtl->i_getControllerType();
10718 ctl.storageBus = pCtl->i_getStorageBus();
10719 ctl.ulInstance = pCtl->i_getInstance();
10720 ctl.fBootable = pCtl->i_getBootable();
10721
10722 /* Save the port count. */
10723 ULONG portCount;
10724 rc = pCtl->COMGETTER(PortCount)(&portCount);
10725 ComAssertComRCRet(rc, rc);
10726 ctl.ulPortCount = portCount;
10727
10728 /* Save fUseHostIOCache */
10729 BOOL fUseHostIOCache;
10730 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10731 ComAssertComRCRet(rc, rc);
10732 ctl.fUseHostIOCache = !!fUseHostIOCache;
10733
10734 /* save the devices now. */
10735 rc = i_saveStorageDevices(pCtl, ctl);
10736 ComAssertComRCRet(rc, rc);
10737
10738 data.llStorageControllers.push_back(ctl);
10739 }
10740
10741 return S_OK;
10742}
10743
10744/**
10745 * Saves the hard disk configuration.
10746 */
10747HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10748 settings::StorageController &data)
10749{
10750 MediumAttachmentList atts;
10751
10752 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10753 if (FAILED(rc)) return rc;
10754
10755 data.llAttachedDevices.clear();
10756 for (MediumAttachmentList::const_iterator
10757 it = atts.begin();
10758 it != atts.end();
10759 ++it)
10760 {
10761 settings::AttachedDevice dev;
10762 IMediumAttachment *iA = *it;
10763 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10764 Medium *pMedium = pAttach->i_getMedium();
10765
10766 dev.deviceType = pAttach->i_getType();
10767 dev.lPort = pAttach->i_getPort();
10768 dev.lDevice = pAttach->i_getDevice();
10769 dev.fPassThrough = pAttach->i_getPassthrough();
10770 dev.fHotPluggable = pAttach->i_getHotPluggable();
10771 if (pMedium)
10772 {
10773 if (pMedium->i_isHostDrive())
10774 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10775 else
10776 dev.uuid = pMedium->i_getId();
10777 dev.fTempEject = pAttach->i_getTempEject();
10778 dev.fNonRotational = pAttach->i_getNonRotational();
10779 dev.fDiscard = pAttach->i_getDiscard();
10780 }
10781
10782 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10783
10784 data.llAttachedDevices.push_back(dev);
10785 }
10786
10787 return S_OK;
10788}
10789
10790/**
10791 * Saves machine state settings as defined by aFlags
10792 * (SaveSTS_* values).
10793 *
10794 * @param aFlags Combination of SaveSTS_* flags.
10795 *
10796 * @note Locks objects for writing.
10797 */
10798HRESULT Machine::i_saveStateSettings(int aFlags)
10799{
10800 if (aFlags == 0)
10801 return S_OK;
10802
10803 AutoCaller autoCaller(this);
10804 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10805
10806 /* This object's write lock is also necessary to serialize file access
10807 * (prevent concurrent reads and writes) */
10808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10809
10810 HRESULT rc = S_OK;
10811
10812 Assert(mData->pMachineConfigFile);
10813
10814 try
10815 {
10816 if (aFlags & SaveSTS_CurStateModified)
10817 mData->pMachineConfigFile->fCurrentStateModified = true;
10818
10819 if (aFlags & SaveSTS_StateFilePath)
10820 {
10821 if (!mSSData->strStateFilePath.isEmpty())
10822 /* try to make the file name relative to the settings file dir */
10823 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10824 else
10825 mData->pMachineConfigFile->strStateFile.setNull();
10826 }
10827
10828 if (aFlags & SaveSTS_StateTimeStamp)
10829 {
10830 Assert( mData->mMachineState != MachineState_Aborted
10831 || mSSData->strStateFilePath.isEmpty());
10832
10833 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10834
10835 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10836/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10837 }
10838
10839 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10840 }
10841 catch (...)
10842 {
10843 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10844 }
10845
10846 return rc;
10847}
10848
10849/**
10850 * Ensures that the given medium is added to a media registry. If this machine
10851 * was created with 4.0 or later, then the machine registry is used. Otherwise
10852 * the global VirtualBox media registry is used.
10853 *
10854 * Caller must NOT hold machine lock, media tree or any medium locks!
10855 *
10856 * @param pMedium
10857 */
10858void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10859{
10860 /* Paranoia checks: do not hold machine or media tree locks. */
10861 AssertReturnVoid(!isWriteLockOnCurrentThread());
10862 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10863
10864 ComObjPtr<Medium> pBase;
10865 {
10866 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10867 pBase = pMedium->i_getBase();
10868 }
10869
10870 /* Paranoia checks: do not hold medium locks. */
10871 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10872 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10873
10874 // decide which medium registry to use now that the medium is attached:
10875 Guid uuid;
10876 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10877 if (fCanHaveOwnMediaRegistry)
10878 // machine XML is VirtualBox 4.0 or higher:
10879 uuid = i_getId(); // machine UUID
10880 else
10881 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10882
10883 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10884 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10885 if (pMedium->i_addRegistry(uuid))
10886 mParent->i_markRegistryModified(uuid);
10887
10888 /* For more complex hard disk structures it can happen that the base
10889 * medium isn't yet associated with any medium registry. Do that now. */
10890 if (pMedium != pBase)
10891 {
10892 /* Tree lock needed by Medium::addRegistry when recursing. */
10893 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10894 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10895 {
10896 treeLock.release();
10897 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10898 treeLock.acquire();
10899 }
10900 if (pBase->i_addRegistryRecursive(uuid))
10901 {
10902 treeLock.release();
10903 mParent->i_markRegistryModified(uuid);
10904 }
10905 }
10906}
10907
10908/**
10909 * Creates differencing hard disks for all normal hard disks attached to this
10910 * machine and a new set of attachments to refer to created disks.
10911 *
10912 * Used when taking a snapshot or when deleting the current state. Gets called
10913 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10914 *
10915 * This method assumes that mMediumAttachments contains the original hard disk
10916 * attachments it needs to create diffs for. On success, these attachments will
10917 * be replaced with the created diffs.
10918 *
10919 * Attachments with non-normal hard disks are left as is.
10920 *
10921 * If @a aOnline is @c false then the original hard disks that require implicit
10922 * diffs will be locked for reading. Otherwise it is assumed that they are
10923 * already locked for writing (when the VM was started). Note that in the latter
10924 * case it is responsibility of the caller to lock the newly created diffs for
10925 * writing if this method succeeds.
10926 *
10927 * @param aProgress Progress object to run (must contain at least as
10928 * many operations left as the number of hard disks
10929 * attached).
10930 * @param aWeight Weight of this operation.
10931 * @param aOnline Whether the VM was online prior to this operation.
10932 *
10933 * @note The progress object is not marked as completed, neither on success nor
10934 * on failure. This is a responsibility of the caller.
10935 *
10936 * @note Locks this object and the media tree for writing.
10937 */
10938HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10939 ULONG aWeight,
10940 bool aOnline)
10941{
10942 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10943
10944 AutoCaller autoCaller(this);
10945 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10946
10947 AutoMultiWriteLock2 alock(this->lockHandle(),
10948 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10949
10950 /* must be in a protective state because we release the lock below */
10951 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10952 || mData->mMachineState == MachineState_OnlineSnapshotting
10953 || mData->mMachineState == MachineState_LiveSnapshotting
10954 || mData->mMachineState == MachineState_RestoringSnapshot
10955 || mData->mMachineState == MachineState_DeletingSnapshot
10956 , E_FAIL);
10957
10958 HRESULT rc = S_OK;
10959
10960 // use appropriate locked media map (online or offline)
10961 MediumLockListMap lockedMediaOffline;
10962 MediumLockListMap *lockedMediaMap;
10963 if (aOnline)
10964 lockedMediaMap = &mData->mSession.mLockedMedia;
10965 else
10966 lockedMediaMap = &lockedMediaOffline;
10967
10968 try
10969 {
10970 if (!aOnline)
10971 {
10972 /* lock all attached hard disks early to detect "in use"
10973 * situations before creating actual diffs */
10974 for (MediumAttachmentList::const_iterator
10975 it = mMediumAttachments->begin();
10976 it != mMediumAttachments->end();
10977 ++it)
10978 {
10979 MediumAttachment *pAtt = *it;
10980 if (pAtt->i_getType() == DeviceType_HardDisk)
10981 {
10982 Medium *pMedium = pAtt->i_getMedium();
10983 Assert(pMedium);
10984
10985 MediumLockList *pMediumLockList(new MediumLockList());
10986 alock.release();
10987 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10988 NULL /* pToLockWrite */,
10989 false /* fMediumLockWriteAll */,
10990 NULL,
10991 *pMediumLockList);
10992 alock.acquire();
10993 if (FAILED(rc))
10994 {
10995 delete pMediumLockList;
10996 throw rc;
10997 }
10998 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10999 if (FAILED(rc))
11000 {
11001 throw setError(rc,
11002 tr("Collecting locking information for all attached media failed"));
11003 }
11004 }
11005 }
11006
11007 /* Now lock all media. If this fails, nothing is locked. */
11008 alock.release();
11009 rc = lockedMediaMap->Lock();
11010 alock.acquire();
11011 if (FAILED(rc))
11012 {
11013 throw setError(rc,
11014 tr("Locking of attached media failed"));
11015 }
11016 }
11017
11018 /* remember the current list (note that we don't use backup() since
11019 * mMediumAttachments may be already backed up) */
11020 MediumAttachmentList atts = *mMediumAttachments.data();
11021
11022 /* start from scratch */
11023 mMediumAttachments->clear();
11024
11025 /* go through remembered attachments and create diffs for normal hard
11026 * disks and attach them */
11027 for (MediumAttachmentList::const_iterator
11028 it = atts.begin();
11029 it != atts.end();
11030 ++it)
11031 {
11032 MediumAttachment *pAtt = *it;
11033
11034 DeviceType_T devType = pAtt->i_getType();
11035 Medium *pMedium = pAtt->i_getMedium();
11036
11037 if ( devType != DeviceType_HardDisk
11038 || pMedium == NULL
11039 || pMedium->i_getType() != MediumType_Normal)
11040 {
11041 /* copy the attachment as is */
11042
11043 /** @todo the progress object created in SessionMachine::TakeSnaphot
11044 * only expects operations for hard disks. Later other
11045 * device types need to show up in the progress as well. */
11046 if (devType == DeviceType_HardDisk)
11047 {
11048 if (pMedium == NULL)
11049 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11050 aWeight); // weight
11051 else
11052 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11053 pMedium->i_getBase()->i_getName().c_str()).raw(),
11054 aWeight); // weight
11055 }
11056
11057 mMediumAttachments->push_back(pAtt);
11058 continue;
11059 }
11060
11061 /* need a diff */
11062 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11063 pMedium->i_getBase()->i_getName().c_str()).raw(),
11064 aWeight); // weight
11065
11066 Utf8Str strFullSnapshotFolder;
11067 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11068
11069 ComObjPtr<Medium> diff;
11070 diff.createObject();
11071 // store the diff in the same registry as the parent
11072 // (this cannot fail here because we can't create implicit diffs for
11073 // unregistered images)
11074 Guid uuidRegistryParent;
11075 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11076 Assert(fInRegistry); NOREF(fInRegistry);
11077 rc = diff->init(mParent,
11078 pMedium->i_getPreferredDiffFormat(),
11079 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11080 uuidRegistryParent,
11081 DeviceType_HardDisk);
11082 if (FAILED(rc)) throw rc;
11083
11084 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11085 * the push_back? Looks like we're going to release medium with the
11086 * wrong kind of lock (general issue with if we fail anywhere at all)
11087 * and an orphaned VDI in the snapshots folder. */
11088
11089 /* update the appropriate lock list */
11090 MediumLockList *pMediumLockList;
11091 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11092 AssertComRCThrowRC(rc);
11093 if (aOnline)
11094 {
11095 alock.release();
11096 /* The currently attached medium will be read-only, change
11097 * the lock type to read. */
11098 rc = pMediumLockList->Update(pMedium, false);
11099 alock.acquire();
11100 AssertComRCThrowRC(rc);
11101 }
11102
11103 /* release the locks before the potentially lengthy operation */
11104 alock.release();
11105 rc = pMedium->i_createDiffStorage(diff,
11106 pMedium->i_getPreferredDiffVariant(),
11107 pMediumLockList,
11108 NULL /* aProgress */,
11109 true /* aWait */);
11110 alock.acquire();
11111 if (FAILED(rc)) throw rc;
11112
11113 /* actual lock list update is done in Machine::i_commitMedia */
11114
11115 rc = diff->i_addBackReference(mData->mUuid);
11116 AssertComRCThrowRC(rc);
11117
11118 /* add a new attachment */
11119 ComObjPtr<MediumAttachment> attachment;
11120 attachment.createObject();
11121 rc = attachment->init(this,
11122 diff,
11123 pAtt->i_getControllerName(),
11124 pAtt->i_getPort(),
11125 pAtt->i_getDevice(),
11126 DeviceType_HardDisk,
11127 true /* aImplicit */,
11128 false /* aPassthrough */,
11129 false /* aTempEject */,
11130 pAtt->i_getNonRotational(),
11131 pAtt->i_getDiscard(),
11132 pAtt->i_getHotPluggable(),
11133 pAtt->i_getBandwidthGroup());
11134 if (FAILED(rc)) throw rc;
11135
11136 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11137 AssertComRCThrowRC(rc);
11138 mMediumAttachments->push_back(attachment);
11139 }
11140 }
11141 catch (HRESULT aRC) { rc = aRC; }
11142
11143 /* unlock all hard disks we locked when there is no VM */
11144 if (!aOnline)
11145 {
11146 ErrorInfoKeeper eik;
11147
11148 HRESULT rc1 = lockedMediaMap->Clear();
11149 AssertComRC(rc1);
11150 }
11151
11152 return rc;
11153}
11154
11155/**
11156 * Deletes implicit differencing hard disks created either by
11157 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11158 * mMediumAttachments.
11159 *
11160 * Note that to delete hard disks created by #attachDevice() this method is
11161 * called from #i_rollbackMedia() when the changes are rolled back.
11162 *
11163 * @note Locks this object and the media tree for writing.
11164 */
11165HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11166{
11167 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11168
11169 AutoCaller autoCaller(this);
11170 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11171
11172 AutoMultiWriteLock2 alock(this->lockHandle(),
11173 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11174
11175 /* We absolutely must have backed up state. */
11176 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11177
11178 /* Check if there are any implicitly created diff images. */
11179 bool fImplicitDiffs = false;
11180 for (MediumAttachmentList::const_iterator
11181 it = mMediumAttachments->begin();
11182 it != mMediumAttachments->end();
11183 ++it)
11184 {
11185 const ComObjPtr<MediumAttachment> &pAtt = *it;
11186 if (pAtt->i_isImplicit())
11187 {
11188 fImplicitDiffs = true;
11189 break;
11190 }
11191 }
11192 /* If there is nothing to do, leave early. This saves lots of image locking
11193 * effort. It also avoids a MachineStateChanged event without real reason.
11194 * This is important e.g. when loading a VM config, because there should be
11195 * no events. Otherwise API clients can become thoroughly confused for
11196 * inaccessible VMs (the code for loading VM configs uses this method for
11197 * cleanup if the config makes no sense), as they take such events as an
11198 * indication that the VM is alive, and they would force the VM config to
11199 * be reread, leading to an endless loop. */
11200 if (!fImplicitDiffs)
11201 return S_OK;
11202
11203 HRESULT rc = S_OK;
11204 MachineState_T oldState = mData->mMachineState;
11205
11206 /* will release the lock before the potentially lengthy operation,
11207 * so protect with the special state (unless already protected) */
11208 if ( oldState != MachineState_Snapshotting
11209 && oldState != MachineState_OnlineSnapshotting
11210 && oldState != MachineState_LiveSnapshotting
11211 && oldState != MachineState_RestoringSnapshot
11212 && oldState != MachineState_DeletingSnapshot
11213 && oldState != MachineState_DeletingSnapshotOnline
11214 && oldState != MachineState_DeletingSnapshotPaused
11215 )
11216 i_setMachineState(MachineState_SettingUp);
11217
11218 // use appropriate locked media map (online or offline)
11219 MediumLockListMap lockedMediaOffline;
11220 MediumLockListMap *lockedMediaMap;
11221 if (aOnline)
11222 lockedMediaMap = &mData->mSession.mLockedMedia;
11223 else
11224 lockedMediaMap = &lockedMediaOffline;
11225
11226 try
11227 {
11228 if (!aOnline)
11229 {
11230 /* lock all attached hard disks early to detect "in use"
11231 * situations before deleting actual diffs */
11232 for (MediumAttachmentList::const_iterator
11233 it = mMediumAttachments->begin();
11234 it != mMediumAttachments->end();
11235 ++it)
11236 {
11237 MediumAttachment *pAtt = *it;
11238 if (pAtt->i_getType() == DeviceType_HardDisk)
11239 {
11240 Medium *pMedium = pAtt->i_getMedium();
11241 Assert(pMedium);
11242
11243 MediumLockList *pMediumLockList(new MediumLockList());
11244 alock.release();
11245 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11246 NULL /* pToLockWrite */,
11247 false /* fMediumLockWriteAll */,
11248 NULL,
11249 *pMediumLockList);
11250 alock.acquire();
11251
11252 if (FAILED(rc))
11253 {
11254 delete pMediumLockList;
11255 throw rc;
11256 }
11257
11258 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11259 if (FAILED(rc))
11260 throw rc;
11261 }
11262 }
11263
11264 if (FAILED(rc))
11265 throw rc;
11266 } // end of offline
11267
11268 /* Lock lists are now up to date and include implicitly created media */
11269
11270 /* Go through remembered attachments and delete all implicitly created
11271 * diffs and fix up the attachment information */
11272 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11273 MediumAttachmentList implicitAtts;
11274 for (MediumAttachmentList::const_iterator
11275 it = mMediumAttachments->begin();
11276 it != mMediumAttachments->end();
11277 ++it)
11278 {
11279 ComObjPtr<MediumAttachment> pAtt = *it;
11280 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11281 if (pMedium.isNull())
11282 continue;
11283
11284 // Implicit attachments go on the list for deletion and back references are removed.
11285 if (pAtt->i_isImplicit())
11286 {
11287 /* Deassociate and mark for deletion */
11288 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11289 rc = pMedium->i_removeBackReference(mData->mUuid);
11290 if (FAILED(rc))
11291 throw rc;
11292 implicitAtts.push_back(pAtt);
11293 continue;
11294 }
11295
11296 /* Was this medium attached before? */
11297 if (!i_findAttachment(oldAtts, pMedium))
11298 {
11299 /* no: de-associate */
11300 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11301 rc = pMedium->i_removeBackReference(mData->mUuid);
11302 if (FAILED(rc))
11303 throw rc;
11304 continue;
11305 }
11306 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11307 }
11308
11309 /* If there are implicit attachments to delete, throw away the lock
11310 * map contents (which will unlock all media) since the medium
11311 * attachments will be rolled back. Below we need to completely
11312 * recreate the lock map anyway since it is infinitely complex to
11313 * do this incrementally (would need reconstructing each attachment
11314 * change, which would be extremely hairy). */
11315 if (implicitAtts.size() != 0)
11316 {
11317 ErrorInfoKeeper eik;
11318
11319 HRESULT rc1 = lockedMediaMap->Clear();
11320 AssertComRC(rc1);
11321 }
11322
11323 /* rollback hard disk changes */
11324 mMediumAttachments.rollback();
11325
11326 MultiResult mrc(S_OK);
11327
11328 // Delete unused implicit diffs.
11329 if (implicitAtts.size() != 0)
11330 {
11331 alock.release();
11332
11333 for (MediumAttachmentList::const_iterator
11334 it = implicitAtts.begin();
11335 it != implicitAtts.end();
11336 ++it)
11337 {
11338 // Remove medium associated with this attachment.
11339 ComObjPtr<MediumAttachment> pAtt = *it;
11340 Assert(pAtt);
11341 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11342 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11343 Assert(pMedium);
11344
11345 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11346 // continue on delete failure, just collect error messages
11347 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11348 pMedium->i_getLocationFull().c_str() ));
11349 mrc = rc;
11350 }
11351 // Clear the list of deleted implicit attachments now, while not
11352 // holding the lock, as it will ultimately trigger Medium::uninit()
11353 // calls which assume that the media tree lock isn't held.
11354 implicitAtts.clear();
11355
11356 alock.acquire();
11357
11358 /* if there is a VM recreate media lock map as mentioned above,
11359 * otherwise it is a waste of time and we leave things unlocked */
11360 if (aOnline)
11361 {
11362 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11363 /* must never be NULL, but better safe than sorry */
11364 if (!pMachine.isNull())
11365 {
11366 alock.release();
11367 rc = mData->mSession.mMachine->i_lockMedia();
11368 alock.acquire();
11369 if (FAILED(rc))
11370 throw rc;
11371 }
11372 }
11373 }
11374 }
11375 catch (HRESULT aRC) {rc = aRC;}
11376
11377 if (mData->mMachineState == MachineState_SettingUp)
11378 i_setMachineState(oldState);
11379
11380 /* unlock all hard disks we locked when there is no VM */
11381 if (!aOnline)
11382 {
11383 ErrorInfoKeeper eik;
11384
11385 HRESULT rc1 = lockedMediaMap->Clear();
11386 AssertComRC(rc1);
11387 }
11388
11389 return rc;
11390}
11391
11392
11393/**
11394 * Looks through the given list of media attachments for one with the given parameters
11395 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11396 * can be searched as well if needed.
11397 *
11398 * @param ll
11399 * @param aControllerName
11400 * @param aControllerPort
11401 * @param aDevice
11402 * @return
11403 */
11404MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11405 const Utf8Str &aControllerName,
11406 LONG aControllerPort,
11407 LONG aDevice)
11408{
11409 for (MediumAttachmentList::const_iterator
11410 it = ll.begin();
11411 it != ll.end();
11412 ++it)
11413 {
11414 MediumAttachment *pAttach = *it;
11415 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11416 return pAttach;
11417 }
11418
11419 return NULL;
11420}
11421
11422/**
11423 * Looks through the given list of media attachments for one with the given parameters
11424 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11425 * can be searched as well if needed.
11426 *
11427 * @param ll
11428 * @param pMedium
11429 * @return
11430 */
11431MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11432 ComObjPtr<Medium> pMedium)
11433{
11434 for (MediumAttachmentList::const_iterator
11435 it = ll.begin();
11436 it != ll.end();
11437 ++it)
11438 {
11439 MediumAttachment *pAttach = *it;
11440 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11441 if (pMediumThis == pMedium)
11442 return pAttach;
11443 }
11444
11445 return NULL;
11446}
11447
11448/**
11449 * Looks through the given list of media attachments for one with the given parameters
11450 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11451 * can be searched as well if needed.
11452 *
11453 * @param ll
11454 * @param id
11455 * @return
11456 */
11457MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11458 Guid &id)
11459{
11460 for (MediumAttachmentList::const_iterator
11461 it = ll.begin();
11462 it != ll.end();
11463 ++it)
11464 {
11465 MediumAttachment *pAttach = *it;
11466 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11467 if (pMediumThis->i_getId() == id)
11468 return pAttach;
11469 }
11470
11471 return NULL;
11472}
11473
11474/**
11475 * Main implementation for Machine::DetachDevice. This also gets called
11476 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11477 *
11478 * @param pAttach Medium attachment to detach.
11479 * @param writeLock Machine write lock which the caller must have locked once.
11480 * This may be released temporarily in here.
11481 * @param pSnapshot If NULL, then the detachment is for the current machine.
11482 * Otherwise this is for a SnapshotMachine, and this must be
11483 * its snapshot.
11484 * @return
11485 */
11486HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11487 AutoWriteLock &writeLock,
11488 Snapshot *pSnapshot)
11489{
11490 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11491 DeviceType_T mediumType = pAttach->i_getType();
11492
11493 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11494
11495 if (pAttach->i_isImplicit())
11496 {
11497 /* attempt to implicitly delete the implicitly created diff */
11498
11499 /// @todo move the implicit flag from MediumAttachment to Medium
11500 /// and forbid any hard disk operation when it is implicit. Or maybe
11501 /// a special media state for it to make it even more simple.
11502
11503 Assert(mMediumAttachments.isBackedUp());
11504
11505 /* will release the lock before the potentially lengthy operation, so
11506 * protect with the special state */
11507 MachineState_T oldState = mData->mMachineState;
11508 i_setMachineState(MachineState_SettingUp);
11509
11510 writeLock.release();
11511
11512 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11513 true /*aWait*/);
11514
11515 writeLock.acquire();
11516
11517 i_setMachineState(oldState);
11518
11519 if (FAILED(rc)) return rc;
11520 }
11521
11522 i_setModified(IsModified_Storage);
11523 mMediumAttachments.backup();
11524 mMediumAttachments->remove(pAttach);
11525
11526 if (!oldmedium.isNull())
11527 {
11528 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11529 if (pSnapshot)
11530 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11531 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11532 else if (mediumType != DeviceType_HardDisk)
11533 oldmedium->i_removeBackReference(mData->mUuid);
11534 }
11535
11536 return S_OK;
11537}
11538
11539/**
11540 * Goes thru all media of the given list and
11541 *
11542 * 1) calls i_detachDevice() on each of them for this machine and
11543 * 2) adds all Medium objects found in the process to the given list,
11544 * depending on cleanupMode.
11545 *
11546 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11547 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11548 * media to the list.
11549 *
11550 * This gets called from Machine::Unregister, both for the actual Machine and
11551 * the SnapshotMachine objects that might be found in the snapshots.
11552 *
11553 * Requires caller and locking. The machine lock must be passed in because it
11554 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11555 *
11556 * @param writeLock Machine lock from top-level caller; this gets passed to
11557 * i_detachDevice.
11558 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11559 * object if called for a SnapshotMachine.
11560 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11561 * added to llMedia; if Full, then all media get added;
11562 * otherwise no media get added.
11563 * @param llMedia Caller's list to receive Medium objects which got detached so
11564 * caller can close() them, depending on cleanupMode.
11565 * @return
11566 */
11567HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11568 Snapshot *pSnapshot,
11569 CleanupMode_T cleanupMode,
11570 MediaList &llMedia)
11571{
11572 Assert(isWriteLockOnCurrentThread());
11573
11574 HRESULT rc;
11575
11576 // make a temporary list because i_detachDevice invalidates iterators into
11577 // mMediumAttachments
11578 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11579
11580 for (MediumAttachmentList::iterator
11581 it = llAttachments2.begin();
11582 it != llAttachments2.end();
11583 ++it)
11584 {
11585 ComObjPtr<MediumAttachment> &pAttach = *it;
11586 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11587
11588 if (!pMedium.isNull())
11589 {
11590 AutoCaller mac(pMedium);
11591 if (FAILED(mac.rc())) return mac.rc();
11592 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11593 DeviceType_T devType = pMedium->i_getDeviceType();
11594 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11595 && devType == DeviceType_HardDisk)
11596 || (cleanupMode == CleanupMode_Full)
11597 )
11598 {
11599 llMedia.push_back(pMedium);
11600 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11601 /* Not allowed to keep this lock as below we need the parent
11602 * medium lock, and the lock order is parent to child. */
11603 lock.release();
11604 /*
11605 * Search for medias which are not attached to any machine, but
11606 * in the chain to an attached disk. Mediums are only consided
11607 * if they are:
11608 * - have only one child
11609 * - no references to any machines
11610 * - are of normal medium type
11611 */
11612 while (!pParent.isNull())
11613 {
11614 AutoCaller mac1(pParent);
11615 if (FAILED(mac1.rc())) return mac1.rc();
11616 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11617 if (pParent->i_getChildren().size() == 1)
11618 {
11619 if ( pParent->i_getMachineBackRefCount() == 0
11620 && pParent->i_getType() == MediumType_Normal
11621 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11622 llMedia.push_back(pParent);
11623 }
11624 else
11625 break;
11626 pParent = pParent->i_getParent();
11627 }
11628 }
11629 }
11630
11631 // real machine: then we need to use the proper method
11632 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11633
11634 if (FAILED(rc))
11635 return rc;
11636 }
11637
11638 return S_OK;
11639}
11640
11641/**
11642 * Perform deferred hard disk detachments.
11643 *
11644 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11645 * changed (not backed up).
11646 *
11647 * If @a aOnline is @c true then this method will also unlock the old hard
11648 * disks for which the new implicit diffs were created and will lock these new
11649 * diffs for writing.
11650 *
11651 * @param aOnline Whether the VM was online prior to this operation.
11652 *
11653 * @note Locks this object for writing!
11654 */
11655void Machine::i_commitMedia(bool aOnline /*= false*/)
11656{
11657 AutoCaller autoCaller(this);
11658 AssertComRCReturnVoid(autoCaller.rc());
11659
11660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11661
11662 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11663
11664 HRESULT rc = S_OK;
11665
11666 /* no attach/detach operations -- nothing to do */
11667 if (!mMediumAttachments.isBackedUp())
11668 return;
11669
11670 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11671 bool fMediaNeedsLocking = false;
11672
11673 /* enumerate new attachments */
11674 for (MediumAttachmentList::const_iterator
11675 it = mMediumAttachments->begin();
11676 it != mMediumAttachments->end();
11677 ++it)
11678 {
11679 MediumAttachment *pAttach = *it;
11680
11681 pAttach->i_commit();
11682
11683 Medium *pMedium = pAttach->i_getMedium();
11684 bool fImplicit = pAttach->i_isImplicit();
11685
11686 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11687 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11688 fImplicit));
11689
11690 /** @todo convert all this Machine-based voodoo to MediumAttachment
11691 * based commit logic. */
11692 if (fImplicit)
11693 {
11694 /* convert implicit attachment to normal */
11695 pAttach->i_setImplicit(false);
11696
11697 if ( aOnline
11698 && pMedium
11699 && pAttach->i_getType() == DeviceType_HardDisk
11700 )
11701 {
11702 /* update the appropriate lock list */
11703 MediumLockList *pMediumLockList;
11704 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11705 AssertComRC(rc);
11706 if (pMediumLockList)
11707 {
11708 /* unlock if there's a need to change the locking */
11709 if (!fMediaNeedsLocking)
11710 {
11711 rc = mData->mSession.mLockedMedia.Unlock();
11712 AssertComRC(rc);
11713 fMediaNeedsLocking = true;
11714 }
11715 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11716 AssertComRC(rc);
11717 rc = pMediumLockList->Append(pMedium, true);
11718 AssertComRC(rc);
11719 }
11720 }
11721
11722 continue;
11723 }
11724
11725 if (pMedium)
11726 {
11727 /* was this medium attached before? */
11728 for (MediumAttachmentList::iterator
11729 oldIt = oldAtts.begin();
11730 oldIt != oldAtts.end();
11731 ++oldIt)
11732 {
11733 MediumAttachment *pOldAttach = *oldIt;
11734 if (pOldAttach->i_getMedium() == pMedium)
11735 {
11736 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11737
11738 /* yes: remove from old to avoid de-association */
11739 oldAtts.erase(oldIt);
11740 break;
11741 }
11742 }
11743 }
11744 }
11745
11746 /* enumerate remaining old attachments and de-associate from the
11747 * current machine state */
11748 for (MediumAttachmentList::const_iterator
11749 it = oldAtts.begin();
11750 it != oldAtts.end();
11751 ++it)
11752 {
11753 MediumAttachment *pAttach = *it;
11754 Medium *pMedium = pAttach->i_getMedium();
11755
11756 /* Detach only hard disks, since DVD/floppy media is detached
11757 * instantly in MountMedium. */
11758 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11759 {
11760 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11761
11762 /* now de-associate from the current machine state */
11763 rc = pMedium->i_removeBackReference(mData->mUuid);
11764 AssertComRC(rc);
11765
11766 if (aOnline)
11767 {
11768 /* unlock since medium is not used anymore */
11769 MediumLockList *pMediumLockList;
11770 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11771 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11772 {
11773 /* this happens for online snapshots, there the attachment
11774 * is changing, but only to a diff image created under
11775 * the old one, so there is no separate lock list */
11776 Assert(!pMediumLockList);
11777 }
11778 else
11779 {
11780 AssertComRC(rc);
11781 if (pMediumLockList)
11782 {
11783 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11784 AssertComRC(rc);
11785 }
11786 }
11787 }
11788 }
11789 }
11790
11791 /* take media locks again so that the locking state is consistent */
11792 if (fMediaNeedsLocking)
11793 {
11794 Assert(aOnline);
11795 rc = mData->mSession.mLockedMedia.Lock();
11796 AssertComRC(rc);
11797 }
11798
11799 /* commit the hard disk changes */
11800 mMediumAttachments.commit();
11801
11802 if (i_isSessionMachine())
11803 {
11804 /*
11805 * Update the parent machine to point to the new owner.
11806 * This is necessary because the stored parent will point to the
11807 * session machine otherwise and cause crashes or errors later
11808 * when the session machine gets invalid.
11809 */
11810 /** @todo Change the MediumAttachment class to behave like any other
11811 * class in this regard by creating peer MediumAttachment
11812 * objects for session machines and share the data with the peer
11813 * machine.
11814 */
11815 for (MediumAttachmentList::const_iterator
11816 it = mMediumAttachments->begin();
11817 it != mMediumAttachments->end();
11818 ++it)
11819 (*it)->i_updateParentMachine(mPeer);
11820
11821 /* attach new data to the primary machine and reshare it */
11822 mPeer->mMediumAttachments.attach(mMediumAttachments);
11823 }
11824
11825 return;
11826}
11827
11828/**
11829 * Perform deferred deletion of implicitly created diffs.
11830 *
11831 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11832 * changed (not backed up).
11833 *
11834 * @note Locks this object for writing!
11835 */
11836void Machine::i_rollbackMedia()
11837{
11838 AutoCaller autoCaller(this);
11839 AssertComRCReturnVoid(autoCaller.rc());
11840
11841 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11842 LogFlowThisFunc(("Entering rollbackMedia\n"));
11843
11844 HRESULT rc = S_OK;
11845
11846 /* no attach/detach operations -- nothing to do */
11847 if (!mMediumAttachments.isBackedUp())
11848 return;
11849
11850 /* enumerate new attachments */
11851 for (MediumAttachmentList::const_iterator
11852 it = mMediumAttachments->begin();
11853 it != mMediumAttachments->end();
11854 ++it)
11855 {
11856 MediumAttachment *pAttach = *it;
11857 /* Fix up the backrefs for DVD/floppy media. */
11858 if (pAttach->i_getType() != DeviceType_HardDisk)
11859 {
11860 Medium *pMedium = pAttach->i_getMedium();
11861 if (pMedium)
11862 {
11863 rc = pMedium->i_removeBackReference(mData->mUuid);
11864 AssertComRC(rc);
11865 }
11866 }
11867
11868 (*it)->i_rollback();
11869
11870 pAttach = *it;
11871 /* Fix up the backrefs for DVD/floppy media. */
11872 if (pAttach->i_getType() != DeviceType_HardDisk)
11873 {
11874 Medium *pMedium = pAttach->i_getMedium();
11875 if (pMedium)
11876 {
11877 rc = pMedium->i_addBackReference(mData->mUuid);
11878 AssertComRC(rc);
11879 }
11880 }
11881 }
11882
11883 /** @todo convert all this Machine-based voodoo to MediumAttachment
11884 * based rollback logic. */
11885 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11886
11887 return;
11888}
11889
11890/**
11891 * Returns true if the settings file is located in the directory named exactly
11892 * as the machine; this means, among other things, that the machine directory
11893 * should be auto-renamed.
11894 *
11895 * @param aSettingsDir if not NULL, the full machine settings file directory
11896 * name will be assigned there.
11897 *
11898 * @note Doesn't lock anything.
11899 * @note Not thread safe (must be called from this object's lock).
11900 */
11901bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11902{
11903 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11904 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11905 if (aSettingsDir)
11906 *aSettingsDir = strMachineDirName;
11907 strMachineDirName.stripPath(); // vmname
11908 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11909 strConfigFileOnly.stripPath() // vmname.vbox
11910 .stripSuffix(); // vmname
11911 /** @todo hack, make somehow use of ComposeMachineFilename */
11912 if (mUserData->s.fDirectoryIncludesUUID)
11913 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11914
11915 AssertReturn(!strMachineDirName.isEmpty(), false);
11916 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11917
11918 return strMachineDirName == strConfigFileOnly;
11919}
11920
11921/**
11922 * Discards all changes to machine settings.
11923 *
11924 * @param aNotify Whether to notify the direct session about changes or not.
11925 *
11926 * @note Locks objects for writing!
11927 */
11928void Machine::i_rollback(bool aNotify)
11929{
11930 AutoCaller autoCaller(this);
11931 AssertComRCReturn(autoCaller.rc(), (void)0);
11932
11933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11934
11935 if (!mStorageControllers.isNull())
11936 {
11937 if (mStorageControllers.isBackedUp())
11938 {
11939 /* unitialize all new devices (absent in the backed up list). */
11940 StorageControllerList *backedList = mStorageControllers.backedUpData();
11941 for (StorageControllerList::const_iterator
11942 it = mStorageControllers->begin();
11943 it != mStorageControllers->end();
11944 ++it)
11945 {
11946 if ( std::find(backedList->begin(), backedList->end(), *it)
11947 == backedList->end()
11948 )
11949 {
11950 (*it)->uninit();
11951 }
11952 }
11953
11954 /* restore the list */
11955 mStorageControllers.rollback();
11956 }
11957
11958 /* rollback any changes to devices after restoring the list */
11959 if (mData->flModifications & IsModified_Storage)
11960 {
11961 for (StorageControllerList::const_iterator
11962 it = mStorageControllers->begin();
11963 it != mStorageControllers->end();
11964 ++it)
11965 {
11966 (*it)->i_rollback();
11967 }
11968 }
11969 }
11970
11971 if (!mUSBControllers.isNull())
11972 {
11973 if (mUSBControllers.isBackedUp())
11974 {
11975 /* unitialize all new devices (absent in the backed up list). */
11976 USBControllerList *backedList = mUSBControllers.backedUpData();
11977 for (USBControllerList::const_iterator
11978 it = mUSBControllers->begin();
11979 it != mUSBControllers->end();
11980 ++it)
11981 {
11982 if ( std::find(backedList->begin(), backedList->end(), *it)
11983 == backedList->end()
11984 )
11985 {
11986 (*it)->uninit();
11987 }
11988 }
11989
11990 /* restore the list */
11991 mUSBControllers.rollback();
11992 }
11993
11994 /* rollback any changes to devices after restoring the list */
11995 if (mData->flModifications & IsModified_USB)
11996 {
11997 for (USBControllerList::const_iterator
11998 it = mUSBControllers->begin();
11999 it != mUSBControllers->end();
12000 ++it)
12001 {
12002 (*it)->i_rollback();
12003 }
12004 }
12005 }
12006
12007 mUserData.rollback();
12008
12009 mHWData.rollback();
12010
12011 if (mData->flModifications & IsModified_Storage)
12012 i_rollbackMedia();
12013
12014 if (mBIOSSettings)
12015 mBIOSSettings->i_rollback();
12016
12017 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12018 mVRDEServer->i_rollback();
12019
12020 if (mAudioAdapter)
12021 mAudioAdapter->i_rollback();
12022
12023 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12024 mUSBDeviceFilters->i_rollback();
12025
12026 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12027 mBandwidthControl->i_rollback();
12028
12029 if (!mHWData.isNull())
12030 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12031 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12032 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12033 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12034
12035 if (mData->flModifications & IsModified_NetworkAdapters)
12036 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12037 if ( mNetworkAdapters[slot]
12038 && mNetworkAdapters[slot]->i_isModified())
12039 {
12040 mNetworkAdapters[slot]->i_rollback();
12041 networkAdapters[slot] = mNetworkAdapters[slot];
12042 }
12043
12044 if (mData->flModifications & IsModified_SerialPorts)
12045 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12046 if ( mSerialPorts[slot]
12047 && mSerialPorts[slot]->i_isModified())
12048 {
12049 mSerialPorts[slot]->i_rollback();
12050 serialPorts[slot] = mSerialPorts[slot];
12051 }
12052
12053 if (mData->flModifications & IsModified_ParallelPorts)
12054 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12055 if ( mParallelPorts[slot]
12056 && mParallelPorts[slot]->i_isModified())
12057 {
12058 mParallelPorts[slot]->i_rollback();
12059 parallelPorts[slot] = mParallelPorts[slot];
12060 }
12061
12062 if (aNotify)
12063 {
12064 /* inform the direct session about changes */
12065
12066 ComObjPtr<Machine> that = this;
12067 uint32_t flModifications = mData->flModifications;
12068 alock.release();
12069
12070 if (flModifications & IsModified_SharedFolders)
12071 that->i_onSharedFolderChange();
12072
12073 if (flModifications & IsModified_VRDEServer)
12074 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12075 if (flModifications & IsModified_USB)
12076 that->i_onUSBControllerChange();
12077
12078 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12079 if (networkAdapters[slot])
12080 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12081 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12082 if (serialPorts[slot])
12083 that->i_onSerialPortChange(serialPorts[slot]);
12084 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12085 if (parallelPorts[slot])
12086 that->i_onParallelPortChange(parallelPorts[slot]);
12087
12088 if (flModifications & IsModified_Storage)
12089 that->i_onStorageControllerChange();
12090
12091#if 0
12092 if (flModifications & IsModified_BandwidthControl)
12093 that->onBandwidthControlChange();
12094#endif
12095 }
12096}
12097
12098/**
12099 * Commits all the changes to machine settings.
12100 *
12101 * Note that this operation is supposed to never fail.
12102 *
12103 * @note Locks this object and children for writing.
12104 */
12105void Machine::i_commit()
12106{
12107 AutoCaller autoCaller(this);
12108 AssertComRCReturnVoid(autoCaller.rc());
12109
12110 AutoCaller peerCaller(mPeer);
12111 AssertComRCReturnVoid(peerCaller.rc());
12112
12113 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12114
12115 /*
12116 * use safe commit to ensure Snapshot machines (that share mUserData)
12117 * will still refer to a valid memory location
12118 */
12119 mUserData.commitCopy();
12120
12121 mHWData.commit();
12122
12123 if (mMediumAttachments.isBackedUp())
12124 i_commitMedia(Global::IsOnline(mData->mMachineState));
12125
12126 mBIOSSettings->i_commit();
12127 mVRDEServer->i_commit();
12128 mAudioAdapter->i_commit();
12129 mUSBDeviceFilters->i_commit();
12130 mBandwidthControl->i_commit();
12131
12132 /* Since mNetworkAdapters is a list which might have been changed (resized)
12133 * without using the Backupable<> template we need to handle the copying
12134 * of the list entries manually, including the creation of peers for the
12135 * new objects. */
12136 bool commitNetworkAdapters = false;
12137 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12138 if (mPeer)
12139 {
12140 /* commit everything, even the ones which will go away */
12141 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12142 mNetworkAdapters[slot]->i_commit();
12143 /* copy over the new entries, creating a peer and uninit the original */
12144 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12145 for (size_t slot = 0; slot < newSize; slot++)
12146 {
12147 /* look if this adapter has a peer device */
12148 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12149 if (!peer)
12150 {
12151 /* no peer means the adapter is a newly created one;
12152 * create a peer owning data this data share it with */
12153 peer.createObject();
12154 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12155 }
12156 mPeer->mNetworkAdapters[slot] = peer;
12157 }
12158 /* uninit any no longer needed network adapters */
12159 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12160 mNetworkAdapters[slot]->uninit();
12161 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12162 {
12163 if (mPeer->mNetworkAdapters[slot])
12164 mPeer->mNetworkAdapters[slot]->uninit();
12165 }
12166 /* Keep the original network adapter count until this point, so that
12167 * discarding a chipset type change will not lose settings. */
12168 mNetworkAdapters.resize(newSize);
12169 mPeer->mNetworkAdapters.resize(newSize);
12170 }
12171 else
12172 {
12173 /* we have no peer (our parent is the newly created machine);
12174 * just commit changes to the network adapters */
12175 commitNetworkAdapters = true;
12176 }
12177 if (commitNetworkAdapters)
12178 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12179 mNetworkAdapters[slot]->i_commit();
12180
12181 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12182 mSerialPorts[slot]->i_commit();
12183 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12184 mParallelPorts[slot]->i_commit();
12185
12186 bool commitStorageControllers = false;
12187
12188 if (mStorageControllers.isBackedUp())
12189 {
12190 mStorageControllers.commit();
12191
12192 if (mPeer)
12193 {
12194 /* Commit all changes to new controllers (this will reshare data with
12195 * peers for those who have peers) */
12196 StorageControllerList *newList = new StorageControllerList();
12197 for (StorageControllerList::const_iterator
12198 it = mStorageControllers->begin();
12199 it != mStorageControllers->end();
12200 ++it)
12201 {
12202 (*it)->i_commit();
12203
12204 /* look if this controller has a peer device */
12205 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12206 if (!peer)
12207 {
12208 /* no peer means the device is a newly created one;
12209 * create a peer owning data this device share it with */
12210 peer.createObject();
12211 peer->init(mPeer, *it, true /* aReshare */);
12212 }
12213 else
12214 {
12215 /* remove peer from the old list */
12216 mPeer->mStorageControllers->remove(peer);
12217 }
12218 /* and add it to the new list */
12219 newList->push_back(peer);
12220 }
12221
12222 /* uninit old peer's controllers that are left */
12223 for (StorageControllerList::const_iterator
12224 it = mPeer->mStorageControllers->begin();
12225 it != mPeer->mStorageControllers->end();
12226 ++it)
12227 {
12228 (*it)->uninit();
12229 }
12230
12231 /* attach new list of controllers to our peer */
12232 mPeer->mStorageControllers.attach(newList);
12233 }
12234 else
12235 {
12236 /* we have no peer (our parent is the newly created machine);
12237 * just commit changes to devices */
12238 commitStorageControllers = true;
12239 }
12240 }
12241 else
12242 {
12243 /* the list of controllers itself is not changed,
12244 * just commit changes to controllers themselves */
12245 commitStorageControllers = true;
12246 }
12247
12248 if (commitStorageControllers)
12249 {
12250 for (StorageControllerList::const_iterator
12251 it = mStorageControllers->begin();
12252 it != mStorageControllers->end();
12253 ++it)
12254 {
12255 (*it)->i_commit();
12256 }
12257 }
12258
12259 bool commitUSBControllers = false;
12260
12261 if (mUSBControllers.isBackedUp())
12262 {
12263 mUSBControllers.commit();
12264
12265 if (mPeer)
12266 {
12267 /* Commit all changes to new controllers (this will reshare data with
12268 * peers for those who have peers) */
12269 USBControllerList *newList = new USBControllerList();
12270 for (USBControllerList::const_iterator
12271 it = mUSBControllers->begin();
12272 it != mUSBControllers->end();
12273 ++it)
12274 {
12275 (*it)->i_commit();
12276
12277 /* look if this controller has a peer device */
12278 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12279 if (!peer)
12280 {
12281 /* no peer means the device is a newly created one;
12282 * create a peer owning data this device share it with */
12283 peer.createObject();
12284 peer->init(mPeer, *it, true /* aReshare */);
12285 }
12286 else
12287 {
12288 /* remove peer from the old list */
12289 mPeer->mUSBControllers->remove(peer);
12290 }
12291 /* and add it to the new list */
12292 newList->push_back(peer);
12293 }
12294
12295 /* uninit old peer's controllers that are left */
12296 for (USBControllerList::const_iterator
12297 it = mPeer->mUSBControllers->begin();
12298 it != mPeer->mUSBControllers->end();
12299 ++it)
12300 {
12301 (*it)->uninit();
12302 }
12303
12304 /* attach new list of controllers to our peer */
12305 mPeer->mUSBControllers.attach(newList);
12306 }
12307 else
12308 {
12309 /* we have no peer (our parent is the newly created machine);
12310 * just commit changes to devices */
12311 commitUSBControllers = true;
12312 }
12313 }
12314 else
12315 {
12316 /* the list of controllers itself is not changed,
12317 * just commit changes to controllers themselves */
12318 commitUSBControllers = true;
12319 }
12320
12321 if (commitUSBControllers)
12322 {
12323 for (USBControllerList::const_iterator
12324 it = mUSBControllers->begin();
12325 it != mUSBControllers->end();
12326 ++it)
12327 {
12328 (*it)->i_commit();
12329 }
12330 }
12331
12332 if (i_isSessionMachine())
12333 {
12334 /* attach new data to the primary machine and reshare it */
12335 mPeer->mUserData.attach(mUserData);
12336 mPeer->mHWData.attach(mHWData);
12337 /* mmMediumAttachments is reshared by fixupMedia */
12338 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12339 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12340 }
12341}
12342
12343/**
12344 * Copies all the hardware data from the given machine.
12345 *
12346 * Currently, only called when the VM is being restored from a snapshot. In
12347 * particular, this implies that the VM is not running during this method's
12348 * call.
12349 *
12350 * @note This method must be called from under this object's lock.
12351 *
12352 * @note This method doesn't call #i_commit(), so all data remains backed up and
12353 * unsaved.
12354 */
12355void Machine::i_copyFrom(Machine *aThat)
12356{
12357 AssertReturnVoid(!i_isSnapshotMachine());
12358 AssertReturnVoid(aThat->i_isSnapshotMachine());
12359
12360 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12361
12362 mHWData.assignCopy(aThat->mHWData);
12363
12364 // create copies of all shared folders (mHWData after attaching a copy
12365 // contains just references to original objects)
12366 for (HWData::SharedFolderList::iterator
12367 it = mHWData->mSharedFolders.begin();
12368 it != mHWData->mSharedFolders.end();
12369 ++it)
12370 {
12371 ComObjPtr<SharedFolder> folder;
12372 folder.createObject();
12373 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12374 AssertComRC(rc);
12375 *it = folder;
12376 }
12377
12378 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12379 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12380 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12381 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12382 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12383
12384 /* create private copies of all controllers */
12385 mStorageControllers.backup();
12386 mStorageControllers->clear();
12387 for (StorageControllerList::const_iterator
12388 it = aThat->mStorageControllers->begin();
12389 it != aThat->mStorageControllers->end();
12390 ++it)
12391 {
12392 ComObjPtr<StorageController> ctrl;
12393 ctrl.createObject();
12394 ctrl->initCopy(this, *it);
12395 mStorageControllers->push_back(ctrl);
12396 }
12397
12398 /* create private copies of all USB controllers */
12399 mUSBControllers.backup();
12400 mUSBControllers->clear();
12401 for (USBControllerList::const_iterator
12402 it = aThat->mUSBControllers->begin();
12403 it != aThat->mUSBControllers->end();
12404 ++it)
12405 {
12406 ComObjPtr<USBController> ctrl;
12407 ctrl.createObject();
12408 ctrl->initCopy(this, *it);
12409 mUSBControllers->push_back(ctrl);
12410 }
12411
12412 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12413 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12414 {
12415 if (mNetworkAdapters[slot].isNotNull())
12416 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12417 else
12418 {
12419 unconst(mNetworkAdapters[slot]).createObject();
12420 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12421 }
12422 }
12423 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12424 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12425 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12426 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12427}
12428
12429/**
12430 * Returns whether the given storage controller is hotplug capable.
12431 *
12432 * @returns true if the controller supports hotplugging
12433 * false otherwise.
12434 * @param enmCtrlType The controller type to check for.
12435 */
12436bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12437{
12438 ComPtr<ISystemProperties> systemProperties;
12439 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12440 if (FAILED(rc))
12441 return false;
12442
12443 BOOL aHotplugCapable = FALSE;
12444 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12445
12446 return RT_BOOL(aHotplugCapable);
12447}
12448
12449#ifdef VBOX_WITH_RESOURCE_USAGE_API
12450
12451void Machine::i_getDiskList(MediaList &list)
12452{
12453 for (MediumAttachmentList::const_iterator
12454 it = mMediumAttachments->begin();
12455 it != mMediumAttachments->end();
12456 ++it)
12457 {
12458 MediumAttachment *pAttach = *it;
12459 /* just in case */
12460 AssertContinue(pAttach);
12461
12462 AutoCaller localAutoCallerA(pAttach);
12463 if (FAILED(localAutoCallerA.rc())) continue;
12464
12465 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12466
12467 if (pAttach->i_getType() == DeviceType_HardDisk)
12468 list.push_back(pAttach->i_getMedium());
12469 }
12470}
12471
12472void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12473{
12474 AssertReturnVoid(isWriteLockOnCurrentThread());
12475 AssertPtrReturnVoid(aCollector);
12476
12477 pm::CollectorHAL *hal = aCollector->getHAL();
12478 /* Create sub metrics */
12479 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12480 "Percentage of processor time spent in user mode by the VM process.");
12481 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12482 "Percentage of processor time spent in kernel mode by the VM process.");
12483 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12484 "Size of resident portion of VM process in memory.");
12485 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12486 "Actual size of all VM disks combined.");
12487 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12488 "Network receive rate.");
12489 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12490 "Network transmit rate.");
12491 /* Create and register base metrics */
12492 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12493 cpuLoadUser, cpuLoadKernel);
12494 aCollector->registerBaseMetric(cpuLoad);
12495 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12496 ramUsageUsed);
12497 aCollector->registerBaseMetric(ramUsage);
12498 MediaList disks;
12499 i_getDiskList(disks);
12500 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12501 diskUsageUsed);
12502 aCollector->registerBaseMetric(diskUsage);
12503
12504 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12505 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12506 new pm::AggregateAvg()));
12507 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12508 new pm::AggregateMin()));
12509 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12510 new pm::AggregateMax()));
12511 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12512 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12513 new pm::AggregateAvg()));
12514 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12515 new pm::AggregateMin()));
12516 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12517 new pm::AggregateMax()));
12518
12519 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12520 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12521 new pm::AggregateAvg()));
12522 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12523 new pm::AggregateMin()));
12524 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12525 new pm::AggregateMax()));
12526
12527 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12528 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12529 new pm::AggregateAvg()));
12530 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12531 new pm::AggregateMin()));
12532 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12533 new pm::AggregateMax()));
12534
12535
12536 /* Guest metrics collector */
12537 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12538 aCollector->registerGuest(mCollectorGuest);
12539 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12540
12541 /* Create sub metrics */
12542 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12543 "Percentage of processor time spent in user mode as seen by the guest.");
12544 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12545 "Percentage of processor time spent in kernel mode as seen by the guest.");
12546 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12547 "Percentage of processor time spent idling as seen by the guest.");
12548
12549 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12550 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12551 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12552 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12553 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12554 pm::SubMetric *guestMemCache = new pm::SubMetric(
12555 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12556
12557 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12558 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12559
12560 /* Create and register base metrics */
12561 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12562 machineNetRx, machineNetTx);
12563 aCollector->registerBaseMetric(machineNetRate);
12564
12565 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12566 guestLoadUser, guestLoadKernel, guestLoadIdle);
12567 aCollector->registerBaseMetric(guestCpuLoad);
12568
12569 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12570 guestMemTotal, guestMemFree,
12571 guestMemBalloon, guestMemShared,
12572 guestMemCache, guestPagedTotal);
12573 aCollector->registerBaseMetric(guestCpuMem);
12574
12575 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12576 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12577 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12578 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12579
12580 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12581 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12582 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12583 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12584
12585 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12586 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12587 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12588 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12589
12590 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12591 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12592 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12593 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12594
12595 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12596 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12597 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12598 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12599
12600 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12601 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12602 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12603 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12604
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12608 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12609
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12613 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12614
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12618 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12619
12620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12623 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12624
12625 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12626 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12627 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12628 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12629}
12630
12631void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12632{
12633 AssertReturnVoid(isWriteLockOnCurrentThread());
12634
12635 if (aCollector)
12636 {
12637 aCollector->unregisterMetricsFor(aMachine);
12638 aCollector->unregisterBaseMetricsFor(aMachine);
12639 }
12640}
12641
12642#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12643
12644
12645////////////////////////////////////////////////////////////////////////////////
12646
12647DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12648
12649HRESULT SessionMachine::FinalConstruct()
12650{
12651 LogFlowThisFunc(("\n"));
12652
12653 mClientToken = NULL;
12654
12655 return BaseFinalConstruct();
12656}
12657
12658void SessionMachine::FinalRelease()
12659{
12660 LogFlowThisFunc(("\n"));
12661
12662 Assert(!mClientToken);
12663 /* paranoia, should not hang around any more */
12664 if (mClientToken)
12665 {
12666 delete mClientToken;
12667 mClientToken = NULL;
12668 }
12669
12670 uninit(Uninit::Unexpected);
12671
12672 BaseFinalRelease();
12673}
12674
12675/**
12676 * @note Must be called only by Machine::LockMachine() from its own write lock.
12677 */
12678HRESULT SessionMachine::init(Machine *aMachine)
12679{
12680 LogFlowThisFuncEnter();
12681 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12682
12683 AssertReturn(aMachine, E_INVALIDARG);
12684
12685 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12686
12687 /* Enclose the state transition NotReady->InInit->Ready */
12688 AutoInitSpan autoInitSpan(this);
12689 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12690
12691 HRESULT rc = S_OK;
12692
12693 RT_ZERO(mAuthLibCtx);
12694
12695 /* create the machine client token */
12696 try
12697 {
12698 mClientToken = new ClientToken(aMachine, this);
12699 if (!mClientToken->isReady())
12700 {
12701 delete mClientToken;
12702 mClientToken = NULL;
12703 rc = E_FAIL;
12704 }
12705 }
12706 catch (std::bad_alloc &)
12707 {
12708 rc = E_OUTOFMEMORY;
12709 }
12710 if (FAILED(rc))
12711 return rc;
12712
12713 /* memorize the peer Machine */
12714 unconst(mPeer) = aMachine;
12715 /* share the parent pointer */
12716 unconst(mParent) = aMachine->mParent;
12717
12718 /* take the pointers to data to share */
12719 mData.share(aMachine->mData);
12720 mSSData.share(aMachine->mSSData);
12721
12722 mUserData.share(aMachine->mUserData);
12723 mHWData.share(aMachine->mHWData);
12724 mMediumAttachments.share(aMachine->mMediumAttachments);
12725
12726 mStorageControllers.allocate();
12727 for (StorageControllerList::const_iterator
12728 it = aMachine->mStorageControllers->begin();
12729 it != aMachine->mStorageControllers->end();
12730 ++it)
12731 {
12732 ComObjPtr<StorageController> ctl;
12733 ctl.createObject();
12734 ctl->init(this, *it);
12735 mStorageControllers->push_back(ctl);
12736 }
12737
12738 mUSBControllers.allocate();
12739 for (USBControllerList::const_iterator
12740 it = aMachine->mUSBControllers->begin();
12741 it != aMachine->mUSBControllers->end();
12742 ++it)
12743 {
12744 ComObjPtr<USBController> ctl;
12745 ctl.createObject();
12746 ctl->init(this, *it);
12747 mUSBControllers->push_back(ctl);
12748 }
12749
12750 unconst(mBIOSSettings).createObject();
12751 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12752 /* create another VRDEServer object that will be mutable */
12753 unconst(mVRDEServer).createObject();
12754 mVRDEServer->init(this, aMachine->mVRDEServer);
12755 /* create another audio adapter object that will be mutable */
12756 unconst(mAudioAdapter).createObject();
12757 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12758 /* create a list of serial ports that will be mutable */
12759 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12760 {
12761 unconst(mSerialPorts[slot]).createObject();
12762 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12763 }
12764 /* create a list of parallel ports that will be mutable */
12765 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12766 {
12767 unconst(mParallelPorts[slot]).createObject();
12768 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12769 }
12770
12771 /* create another USB device filters object that will be mutable */
12772 unconst(mUSBDeviceFilters).createObject();
12773 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12774
12775 /* create a list of network adapters that will be mutable */
12776 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12777 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12778 {
12779 unconst(mNetworkAdapters[slot]).createObject();
12780 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12781 }
12782
12783 /* create another bandwidth control object that will be mutable */
12784 unconst(mBandwidthControl).createObject();
12785 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12786
12787 /* default is to delete saved state on Saved -> PoweredOff transition */
12788 mRemoveSavedState = true;
12789
12790 /* Confirm a successful initialization when it's the case */
12791 autoInitSpan.setSucceeded();
12792
12793 miNATNetworksStarted = 0;
12794
12795 LogFlowThisFuncLeave();
12796 return rc;
12797}
12798
12799/**
12800 * Uninitializes this session object. If the reason is other than
12801 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12802 * or the client watcher code.
12803 *
12804 * @param aReason uninitialization reason
12805 *
12806 * @note Locks mParent + this object for writing.
12807 */
12808void SessionMachine::uninit(Uninit::Reason aReason)
12809{
12810 LogFlowThisFuncEnter();
12811 LogFlowThisFunc(("reason=%d\n", aReason));
12812
12813 /*
12814 * Strongly reference ourselves to prevent this object deletion after
12815 * mData->mSession.mMachine.setNull() below (which can release the last
12816 * reference and call the destructor). Important: this must be done before
12817 * accessing any members (and before AutoUninitSpan that does it as well).
12818 * This self reference will be released as the very last step on return.
12819 */
12820 ComObjPtr<SessionMachine> selfRef;
12821 if (aReason != Uninit::Unexpected)
12822 selfRef = this;
12823
12824 /* Enclose the state transition Ready->InUninit->NotReady */
12825 AutoUninitSpan autoUninitSpan(this);
12826 if (autoUninitSpan.uninitDone())
12827 {
12828 LogFlowThisFunc(("Already uninitialized\n"));
12829 LogFlowThisFuncLeave();
12830 return;
12831 }
12832
12833 if (autoUninitSpan.initFailed())
12834 {
12835 /* We've been called by init() because it's failed. It's not really
12836 * necessary (nor it's safe) to perform the regular uninit sequence
12837 * below, the following is enough.
12838 */
12839 LogFlowThisFunc(("Initialization failed.\n"));
12840 /* destroy the machine client token */
12841 if (mClientToken)
12842 {
12843 delete mClientToken;
12844 mClientToken = NULL;
12845 }
12846 uninitDataAndChildObjects();
12847 mData.free();
12848 unconst(mParent) = NULL;
12849 unconst(mPeer) = NULL;
12850 LogFlowThisFuncLeave();
12851 return;
12852 }
12853
12854 MachineState_T lastState;
12855 {
12856 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12857 lastState = mData->mMachineState;
12858 }
12859 NOREF(lastState);
12860
12861#ifdef VBOX_WITH_USB
12862 // release all captured USB devices, but do this before requesting the locks below
12863 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12864 {
12865 /* Console::captureUSBDevices() is called in the VM process only after
12866 * setting the machine state to Starting or Restoring.
12867 * Console::detachAllUSBDevices() will be called upon successful
12868 * termination. So, we need to release USB devices only if there was
12869 * an abnormal termination of a running VM.
12870 *
12871 * This is identical to SessionMachine::DetachAllUSBDevices except
12872 * for the aAbnormal argument. */
12873 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12874 AssertComRC(rc);
12875 NOREF(rc);
12876
12877 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12878 if (service)
12879 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12880 }
12881#endif /* VBOX_WITH_USB */
12882
12883 // we need to lock this object in uninit() because the lock is shared
12884 // with mPeer (as well as data we modify below). mParent lock is needed
12885 // by several calls to it.
12886 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12887
12888#ifdef VBOX_WITH_RESOURCE_USAGE_API
12889 /*
12890 * It is safe to call Machine::i_unregisterMetrics() here because
12891 * PerformanceCollector::samplerCallback no longer accesses guest methods
12892 * holding the lock.
12893 */
12894 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12895 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12896 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12897 if (mCollectorGuest)
12898 {
12899 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12900 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12901 mCollectorGuest = NULL;
12902 }
12903#endif
12904
12905 if (aReason == Uninit::Abnormal)
12906 {
12907 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12908
12909 /* reset the state to Aborted */
12910 if (mData->mMachineState != MachineState_Aborted)
12911 i_setMachineState(MachineState_Aborted);
12912 }
12913
12914 // any machine settings modified?
12915 if (mData->flModifications)
12916 {
12917 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12918 i_rollback(false /* aNotify */);
12919 }
12920
12921 mData->mSession.mPID = NIL_RTPROCESS;
12922
12923 if (aReason == Uninit::Unexpected)
12924 {
12925 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12926 * client watcher thread to update the set of machines that have open
12927 * sessions. */
12928 mParent->i_updateClientWatcher();
12929 }
12930
12931 /* uninitialize all remote controls */
12932 if (mData->mSession.mRemoteControls.size())
12933 {
12934 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12935 mData->mSession.mRemoteControls.size()));
12936
12937 /* Always restart a the beginning, since the iterator is invalidated
12938 * by using erase(). */
12939 for (Data::Session::RemoteControlList::iterator
12940 it = mData->mSession.mRemoteControls.begin();
12941 it != mData->mSession.mRemoteControls.end();
12942 it = mData->mSession.mRemoteControls.begin())
12943 {
12944 ComPtr<IInternalSessionControl> pControl = *it;
12945 mData->mSession.mRemoteControls.erase(it);
12946 multilock.release();
12947 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12948 HRESULT rc = pControl->Uninitialize();
12949 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12950 if (FAILED(rc))
12951 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12952 multilock.acquire();
12953 }
12954 mData->mSession.mRemoteControls.clear();
12955 }
12956
12957 /* Remove all references to the NAT network service. The service will stop
12958 * if all references (also from other VMs) are removed. */
12959 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12960 {
12961 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12962 {
12963 BOOL enabled;
12964 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12965 if ( FAILED(hrc)
12966 || !enabled)
12967 continue;
12968
12969 NetworkAttachmentType_T type;
12970 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12971 if ( SUCCEEDED(hrc)
12972 && type == NetworkAttachmentType_NATNetwork)
12973 {
12974 Bstr name;
12975 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12976 if (SUCCEEDED(hrc))
12977 {
12978 multilock.release();
12979 Utf8Str strName(name);
12980 LogRel(("VM '%s' stops using NAT network '%s'\n",
12981 mUserData->s.strName.c_str(), strName.c_str()));
12982 mParent->i_natNetworkRefDec(strName);
12983 multilock.acquire();
12984 }
12985 }
12986 }
12987 }
12988
12989 /*
12990 * An expected uninitialization can come only from #i_checkForDeath().
12991 * Otherwise it means that something's gone really wrong (for example,
12992 * the Session implementation has released the VirtualBox reference
12993 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12994 * etc). However, it's also possible, that the client releases the IPC
12995 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12996 * but the VirtualBox release event comes first to the server process.
12997 * This case is practically possible, so we should not assert on an
12998 * unexpected uninit, just log a warning.
12999 */
13000
13001 if (aReason == Uninit::Unexpected)
13002 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13003
13004 if (aReason != Uninit::Normal)
13005 {
13006 mData->mSession.mDirectControl.setNull();
13007 }
13008 else
13009 {
13010 /* this must be null here (see #OnSessionEnd()) */
13011 Assert(mData->mSession.mDirectControl.isNull());
13012 Assert(mData->mSession.mState == SessionState_Unlocking);
13013 Assert(!mData->mSession.mProgress.isNull());
13014 }
13015 if (mData->mSession.mProgress)
13016 {
13017 if (aReason == Uninit::Normal)
13018 mData->mSession.mProgress->i_notifyComplete(S_OK);
13019 else
13020 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13021 COM_IIDOF(ISession),
13022 getComponentName(),
13023 tr("The VM session was aborted"));
13024 mData->mSession.mProgress.setNull();
13025 }
13026
13027 if (mConsoleTaskData.mProgress)
13028 {
13029 Assert(aReason == Uninit::Abnormal);
13030 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13031 COM_IIDOF(ISession),
13032 getComponentName(),
13033 tr("The VM session was aborted"));
13034 mConsoleTaskData.mProgress.setNull();
13035 }
13036
13037 /* remove the association between the peer machine and this session machine */
13038 Assert( (SessionMachine*)mData->mSession.mMachine == this
13039 || aReason == Uninit::Unexpected);
13040
13041 /* reset the rest of session data */
13042 mData->mSession.mLockType = LockType_Null;
13043 mData->mSession.mMachine.setNull();
13044 mData->mSession.mState = SessionState_Unlocked;
13045 mData->mSession.mName.setNull();
13046
13047 /* destroy the machine client token before leaving the exclusive lock */
13048 if (mClientToken)
13049 {
13050 delete mClientToken;
13051 mClientToken = NULL;
13052 }
13053
13054 /* fire an event */
13055 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13056
13057 uninitDataAndChildObjects();
13058
13059 /* free the essential data structure last */
13060 mData.free();
13061
13062 /* release the exclusive lock before setting the below two to NULL */
13063 multilock.release();
13064
13065 unconst(mParent) = NULL;
13066 unconst(mPeer) = NULL;
13067
13068 AuthLibUnload(&mAuthLibCtx);
13069
13070 LogFlowThisFuncLeave();
13071}
13072
13073// util::Lockable interface
13074////////////////////////////////////////////////////////////////////////////////
13075
13076/**
13077 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13078 * with the primary Machine instance (mPeer).
13079 */
13080RWLockHandle *SessionMachine::lockHandle() const
13081{
13082 AssertReturn(mPeer != NULL, NULL);
13083 return mPeer->lockHandle();
13084}
13085
13086// IInternalMachineControl methods
13087////////////////////////////////////////////////////////////////////////////////
13088
13089/**
13090 * Passes collected guest statistics to performance collector object
13091 */
13092HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13093 ULONG aCpuKernel, ULONG aCpuIdle,
13094 ULONG aMemTotal, ULONG aMemFree,
13095 ULONG aMemBalloon, ULONG aMemShared,
13096 ULONG aMemCache, ULONG aPageTotal,
13097 ULONG aAllocVMM, ULONG aFreeVMM,
13098 ULONG aBalloonedVMM, ULONG aSharedVMM,
13099 ULONG aVmNetRx, ULONG aVmNetTx)
13100{
13101#ifdef VBOX_WITH_RESOURCE_USAGE_API
13102 if (mCollectorGuest)
13103 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13104 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13105 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13106 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13107
13108 return S_OK;
13109#else
13110 NOREF(aValidStats);
13111 NOREF(aCpuUser);
13112 NOREF(aCpuKernel);
13113 NOREF(aCpuIdle);
13114 NOREF(aMemTotal);
13115 NOREF(aMemFree);
13116 NOREF(aMemBalloon);
13117 NOREF(aMemShared);
13118 NOREF(aMemCache);
13119 NOREF(aPageTotal);
13120 NOREF(aAllocVMM);
13121 NOREF(aFreeVMM);
13122 NOREF(aBalloonedVMM);
13123 NOREF(aSharedVMM);
13124 NOREF(aVmNetRx);
13125 NOREF(aVmNetTx);
13126 return E_NOTIMPL;
13127#endif
13128}
13129
13130////////////////////////////////////////////////////////////////////////////////
13131//
13132// SessionMachine task records
13133//
13134////////////////////////////////////////////////////////////////////////////////
13135
13136/**
13137 * Task record for saving the machine state.
13138 */
13139class SessionMachine::SaveStateTask
13140 : public Machine::Task
13141{
13142public:
13143 SaveStateTask(SessionMachine *m,
13144 Progress *p,
13145 const Utf8Str &t,
13146 Reason_T enmReason,
13147 const Utf8Str &strStateFilePath)
13148 : Task(m, p, t),
13149 m_enmReason(enmReason),
13150 m_strStateFilePath(strStateFilePath)
13151 {}
13152
13153private:
13154 void handler()
13155 {
13156 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13157 }
13158
13159 Reason_T m_enmReason;
13160 Utf8Str m_strStateFilePath;
13161
13162 friend class SessionMachine;
13163};
13164
13165/**
13166 * Task thread implementation for SessionMachine::SaveState(), called from
13167 * SessionMachine::taskHandler().
13168 *
13169 * @note Locks this object for writing.
13170 *
13171 * @param task
13172 * @return
13173 */
13174void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13175{
13176 LogFlowThisFuncEnter();
13177
13178 AutoCaller autoCaller(this);
13179 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13180 if (FAILED(autoCaller.rc()))
13181 {
13182 /* we might have been uninitialized because the session was accidentally
13183 * closed by the client, so don't assert */
13184 HRESULT rc = setError(E_FAIL,
13185 tr("The session has been accidentally closed"));
13186 task.m_pProgress->i_notifyComplete(rc);
13187 LogFlowThisFuncLeave();
13188 return;
13189 }
13190
13191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13192
13193 HRESULT rc = S_OK;
13194
13195 try
13196 {
13197 ComPtr<IInternalSessionControl> directControl;
13198 if (mData->mSession.mLockType == LockType_VM)
13199 directControl = mData->mSession.mDirectControl;
13200 if (directControl.isNull())
13201 throw setError(VBOX_E_INVALID_VM_STATE,
13202 tr("Trying to save state without a running VM"));
13203 alock.release();
13204 BOOL fSuspendedBySave;
13205 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13206 Assert(!fSuspendedBySave);
13207 alock.acquire();
13208
13209 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13210 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13211 throw E_FAIL);
13212
13213 if (SUCCEEDED(rc))
13214 {
13215 mSSData->strStateFilePath = task.m_strStateFilePath;
13216
13217 /* save all VM settings */
13218 rc = i_saveSettings(NULL);
13219 // no need to check whether VirtualBox.xml needs saving also since
13220 // we can't have a name change pending at this point
13221 }
13222 else
13223 {
13224 // On failure, set the state to the state we had at the beginning.
13225 i_setMachineState(task.m_machineStateBackup);
13226 i_updateMachineStateOnClient();
13227
13228 // Delete the saved state file (might have been already created).
13229 // No need to check whether this is shared with a snapshot here
13230 // because we certainly created a fresh saved state file here.
13231 RTFileDelete(task.m_strStateFilePath.c_str());
13232 }
13233 }
13234 catch (HRESULT aRC) { rc = aRC; }
13235
13236 task.m_pProgress->i_notifyComplete(rc);
13237
13238 LogFlowThisFuncLeave();
13239}
13240
13241/**
13242 * @note Locks this object for writing.
13243 */
13244HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13245{
13246 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13247}
13248
13249HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13250{
13251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13252
13253 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13254 if (FAILED(rc)) return rc;
13255
13256 if ( mData->mMachineState != MachineState_Running
13257 && mData->mMachineState != MachineState_Paused
13258 )
13259 return setError(VBOX_E_INVALID_VM_STATE,
13260 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13261 Global::stringifyMachineState(mData->mMachineState));
13262
13263 ComObjPtr<Progress> pProgress;
13264 pProgress.createObject();
13265 rc = pProgress->init(i_getVirtualBox(),
13266 static_cast<IMachine *>(this) /* aInitiator */,
13267 tr("Saving the execution state of the virtual machine"),
13268 FALSE /* aCancelable */);
13269 if (FAILED(rc))
13270 return rc;
13271
13272 Utf8Str strStateFilePath;
13273 i_composeSavedStateFilename(strStateFilePath);
13274
13275 /* create and start the task on a separate thread (note that it will not
13276 * start working until we release alock) */
13277 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13278 rc = pTask->createThread();
13279 if (FAILED(rc))
13280 return rc;
13281
13282 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13283 i_setMachineState(MachineState_Saving);
13284 i_updateMachineStateOnClient();
13285
13286 pProgress.queryInterfaceTo(aProgress.asOutParam());
13287
13288 return S_OK;
13289}
13290
13291/**
13292 * @note Locks this object for writing.
13293 */
13294HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13295{
13296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13297
13298 HRESULT rc = i_checkStateDependency(MutableStateDep);
13299 if (FAILED(rc)) return rc;
13300
13301 if ( mData->mMachineState != MachineState_PoweredOff
13302 && mData->mMachineState != MachineState_Teleported
13303 && mData->mMachineState != MachineState_Aborted
13304 )
13305 return setError(VBOX_E_INVALID_VM_STATE,
13306 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13307 Global::stringifyMachineState(mData->mMachineState));
13308
13309 com::Utf8Str stateFilePathFull;
13310 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13311 if (RT_FAILURE(vrc))
13312 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13313 tr("Invalid saved state file path '%s' (%Rrc)"),
13314 aSavedStateFile.c_str(),
13315 vrc);
13316
13317 mSSData->strStateFilePath = stateFilePathFull;
13318
13319 /* The below i_setMachineState() will detect the state transition and will
13320 * update the settings file */
13321
13322 return i_setMachineState(MachineState_Saved);
13323}
13324
13325/**
13326 * @note Locks this object for writing.
13327 */
13328HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13329{
13330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13331
13332 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13333 if (FAILED(rc)) return rc;
13334
13335 if (mData->mMachineState != MachineState_Saved)
13336 return setError(VBOX_E_INVALID_VM_STATE,
13337 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13338 Global::stringifyMachineState(mData->mMachineState));
13339
13340 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13341
13342 /*
13343 * Saved -> PoweredOff transition will be detected in the SessionMachine
13344 * and properly handled.
13345 */
13346 rc = i_setMachineState(MachineState_PoweredOff);
13347 return rc;
13348}
13349
13350
13351/**
13352 * @note Locks the same as #i_setMachineState() does.
13353 */
13354HRESULT SessionMachine::updateState(MachineState_T aState)
13355{
13356 return i_setMachineState(aState);
13357}
13358
13359/**
13360 * @note Locks this object for writing.
13361 */
13362HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13363{
13364 IProgress *pProgress(aProgress);
13365
13366 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13367
13368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13369
13370 if (mData->mSession.mState != SessionState_Locked)
13371 return VBOX_E_INVALID_OBJECT_STATE;
13372
13373 if (!mData->mSession.mProgress.isNull())
13374 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13375
13376 /* If we didn't reference the NAT network service yet, add a reference to
13377 * force a start */
13378 if (miNATNetworksStarted < 1)
13379 {
13380 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13381 {
13382 BOOL enabled;
13383 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13384 if ( FAILED(hrc)
13385 || !enabled)
13386 continue;
13387
13388 NetworkAttachmentType_T type;
13389 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13390 if ( SUCCEEDED(hrc)
13391 && type == NetworkAttachmentType_NATNetwork)
13392 {
13393 Bstr name;
13394 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13395 if (SUCCEEDED(hrc))
13396 {
13397 Utf8Str strName(name);
13398 LogRel(("VM '%s' starts using NAT network '%s'\n",
13399 mUserData->s.strName.c_str(), strName.c_str()));
13400 mPeer->lockHandle()->unlockWrite();
13401 mParent->i_natNetworkRefInc(strName);
13402#ifdef RT_LOCK_STRICT
13403 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13404#else
13405 mPeer->lockHandle()->lockWrite();
13406#endif
13407 }
13408 }
13409 }
13410 miNATNetworksStarted++;
13411 }
13412
13413 LogFlowThisFunc(("returns S_OK.\n"));
13414 return S_OK;
13415}
13416
13417/**
13418 * @note Locks this object for writing.
13419 */
13420HRESULT SessionMachine::endPowerUp(LONG aResult)
13421{
13422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13423
13424 if (mData->mSession.mState != SessionState_Locked)
13425 return VBOX_E_INVALID_OBJECT_STATE;
13426
13427 /* Finalize the LaunchVMProcess progress object. */
13428 if (mData->mSession.mProgress)
13429 {
13430 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13431 mData->mSession.mProgress.setNull();
13432 }
13433
13434 if (SUCCEEDED((HRESULT)aResult))
13435 {
13436#ifdef VBOX_WITH_RESOURCE_USAGE_API
13437 /* The VM has been powered up successfully, so it makes sense
13438 * now to offer the performance metrics for a running machine
13439 * object. Doing it earlier wouldn't be safe. */
13440 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13441 mData->mSession.mPID);
13442#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13443 }
13444
13445 return S_OK;
13446}
13447
13448/**
13449 * @note Locks this object for writing.
13450 */
13451HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13452{
13453 LogFlowThisFuncEnter();
13454
13455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13456
13457 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13458 E_FAIL);
13459
13460 /* create a progress object to track operation completion */
13461 ComObjPtr<Progress> pProgress;
13462 pProgress.createObject();
13463 pProgress->init(i_getVirtualBox(),
13464 static_cast<IMachine *>(this) /* aInitiator */,
13465 tr("Stopping the virtual machine"),
13466 FALSE /* aCancelable */);
13467
13468 /* fill in the console task data */
13469 mConsoleTaskData.mLastState = mData->mMachineState;
13470 mConsoleTaskData.mProgress = pProgress;
13471
13472 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13473 i_setMachineState(MachineState_Stopping);
13474
13475 pProgress.queryInterfaceTo(aProgress.asOutParam());
13476
13477 return S_OK;
13478}
13479
13480/**
13481 * @note Locks this object for writing.
13482 */
13483HRESULT SessionMachine::endPoweringDown(LONG aResult,
13484 const com::Utf8Str &aErrMsg)
13485{
13486 LogFlowThisFuncEnter();
13487
13488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13489
13490 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13491 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13492 && mConsoleTaskData.mLastState != MachineState_Null,
13493 E_FAIL);
13494
13495 /*
13496 * On failure, set the state to the state we had when BeginPoweringDown()
13497 * was called (this is expected by Console::PowerDown() and the associated
13498 * task). On success the VM process already changed the state to
13499 * MachineState_PoweredOff, so no need to do anything.
13500 */
13501 if (FAILED(aResult))
13502 i_setMachineState(mConsoleTaskData.mLastState);
13503
13504 /* notify the progress object about operation completion */
13505 Assert(mConsoleTaskData.mProgress);
13506 if (SUCCEEDED(aResult))
13507 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13508 else
13509 {
13510 if (aErrMsg.length())
13511 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13512 COM_IIDOF(ISession),
13513 getComponentName(),
13514 aErrMsg.c_str());
13515 else
13516 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13517 }
13518
13519 /* clear out the temporary saved state data */
13520 mConsoleTaskData.mLastState = MachineState_Null;
13521 mConsoleTaskData.mProgress.setNull();
13522
13523 LogFlowThisFuncLeave();
13524 return S_OK;
13525}
13526
13527
13528/**
13529 * Goes through the USB filters of the given machine to see if the given
13530 * device matches any filter or not.
13531 *
13532 * @note Locks the same as USBController::hasMatchingFilter() does.
13533 */
13534HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13535 BOOL *aMatched,
13536 ULONG *aMaskedInterfaces)
13537{
13538 LogFlowThisFunc(("\n"));
13539
13540#ifdef VBOX_WITH_USB
13541 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13542#else
13543 NOREF(aDevice);
13544 NOREF(aMaskedInterfaces);
13545 *aMatched = FALSE;
13546#endif
13547
13548 return S_OK;
13549}
13550
13551/**
13552 * @note Locks the same as Host::captureUSBDevice() does.
13553 */
13554HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13555{
13556 LogFlowThisFunc(("\n"));
13557
13558#ifdef VBOX_WITH_USB
13559 /* if captureDeviceForVM() fails, it must have set extended error info */
13560 clearError();
13561 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13562 if (FAILED(rc)) return rc;
13563
13564 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13565 AssertReturn(service, E_FAIL);
13566 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13567#else
13568 NOREF(aId);
13569 return E_NOTIMPL;
13570#endif
13571}
13572
13573/**
13574 * @note Locks the same as Host::detachUSBDevice() does.
13575 */
13576HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13577 BOOL aDone)
13578{
13579 LogFlowThisFunc(("\n"));
13580
13581#ifdef VBOX_WITH_USB
13582 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13583 AssertReturn(service, E_FAIL);
13584 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13585#else
13586 NOREF(aId);
13587 NOREF(aDone);
13588 return E_NOTIMPL;
13589#endif
13590}
13591
13592/**
13593 * Inserts all machine filters to the USB proxy service and then calls
13594 * Host::autoCaptureUSBDevices().
13595 *
13596 * Called by Console from the VM process upon VM startup.
13597 *
13598 * @note Locks what called methods lock.
13599 */
13600HRESULT SessionMachine::autoCaptureUSBDevices()
13601{
13602 LogFlowThisFunc(("\n"));
13603
13604#ifdef VBOX_WITH_USB
13605 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13606 AssertComRC(rc);
13607 NOREF(rc);
13608
13609 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13610 AssertReturn(service, E_FAIL);
13611 return service->autoCaptureDevicesForVM(this);
13612#else
13613 return S_OK;
13614#endif
13615}
13616
13617/**
13618 * Removes all machine filters from the USB proxy service and then calls
13619 * Host::detachAllUSBDevices().
13620 *
13621 * Called by Console from the VM process upon normal VM termination or by
13622 * SessionMachine::uninit() upon abnormal VM termination (from under the
13623 * Machine/SessionMachine lock).
13624 *
13625 * @note Locks what called methods lock.
13626 */
13627HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13628{
13629 LogFlowThisFunc(("\n"));
13630
13631#ifdef VBOX_WITH_USB
13632 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13633 AssertComRC(rc);
13634 NOREF(rc);
13635
13636 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13637 AssertReturn(service, E_FAIL);
13638 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13639#else
13640 NOREF(aDone);
13641 return S_OK;
13642#endif
13643}
13644
13645/**
13646 * @note Locks this object for writing.
13647 */
13648HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13649 ComPtr<IProgress> &aProgress)
13650{
13651 LogFlowThisFuncEnter();
13652
13653 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13654 /*
13655 * We don't assert below because it might happen that a non-direct session
13656 * informs us it is closed right after we've been uninitialized -- it's ok.
13657 */
13658
13659 /* get IInternalSessionControl interface */
13660 ComPtr<IInternalSessionControl> control(aSession);
13661
13662 ComAssertRet(!control.isNull(), E_INVALIDARG);
13663
13664 /* Creating a Progress object requires the VirtualBox lock, and
13665 * thus locking it here is required by the lock order rules. */
13666 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13667
13668 if (control == mData->mSession.mDirectControl)
13669 {
13670 /* The direct session is being normally closed by the client process
13671 * ----------------------------------------------------------------- */
13672
13673 /* go to the closing state (essential for all open*Session() calls and
13674 * for #i_checkForDeath()) */
13675 Assert(mData->mSession.mState == SessionState_Locked);
13676 mData->mSession.mState = SessionState_Unlocking;
13677
13678 /* set direct control to NULL to release the remote instance */
13679 mData->mSession.mDirectControl.setNull();
13680 LogFlowThisFunc(("Direct control is set to NULL\n"));
13681
13682 if (mData->mSession.mProgress)
13683 {
13684 /* finalize the progress, someone might wait if a frontend
13685 * closes the session before powering on the VM. */
13686 mData->mSession.mProgress->notifyComplete(E_FAIL,
13687 COM_IIDOF(ISession),
13688 getComponentName(),
13689 tr("The VM session was closed before any attempt to power it on"));
13690 mData->mSession.mProgress.setNull();
13691 }
13692
13693 /* Create the progress object the client will use to wait until
13694 * #i_checkForDeath() is called to uninitialize this session object after
13695 * it releases the IPC semaphore.
13696 * Note! Because we're "reusing" mProgress here, this must be a proxy
13697 * object just like for LaunchVMProcess. */
13698 Assert(mData->mSession.mProgress.isNull());
13699 ComObjPtr<ProgressProxy> progress;
13700 progress.createObject();
13701 ComPtr<IUnknown> pPeer(mPeer);
13702 progress->init(mParent, pPeer,
13703 Bstr(tr("Closing session")).raw(),
13704 FALSE /* aCancelable */);
13705 progress.queryInterfaceTo(aProgress.asOutParam());
13706 mData->mSession.mProgress = progress;
13707 }
13708 else
13709 {
13710 /* the remote session is being normally closed */
13711 bool found = false;
13712 for (Data::Session::RemoteControlList::iterator
13713 it = mData->mSession.mRemoteControls.begin();
13714 it != mData->mSession.mRemoteControls.end();
13715 ++it)
13716 {
13717 if (control == *it)
13718 {
13719 found = true;
13720 // This MUST be erase(it), not remove(*it) as the latter
13721 // triggers a very nasty use after free due to the place where
13722 // the value "lives".
13723 mData->mSession.mRemoteControls.erase(it);
13724 break;
13725 }
13726 }
13727 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13728 E_INVALIDARG);
13729 }
13730
13731 /* signal the client watcher thread, because the client is going away */
13732 mParent->i_updateClientWatcher();
13733
13734 LogFlowThisFuncLeave();
13735 return S_OK;
13736}
13737
13738HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13739 std::vector<com::Utf8Str> &aValues,
13740 std::vector<LONG64> &aTimestamps,
13741 std::vector<com::Utf8Str> &aFlags)
13742{
13743 LogFlowThisFunc(("\n"));
13744
13745#ifdef VBOX_WITH_GUEST_PROPS
13746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13747
13748 size_t cEntries = mHWData->mGuestProperties.size();
13749 aNames.resize(cEntries);
13750 aValues.resize(cEntries);
13751 aTimestamps.resize(cEntries);
13752 aFlags.resize(cEntries);
13753
13754 size_t i = 0;
13755 for (HWData::GuestPropertyMap::const_iterator
13756 it = mHWData->mGuestProperties.begin();
13757 it != mHWData->mGuestProperties.end();
13758 ++it, ++i)
13759 {
13760 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13761 aNames[i] = it->first;
13762 aValues[i] = it->second.strValue;
13763 aTimestamps[i] = it->second.mTimestamp;
13764
13765 /* If it is NULL, keep it NULL. */
13766 if (it->second.mFlags)
13767 {
13768 GuestPropWriteFlags(it->second.mFlags, szFlags);
13769 aFlags[i] = szFlags;
13770 }
13771 else
13772 aFlags[i] = "";
13773 }
13774 return S_OK;
13775#else
13776 ReturnComNotImplemented();
13777#endif
13778}
13779
13780HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13781 const com::Utf8Str &aValue,
13782 LONG64 aTimestamp,
13783 const com::Utf8Str &aFlags)
13784{
13785 LogFlowThisFunc(("\n"));
13786
13787#ifdef VBOX_WITH_GUEST_PROPS
13788 try
13789 {
13790 /*
13791 * Convert input up front.
13792 */
13793 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13794 if (aFlags.length())
13795 {
13796 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13797 AssertRCReturn(vrc, E_INVALIDARG);
13798 }
13799
13800 /*
13801 * Now grab the object lock, validate the state and do the update.
13802 */
13803
13804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13805
13806 if (!Global::IsOnline(mData->mMachineState))
13807 {
13808 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13809 VBOX_E_INVALID_VM_STATE);
13810 }
13811
13812 i_setModified(IsModified_MachineData);
13813 mHWData.backup();
13814
13815 bool fDelete = !aValue.length();
13816 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13817 if (it != mHWData->mGuestProperties.end())
13818 {
13819 if (!fDelete)
13820 {
13821 it->second.strValue = aValue;
13822 it->second.mTimestamp = aTimestamp;
13823 it->second.mFlags = fFlags;
13824 }
13825 else
13826 mHWData->mGuestProperties.erase(it);
13827
13828 mData->mGuestPropertiesModified = TRUE;
13829 }
13830 else if (!fDelete)
13831 {
13832 HWData::GuestProperty prop;
13833 prop.strValue = aValue;
13834 prop.mTimestamp = aTimestamp;
13835 prop.mFlags = fFlags;
13836
13837 mHWData->mGuestProperties[aName] = prop;
13838 mData->mGuestPropertiesModified = TRUE;
13839 }
13840
13841 alock.release();
13842
13843 mParent->i_onGuestPropertyChange(mData->mUuid,
13844 Bstr(aName).raw(),
13845 Bstr(aValue).raw(),
13846 Bstr(aFlags).raw());
13847 }
13848 catch (...)
13849 {
13850 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13851 }
13852 return S_OK;
13853#else
13854 ReturnComNotImplemented();
13855#endif
13856}
13857
13858
13859HRESULT SessionMachine::lockMedia()
13860{
13861 AutoMultiWriteLock2 alock(this->lockHandle(),
13862 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13863
13864 AssertReturn( mData->mMachineState == MachineState_Starting
13865 || mData->mMachineState == MachineState_Restoring
13866 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13867
13868 clearError();
13869 alock.release();
13870 return i_lockMedia();
13871}
13872
13873HRESULT SessionMachine::unlockMedia()
13874{
13875 HRESULT hrc = i_unlockMedia();
13876 return hrc;
13877}
13878
13879HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13880 ComPtr<IMediumAttachment> &aNewAttachment)
13881{
13882 // request the host lock first, since might be calling Host methods for getting host drives;
13883 // next, protect the media tree all the while we're in here, as well as our member variables
13884 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13885 this->lockHandle(),
13886 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13887
13888 IMediumAttachment *iAttach = aAttachment;
13889 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13890
13891 Utf8Str ctrlName;
13892 LONG lPort;
13893 LONG lDevice;
13894 bool fTempEject;
13895 {
13896 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13897
13898 /* Need to query the details first, as the IMediumAttachment reference
13899 * might be to the original settings, which we are going to change. */
13900 ctrlName = pAttach->i_getControllerName();
13901 lPort = pAttach->i_getPort();
13902 lDevice = pAttach->i_getDevice();
13903 fTempEject = pAttach->i_getTempEject();
13904 }
13905
13906 if (!fTempEject)
13907 {
13908 /* Remember previously mounted medium. The medium before taking the
13909 * backup is not necessarily the same thing. */
13910 ComObjPtr<Medium> oldmedium;
13911 oldmedium = pAttach->i_getMedium();
13912
13913 i_setModified(IsModified_Storage);
13914 mMediumAttachments.backup();
13915
13916 // The backup operation makes the pAttach reference point to the
13917 // old settings. Re-get the correct reference.
13918 pAttach = i_findAttachment(*mMediumAttachments.data(),
13919 ctrlName,
13920 lPort,
13921 lDevice);
13922
13923 {
13924 AutoCaller autoAttachCaller(this);
13925 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13926
13927 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13928 if (!oldmedium.isNull())
13929 oldmedium->i_removeBackReference(mData->mUuid);
13930
13931 pAttach->i_updateMedium(NULL);
13932 pAttach->i_updateEjected();
13933 }
13934
13935 i_setModified(IsModified_Storage);
13936 }
13937 else
13938 {
13939 {
13940 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13941 pAttach->i_updateEjected();
13942 }
13943 }
13944
13945 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13946
13947 return S_OK;
13948}
13949
13950HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13951 com::Utf8Str &aResult)
13952{
13953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13954
13955 HRESULT hr = S_OK;
13956
13957 if (!mAuthLibCtx.hAuthLibrary)
13958 {
13959 /* Load the external authentication library. */
13960 Bstr authLibrary;
13961 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13962
13963 Utf8Str filename = authLibrary;
13964
13965 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13966 if (RT_FAILURE(vrc))
13967 hr = setErrorBoth(E_FAIL, vrc,
13968 tr("Could not load the external authentication library '%s' (%Rrc)"),
13969 filename.c_str(), vrc);
13970 }
13971
13972 /* The auth library might need the machine lock. */
13973 alock.release();
13974
13975 if (FAILED(hr))
13976 return hr;
13977
13978 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13979 {
13980 enum VRDEAuthParams
13981 {
13982 parmUuid = 1,
13983 parmGuestJudgement,
13984 parmUser,
13985 parmPassword,
13986 parmDomain,
13987 parmClientId
13988 };
13989
13990 AuthResult result = AuthResultAccessDenied;
13991
13992 Guid uuid(aAuthParams[parmUuid]);
13993 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13994 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13995
13996 result = AuthLibAuthenticate(&mAuthLibCtx,
13997 uuid.raw(), guestJudgement,
13998 aAuthParams[parmUser].c_str(),
13999 aAuthParams[parmPassword].c_str(),
14000 aAuthParams[parmDomain].c_str(),
14001 u32ClientId);
14002
14003 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14004 size_t cbPassword = aAuthParams[parmPassword].length();
14005 if (cbPassword)
14006 {
14007 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14008 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14009 }
14010
14011 if (result == AuthResultAccessGranted)
14012 aResult = "granted";
14013 else
14014 aResult = "denied";
14015
14016 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14017 aAuthParams[parmUser].c_str(), aResult.c_str()));
14018 }
14019 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14020 {
14021 enum VRDEAuthDisconnectParams
14022 {
14023 parmUuid = 1,
14024 parmClientId
14025 };
14026
14027 Guid uuid(aAuthParams[parmUuid]);
14028 uint32_t u32ClientId = 0;
14029 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14030 }
14031 else
14032 {
14033 hr = E_INVALIDARG;
14034 }
14035
14036 return hr;
14037}
14038
14039// public methods only for internal purposes
14040/////////////////////////////////////////////////////////////////////////////
14041
14042#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14043/**
14044 * Called from the client watcher thread to check for expected or unexpected
14045 * death of the client process that has a direct session to this machine.
14046 *
14047 * On Win32 and on OS/2, this method is called only when we've got the
14048 * mutex (i.e. the client has either died or terminated normally) so it always
14049 * returns @c true (the client is terminated, the session machine is
14050 * uninitialized).
14051 *
14052 * On other platforms, the method returns @c true if the client process has
14053 * terminated normally or abnormally and the session machine was uninitialized,
14054 * and @c false if the client process is still alive.
14055 *
14056 * @note Locks this object for writing.
14057 */
14058bool SessionMachine::i_checkForDeath()
14059{
14060 Uninit::Reason reason;
14061 bool terminated = false;
14062
14063 /* Enclose autoCaller with a block because calling uninit() from under it
14064 * will deadlock. */
14065 {
14066 AutoCaller autoCaller(this);
14067 if (!autoCaller.isOk())
14068 {
14069 /* return true if not ready, to cause the client watcher to exclude
14070 * the corresponding session from watching */
14071 LogFlowThisFunc(("Already uninitialized!\n"));
14072 return true;
14073 }
14074
14075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14076
14077 /* Determine the reason of death: if the session state is Closing here,
14078 * everything is fine. Otherwise it means that the client did not call
14079 * OnSessionEnd() before it released the IPC semaphore. This may happen
14080 * either because the client process has abnormally terminated, or
14081 * because it simply forgot to call ISession::Close() before exiting. We
14082 * threat the latter also as an abnormal termination (see
14083 * Session::uninit() for details). */
14084 reason = mData->mSession.mState == SessionState_Unlocking ?
14085 Uninit::Normal :
14086 Uninit::Abnormal;
14087
14088 if (mClientToken)
14089 terminated = mClientToken->release();
14090 } /* AutoCaller block */
14091
14092 if (terminated)
14093 uninit(reason);
14094
14095 return terminated;
14096}
14097
14098void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14099{
14100 LogFlowThisFunc(("\n"));
14101
14102 strTokenId.setNull();
14103
14104 AutoCaller autoCaller(this);
14105 AssertComRCReturnVoid(autoCaller.rc());
14106
14107 Assert(mClientToken);
14108 if (mClientToken)
14109 mClientToken->getId(strTokenId);
14110}
14111#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14112IToken *SessionMachine::i_getToken()
14113{
14114 LogFlowThisFunc(("\n"));
14115
14116 AutoCaller autoCaller(this);
14117 AssertComRCReturn(autoCaller.rc(), NULL);
14118
14119 Assert(mClientToken);
14120 if (mClientToken)
14121 return mClientToken->getToken();
14122 else
14123 return NULL;
14124}
14125#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14126
14127Machine::ClientToken *SessionMachine::i_getClientToken()
14128{
14129 LogFlowThisFunc(("\n"));
14130
14131 AutoCaller autoCaller(this);
14132 AssertComRCReturn(autoCaller.rc(), NULL);
14133
14134 return mClientToken;
14135}
14136
14137
14138/**
14139 * @note Locks this object for reading.
14140 */
14141HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14142{
14143 LogFlowThisFunc(("\n"));
14144
14145 AutoCaller autoCaller(this);
14146 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14147
14148 ComPtr<IInternalSessionControl> directControl;
14149 {
14150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14151 if (mData->mSession.mLockType == LockType_VM)
14152 directControl = mData->mSession.mDirectControl;
14153 }
14154
14155 /* ignore notifications sent after #OnSessionEnd() is called */
14156 if (!directControl)
14157 return S_OK;
14158
14159 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14160}
14161
14162/**
14163 * @note Locks this object for reading.
14164 */
14165HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14166 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14167 IN_BSTR aGuestIp, LONG aGuestPort)
14168{
14169 LogFlowThisFunc(("\n"));
14170
14171 AutoCaller autoCaller(this);
14172 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14173
14174 ComPtr<IInternalSessionControl> directControl;
14175 {
14176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14177 if (mData->mSession.mLockType == LockType_VM)
14178 directControl = mData->mSession.mDirectControl;
14179 }
14180
14181 /* ignore notifications sent after #OnSessionEnd() is called */
14182 if (!directControl)
14183 return S_OK;
14184 /*
14185 * instead acting like callback we ask IVirtualBox deliver corresponding event
14186 */
14187
14188 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14189 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14190 return S_OK;
14191}
14192
14193/**
14194 * @note Locks this object for reading.
14195 */
14196HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14197{
14198 LogFlowThisFunc(("\n"));
14199
14200 AutoCaller autoCaller(this);
14201 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14202
14203 ComPtr<IInternalSessionControl> directControl;
14204 {
14205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14206 if (mData->mSession.mLockType == LockType_VM)
14207 directControl = mData->mSession.mDirectControl;
14208 }
14209
14210 /* ignore notifications sent after #OnSessionEnd() is called */
14211 if (!directControl)
14212 return S_OK;
14213
14214 return directControl->OnAudioAdapterChange(audioAdapter);
14215}
14216
14217/**
14218 * @note Locks this object for reading.
14219 */
14220HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14221{
14222 LogFlowThisFunc(("\n"));
14223
14224 AutoCaller autoCaller(this);
14225 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14226
14227 ComPtr<IInternalSessionControl> directControl;
14228 {
14229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14230 if (mData->mSession.mLockType == LockType_VM)
14231 directControl = mData->mSession.mDirectControl;
14232 }
14233
14234 /* ignore notifications sent after #OnSessionEnd() is called */
14235 if (!directControl)
14236 return S_OK;
14237
14238 return directControl->OnSerialPortChange(serialPort);
14239}
14240
14241/**
14242 * @note Locks this object for reading.
14243 */
14244HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14245{
14246 LogFlowThisFunc(("\n"));
14247
14248 AutoCaller autoCaller(this);
14249 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14250
14251 ComPtr<IInternalSessionControl> directControl;
14252 {
14253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14254 if (mData->mSession.mLockType == LockType_VM)
14255 directControl = mData->mSession.mDirectControl;
14256 }
14257
14258 /* ignore notifications sent after #OnSessionEnd() is called */
14259 if (!directControl)
14260 return S_OK;
14261
14262 return directControl->OnParallelPortChange(parallelPort);
14263}
14264
14265/**
14266 * @note Locks this object for reading.
14267 */
14268HRESULT SessionMachine::i_onStorageControllerChange()
14269{
14270 LogFlowThisFunc(("\n"));
14271
14272 AutoCaller autoCaller(this);
14273 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14274
14275 ComPtr<IInternalSessionControl> directControl;
14276 {
14277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14278 if (mData->mSession.mLockType == LockType_VM)
14279 directControl = mData->mSession.mDirectControl;
14280 }
14281
14282 /* ignore notifications sent after #OnSessionEnd() is called */
14283 if (!directControl)
14284 return S_OK;
14285
14286 return directControl->OnStorageControllerChange();
14287}
14288
14289/**
14290 * @note Locks this object for reading.
14291 */
14292HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14293{
14294 LogFlowThisFunc(("\n"));
14295
14296 AutoCaller autoCaller(this);
14297 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14298
14299 ComPtr<IInternalSessionControl> directControl;
14300 {
14301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14302 if (mData->mSession.mLockType == LockType_VM)
14303 directControl = mData->mSession.mDirectControl;
14304 }
14305
14306 /* ignore notifications sent after #OnSessionEnd() is called */
14307 if (!directControl)
14308 return S_OK;
14309
14310 return directControl->OnMediumChange(aAttachment, aForce);
14311}
14312
14313/**
14314 * @note Locks this object for reading.
14315 */
14316HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14317{
14318 LogFlowThisFunc(("\n"));
14319
14320 AutoCaller autoCaller(this);
14321 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14322
14323 ComPtr<IInternalSessionControl> directControl;
14324 {
14325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14326 if (mData->mSession.mLockType == LockType_VM)
14327 directControl = mData->mSession.mDirectControl;
14328 }
14329
14330 /* ignore notifications sent after #OnSessionEnd() is called */
14331 if (!directControl)
14332 return S_OK;
14333
14334 return directControl->OnCPUChange(aCPU, aRemove);
14335}
14336
14337HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14338{
14339 LogFlowThisFunc(("\n"));
14340
14341 AutoCaller autoCaller(this);
14342 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14343
14344 ComPtr<IInternalSessionControl> directControl;
14345 {
14346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14347 if (mData->mSession.mLockType == LockType_VM)
14348 directControl = mData->mSession.mDirectControl;
14349 }
14350
14351 /* ignore notifications sent after #OnSessionEnd() is called */
14352 if (!directControl)
14353 return S_OK;
14354
14355 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14356}
14357
14358/**
14359 * @note Locks this object for reading.
14360 */
14361HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14362{
14363 LogFlowThisFunc(("\n"));
14364
14365 AutoCaller autoCaller(this);
14366 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14367
14368 ComPtr<IInternalSessionControl> directControl;
14369 {
14370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14371 if (mData->mSession.mLockType == LockType_VM)
14372 directControl = mData->mSession.mDirectControl;
14373 }
14374
14375 /* ignore notifications sent after #OnSessionEnd() is called */
14376 if (!directControl)
14377 return S_OK;
14378
14379 return directControl->OnVRDEServerChange(aRestart);
14380}
14381
14382/**
14383 * @note Locks this object for reading.
14384 */
14385HRESULT SessionMachine::i_onVideoCaptureChange()
14386{
14387 LogFlowThisFunc(("\n"));
14388
14389 AutoCaller autoCaller(this);
14390 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14391
14392 ComPtr<IInternalSessionControl> directControl;
14393 {
14394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14395 if (mData->mSession.mLockType == LockType_VM)
14396 directControl = mData->mSession.mDirectControl;
14397 }
14398
14399 /* ignore notifications sent after #OnSessionEnd() is called */
14400 if (!directControl)
14401 return S_OK;
14402
14403 return directControl->OnVideoCaptureChange();
14404}
14405
14406/**
14407 * @note Locks this object for reading.
14408 */
14409HRESULT SessionMachine::i_onUSBControllerChange()
14410{
14411 LogFlowThisFunc(("\n"));
14412
14413 AutoCaller autoCaller(this);
14414 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14415
14416 ComPtr<IInternalSessionControl> directControl;
14417 {
14418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14419 if (mData->mSession.mLockType == LockType_VM)
14420 directControl = mData->mSession.mDirectControl;
14421 }
14422
14423 /* ignore notifications sent after #OnSessionEnd() is called */
14424 if (!directControl)
14425 return S_OK;
14426
14427 return directControl->OnUSBControllerChange();
14428}
14429
14430/**
14431 * @note Locks this object for reading.
14432 */
14433HRESULT SessionMachine::i_onSharedFolderChange()
14434{
14435 LogFlowThisFunc(("\n"));
14436
14437 AutoCaller autoCaller(this);
14438 AssertComRCReturnRC(autoCaller.rc());
14439
14440 ComPtr<IInternalSessionControl> directControl;
14441 {
14442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14443 if (mData->mSession.mLockType == LockType_VM)
14444 directControl = mData->mSession.mDirectControl;
14445 }
14446
14447 /* ignore notifications sent after #OnSessionEnd() is called */
14448 if (!directControl)
14449 return S_OK;
14450
14451 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14452}
14453
14454/**
14455 * @note Locks this object for reading.
14456 */
14457HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14458{
14459 LogFlowThisFunc(("\n"));
14460
14461 AutoCaller autoCaller(this);
14462 AssertComRCReturnRC(autoCaller.rc());
14463
14464 ComPtr<IInternalSessionControl> directControl;
14465 {
14466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14467 if (mData->mSession.mLockType == LockType_VM)
14468 directControl = mData->mSession.mDirectControl;
14469 }
14470
14471 /* ignore notifications sent after #OnSessionEnd() is called */
14472 if (!directControl)
14473 return S_OK;
14474
14475 return directControl->OnClipboardModeChange(aClipboardMode);
14476}
14477
14478/**
14479 * @note Locks this object for reading.
14480 */
14481HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14482{
14483 LogFlowThisFunc(("\n"));
14484
14485 AutoCaller autoCaller(this);
14486 AssertComRCReturnRC(autoCaller.rc());
14487
14488 ComPtr<IInternalSessionControl> directControl;
14489 {
14490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14491 if (mData->mSession.mLockType == LockType_VM)
14492 directControl = mData->mSession.mDirectControl;
14493 }
14494
14495 /* ignore notifications sent after #OnSessionEnd() is called */
14496 if (!directControl)
14497 return S_OK;
14498
14499 return directControl->OnDnDModeChange(aDnDMode);
14500}
14501
14502/**
14503 * @note Locks this object for reading.
14504 */
14505HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14506{
14507 LogFlowThisFunc(("\n"));
14508
14509 AutoCaller autoCaller(this);
14510 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14511
14512 ComPtr<IInternalSessionControl> directControl;
14513 {
14514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14515 if (mData->mSession.mLockType == LockType_VM)
14516 directControl = mData->mSession.mDirectControl;
14517 }
14518
14519 /* ignore notifications sent after #OnSessionEnd() is called */
14520 if (!directControl)
14521 return S_OK;
14522
14523 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14524}
14525
14526/**
14527 * @note Locks this object for reading.
14528 */
14529HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14530{
14531 LogFlowThisFunc(("\n"));
14532
14533 AutoCaller autoCaller(this);
14534 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14535
14536 ComPtr<IInternalSessionControl> directControl;
14537 {
14538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14539 if (mData->mSession.mLockType == LockType_VM)
14540 directControl = mData->mSession.mDirectControl;
14541 }
14542
14543 /* ignore notifications sent after #OnSessionEnd() is called */
14544 if (!directControl)
14545 return S_OK;
14546
14547 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14548}
14549
14550/**
14551 * Returns @c true if this machine's USB controller reports it has a matching
14552 * filter for the given USB device and @c false otherwise.
14553 *
14554 * @note locks this object for reading.
14555 */
14556bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14557{
14558 AutoCaller autoCaller(this);
14559 /* silently return if not ready -- this method may be called after the
14560 * direct machine session has been called */
14561 if (!autoCaller.isOk())
14562 return false;
14563
14564#ifdef VBOX_WITH_USB
14565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14566
14567 switch (mData->mMachineState)
14568 {
14569 case MachineState_Starting:
14570 case MachineState_Restoring:
14571 case MachineState_TeleportingIn:
14572 case MachineState_Paused:
14573 case MachineState_Running:
14574 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14575 * elsewhere... */
14576 alock.release();
14577 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14578 default: break;
14579 }
14580#else
14581 NOREF(aDevice);
14582 NOREF(aMaskedIfs);
14583#endif
14584 return false;
14585}
14586
14587/**
14588 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14589 */
14590HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14591 IVirtualBoxErrorInfo *aError,
14592 ULONG aMaskedIfs,
14593 const com::Utf8Str &aCaptureFilename)
14594{
14595 LogFlowThisFunc(("\n"));
14596
14597 AutoCaller autoCaller(this);
14598
14599 /* This notification may happen after the machine object has been
14600 * uninitialized (the session was closed), so don't assert. */
14601 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14602
14603 ComPtr<IInternalSessionControl> directControl;
14604 {
14605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14606 if (mData->mSession.mLockType == LockType_VM)
14607 directControl = mData->mSession.mDirectControl;
14608 }
14609
14610 /* fail on notifications sent after #OnSessionEnd() is called, it is
14611 * expected by the caller */
14612 if (!directControl)
14613 return E_FAIL;
14614
14615 /* No locks should be held at this point. */
14616 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14617 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14618
14619 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14620}
14621
14622/**
14623 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14624 */
14625HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14626 IVirtualBoxErrorInfo *aError)
14627{
14628 LogFlowThisFunc(("\n"));
14629
14630 AutoCaller autoCaller(this);
14631
14632 /* This notification may happen after the machine object has been
14633 * uninitialized (the session was closed), so don't assert. */
14634 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14635
14636 ComPtr<IInternalSessionControl> directControl;
14637 {
14638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14639 if (mData->mSession.mLockType == LockType_VM)
14640 directControl = mData->mSession.mDirectControl;
14641 }
14642
14643 /* fail on notifications sent after #OnSessionEnd() is called, it is
14644 * expected by the caller */
14645 if (!directControl)
14646 return E_FAIL;
14647
14648 /* No locks should be held at this point. */
14649 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14650 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14651
14652 return directControl->OnUSBDeviceDetach(aId, aError);
14653}
14654
14655// protected methods
14656/////////////////////////////////////////////////////////////////////////////
14657
14658/**
14659 * Deletes the given file if it is no longer in use by either the current machine state
14660 * (if the machine is "saved") or any of the machine's snapshots.
14661 *
14662 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14663 * but is different for each SnapshotMachine. When calling this, the order of calling this
14664 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14665 * is therefore critical. I know, it's all rather messy.
14666 *
14667 * @param strStateFile
14668 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14669 * the test for whether the saved state file is in use.
14670 */
14671void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14672 Snapshot *pSnapshotToIgnore)
14673{
14674 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14675 if ( (strStateFile.isNotEmpty())
14676 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14677 )
14678 // ... and it must also not be shared with other snapshots
14679 if ( !mData->mFirstSnapshot
14680 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14681 // this checks the SnapshotMachine's state file paths
14682 )
14683 RTFileDelete(strStateFile.c_str());
14684}
14685
14686/**
14687 * Locks the attached media.
14688 *
14689 * All attached hard disks are locked for writing and DVD/floppy are locked for
14690 * reading. Parents of attached hard disks (if any) are locked for reading.
14691 *
14692 * This method also performs accessibility check of all media it locks: if some
14693 * media is inaccessible, the method will return a failure and a bunch of
14694 * extended error info objects per each inaccessible medium.
14695 *
14696 * Note that this method is atomic: if it returns a success, all media are
14697 * locked as described above; on failure no media is locked at all (all
14698 * succeeded individual locks will be undone).
14699 *
14700 * The caller is responsible for doing the necessary state sanity checks.
14701 *
14702 * The locks made by this method must be undone by calling #unlockMedia() when
14703 * no more needed.
14704 */
14705HRESULT SessionMachine::i_lockMedia()
14706{
14707 AutoCaller autoCaller(this);
14708 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14709
14710 AutoMultiWriteLock2 alock(this->lockHandle(),
14711 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14712
14713 /* bail out if trying to lock things with already set up locking */
14714 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14715
14716 MultiResult mrc(S_OK);
14717
14718 /* Collect locking information for all medium objects attached to the VM. */
14719 for (MediumAttachmentList::const_iterator
14720 it = mMediumAttachments->begin();
14721 it != mMediumAttachments->end();
14722 ++it)
14723 {
14724 MediumAttachment *pAtt = *it;
14725 DeviceType_T devType = pAtt->i_getType();
14726 Medium *pMedium = pAtt->i_getMedium();
14727
14728 MediumLockList *pMediumLockList(new MediumLockList());
14729 // There can be attachments without a medium (floppy/dvd), and thus
14730 // it's impossible to create a medium lock list. It still makes sense
14731 // to have the empty medium lock list in the map in case a medium is
14732 // attached later.
14733 if (pMedium != NULL)
14734 {
14735 MediumType_T mediumType = pMedium->i_getType();
14736 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14737 || mediumType == MediumType_Shareable;
14738 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14739
14740 alock.release();
14741 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14742 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14743 false /* fMediumLockWriteAll */,
14744 NULL,
14745 *pMediumLockList);
14746 alock.acquire();
14747 if (FAILED(mrc))
14748 {
14749 delete pMediumLockList;
14750 mData->mSession.mLockedMedia.Clear();
14751 break;
14752 }
14753 }
14754
14755 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14756 if (FAILED(rc))
14757 {
14758 mData->mSession.mLockedMedia.Clear();
14759 mrc = setError(rc,
14760 tr("Collecting locking information for all attached media failed"));
14761 break;
14762 }
14763 }
14764
14765 if (SUCCEEDED(mrc))
14766 {
14767 /* Now lock all media. If this fails, nothing is locked. */
14768 alock.release();
14769 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14770 alock.acquire();
14771 if (FAILED(rc))
14772 {
14773 mrc = setError(rc,
14774 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14775 }
14776 }
14777
14778 return mrc;
14779}
14780
14781/**
14782 * Undoes the locks made by by #lockMedia().
14783 */
14784HRESULT SessionMachine::i_unlockMedia()
14785{
14786 AutoCaller autoCaller(this);
14787 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14788
14789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14790
14791 /* we may be holding important error info on the current thread;
14792 * preserve it */
14793 ErrorInfoKeeper eik;
14794
14795 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14796 AssertComRC(rc);
14797 return rc;
14798}
14799
14800/**
14801 * Helper to change the machine state (reimplementation).
14802 *
14803 * @note Locks this object for writing.
14804 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14805 * it can cause crashes in random places due to unexpectedly committing
14806 * the current settings. The caller is responsible for that. The call
14807 * to saveStateSettings is fine, because this method does not commit.
14808 */
14809HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14810{
14811 LogFlowThisFuncEnter();
14812 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14813
14814 AutoCaller autoCaller(this);
14815 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14816
14817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14818
14819 MachineState_T oldMachineState = mData->mMachineState;
14820
14821 AssertMsgReturn(oldMachineState != aMachineState,
14822 ("oldMachineState=%s, aMachineState=%s\n",
14823 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14824 E_FAIL);
14825
14826 HRESULT rc = S_OK;
14827
14828 int stsFlags = 0;
14829 bool deleteSavedState = false;
14830
14831 /* detect some state transitions */
14832
14833 if ( ( oldMachineState == MachineState_Saved
14834 && aMachineState == MachineState_Restoring)
14835 || ( ( oldMachineState == MachineState_PoweredOff
14836 || oldMachineState == MachineState_Teleported
14837 || oldMachineState == MachineState_Aborted
14838 )
14839 && ( aMachineState == MachineState_TeleportingIn
14840 || aMachineState == MachineState_Starting
14841 )
14842 )
14843 )
14844 {
14845 /* The EMT thread is about to start */
14846
14847 /* Nothing to do here for now... */
14848
14849 /// @todo NEWMEDIA don't let mDVDDrive and other children
14850 /// change anything when in the Starting/Restoring state
14851 }
14852 else if ( ( oldMachineState == MachineState_Running
14853 || oldMachineState == MachineState_Paused
14854 || oldMachineState == MachineState_Teleporting
14855 || oldMachineState == MachineState_OnlineSnapshotting
14856 || oldMachineState == MachineState_LiveSnapshotting
14857 || oldMachineState == MachineState_Stuck
14858 || oldMachineState == MachineState_Starting
14859 || oldMachineState == MachineState_Stopping
14860 || oldMachineState == MachineState_Saving
14861 || oldMachineState == MachineState_Restoring
14862 || oldMachineState == MachineState_TeleportingPausedVM
14863 || oldMachineState == MachineState_TeleportingIn
14864 )
14865 && ( aMachineState == MachineState_PoweredOff
14866 || aMachineState == MachineState_Saved
14867 || aMachineState == MachineState_Teleported
14868 || aMachineState == MachineState_Aborted
14869 )
14870 )
14871 {
14872 /* The EMT thread has just stopped, unlock attached media. Note that as
14873 * opposed to locking that is done from Console, we do unlocking here
14874 * because the VM process may have aborted before having a chance to
14875 * properly unlock all media it locked. */
14876
14877 unlockMedia();
14878 }
14879
14880 if (oldMachineState == MachineState_Restoring)
14881 {
14882 if (aMachineState != MachineState_Saved)
14883 {
14884 /*
14885 * delete the saved state file once the machine has finished
14886 * restoring from it (note that Console sets the state from
14887 * Restoring to Saved if the VM couldn't restore successfully,
14888 * to give the user an ability to fix an error and retry --
14889 * we keep the saved state file in this case)
14890 */
14891 deleteSavedState = true;
14892 }
14893 }
14894 else if ( oldMachineState == MachineState_Saved
14895 && ( aMachineState == MachineState_PoweredOff
14896 || aMachineState == MachineState_Aborted
14897 || aMachineState == MachineState_Teleported
14898 )
14899 )
14900 {
14901 /*
14902 * delete the saved state after SessionMachine::ForgetSavedState() is called
14903 * or if the VM process (owning a direct VM session) crashed while the
14904 * VM was Saved
14905 */
14906
14907 /// @todo (dmik)
14908 // Not sure that deleting the saved state file just because of the
14909 // client death before it attempted to restore the VM is a good
14910 // thing. But when it crashes we need to go to the Aborted state
14911 // which cannot have the saved state file associated... The only
14912 // way to fix this is to make the Aborted condition not a VM state
14913 // but a bool flag: i.e., when a crash occurs, set it to true and
14914 // change the state to PoweredOff or Saved depending on the
14915 // saved state presence.
14916
14917 deleteSavedState = true;
14918 mData->mCurrentStateModified = TRUE;
14919 stsFlags |= SaveSTS_CurStateModified;
14920 }
14921
14922 if ( aMachineState == MachineState_Starting
14923 || aMachineState == MachineState_Restoring
14924 || aMachineState == MachineState_TeleportingIn
14925 )
14926 {
14927 /* set the current state modified flag to indicate that the current
14928 * state is no more identical to the state in the
14929 * current snapshot */
14930 if (!mData->mCurrentSnapshot.isNull())
14931 {
14932 mData->mCurrentStateModified = TRUE;
14933 stsFlags |= SaveSTS_CurStateModified;
14934 }
14935 }
14936
14937 if (deleteSavedState)
14938 {
14939 if (mRemoveSavedState)
14940 {
14941 Assert(!mSSData->strStateFilePath.isEmpty());
14942
14943 // it is safe to delete the saved state file if ...
14944 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14945 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14946 // ... none of the snapshots share the saved state file
14947 )
14948 RTFileDelete(mSSData->strStateFilePath.c_str());
14949 }
14950
14951 mSSData->strStateFilePath.setNull();
14952 stsFlags |= SaveSTS_StateFilePath;
14953 }
14954
14955 /* redirect to the underlying peer machine */
14956 mPeer->i_setMachineState(aMachineState);
14957
14958 if ( oldMachineState != MachineState_RestoringSnapshot
14959 && ( aMachineState == MachineState_PoweredOff
14960 || aMachineState == MachineState_Teleported
14961 || aMachineState == MachineState_Aborted
14962 || aMachineState == MachineState_Saved))
14963 {
14964 /* the machine has stopped execution
14965 * (or the saved state file was adopted) */
14966 stsFlags |= SaveSTS_StateTimeStamp;
14967 }
14968
14969 if ( ( oldMachineState == MachineState_PoweredOff
14970 || oldMachineState == MachineState_Aborted
14971 || oldMachineState == MachineState_Teleported
14972 )
14973 && aMachineState == MachineState_Saved)
14974 {
14975 /* the saved state file was adopted */
14976 Assert(!mSSData->strStateFilePath.isEmpty());
14977 stsFlags |= SaveSTS_StateFilePath;
14978 }
14979
14980#ifdef VBOX_WITH_GUEST_PROPS
14981 if ( aMachineState == MachineState_PoweredOff
14982 || aMachineState == MachineState_Aborted
14983 || aMachineState == MachineState_Teleported)
14984 {
14985 /* Make sure any transient guest properties get removed from the
14986 * property store on shutdown. */
14987 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14988
14989 /* remove it from the settings representation */
14990 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14991 for (settings::GuestPropertiesList::iterator
14992 it = llGuestProperties.begin();
14993 it != llGuestProperties.end();
14994 /*nothing*/)
14995 {
14996 const settings::GuestProperty &prop = *it;
14997 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14998 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14999 {
15000 it = llGuestProperties.erase(it);
15001 fNeedsSaving = true;
15002 }
15003 else
15004 {
15005 ++it;
15006 }
15007 }
15008
15009 /* Additionally remove it from the HWData representation. Required to
15010 * keep everything in sync, as this is what the API keeps using. */
15011 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15012 for (HWData::GuestPropertyMap::iterator
15013 it = llHWGuestProperties.begin();
15014 it != llHWGuestProperties.end();
15015 /*nothing*/)
15016 {
15017 uint32_t fFlags = it->second.mFlags;
15018 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15019 {
15020 /* iterator where we need to continue after the erase call
15021 * (C++03 is a fact still, and it doesn't return the iterator
15022 * which would allow continuing) */
15023 HWData::GuestPropertyMap::iterator it2 = it;
15024 ++it2;
15025 llHWGuestProperties.erase(it);
15026 it = it2;
15027 fNeedsSaving = true;
15028 }
15029 else
15030 {
15031 ++it;
15032 }
15033 }
15034
15035 if (fNeedsSaving)
15036 {
15037 mData->mCurrentStateModified = TRUE;
15038 stsFlags |= SaveSTS_CurStateModified;
15039 }
15040 }
15041#endif /* VBOX_WITH_GUEST_PROPS */
15042
15043 rc = i_saveStateSettings(stsFlags);
15044
15045 if ( ( oldMachineState != MachineState_PoweredOff
15046 && oldMachineState != MachineState_Aborted
15047 && oldMachineState != MachineState_Teleported
15048 )
15049 && ( aMachineState == MachineState_PoweredOff
15050 || aMachineState == MachineState_Aborted
15051 || aMachineState == MachineState_Teleported
15052 )
15053 )
15054 {
15055 /* we've been shut down for any reason */
15056 /* no special action so far */
15057 }
15058
15059 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15060 LogFlowThisFuncLeave();
15061 return rc;
15062}
15063
15064/**
15065 * Sends the current machine state value to the VM process.
15066 *
15067 * @note Locks this object for reading, then calls a client process.
15068 */
15069HRESULT SessionMachine::i_updateMachineStateOnClient()
15070{
15071 AutoCaller autoCaller(this);
15072 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15073
15074 ComPtr<IInternalSessionControl> directControl;
15075 {
15076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15077 AssertReturn(!!mData, E_FAIL);
15078 if (mData->mSession.mLockType == LockType_VM)
15079 directControl = mData->mSession.mDirectControl;
15080
15081 /* directControl may be already set to NULL here in #OnSessionEnd()
15082 * called too early by the direct session process while there is still
15083 * some operation (like deleting the snapshot) in progress. The client
15084 * process in this case is waiting inside Session::close() for the
15085 * "end session" process object to complete, while #uninit() called by
15086 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15087 * operation to complete. For now, we accept this inconsistent behavior
15088 * and simply do nothing here. */
15089
15090 if (mData->mSession.mState == SessionState_Unlocking)
15091 return S_OK;
15092 }
15093
15094 /* ignore notifications sent after #OnSessionEnd() is called */
15095 if (!directControl)
15096 return S_OK;
15097
15098 return directControl->UpdateMachineState(mData->mMachineState);
15099}
15100
15101
15102/*static*/
15103HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15104{
15105 va_list args;
15106 va_start(args, pcszMsg);
15107 HRESULT rc = setErrorInternal(aResultCode,
15108 getStaticClassIID(),
15109 getStaticComponentName(),
15110 Utf8Str(pcszMsg, args),
15111 false /* aWarning */,
15112 true /* aLogIt */);
15113 va_end(args);
15114 return rc;
15115}
15116
15117
15118HRESULT Machine::updateState(MachineState_T aState)
15119{
15120 NOREF(aState);
15121 ReturnComNotImplemented();
15122}
15123
15124HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15125{
15126 NOREF(aProgress);
15127 ReturnComNotImplemented();
15128}
15129
15130HRESULT Machine::endPowerUp(LONG aResult)
15131{
15132 NOREF(aResult);
15133 ReturnComNotImplemented();
15134}
15135
15136HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15137{
15138 NOREF(aProgress);
15139 ReturnComNotImplemented();
15140}
15141
15142HRESULT Machine::endPoweringDown(LONG aResult,
15143 const com::Utf8Str &aErrMsg)
15144{
15145 NOREF(aResult);
15146 NOREF(aErrMsg);
15147 ReturnComNotImplemented();
15148}
15149
15150HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15151 BOOL *aMatched,
15152 ULONG *aMaskedInterfaces)
15153{
15154 NOREF(aDevice);
15155 NOREF(aMatched);
15156 NOREF(aMaskedInterfaces);
15157 ReturnComNotImplemented();
15158
15159}
15160
15161HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15162{
15163 NOREF(aId); NOREF(aCaptureFilename);
15164 ReturnComNotImplemented();
15165}
15166
15167HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15168 BOOL aDone)
15169{
15170 NOREF(aId);
15171 NOREF(aDone);
15172 ReturnComNotImplemented();
15173}
15174
15175HRESULT Machine::autoCaptureUSBDevices()
15176{
15177 ReturnComNotImplemented();
15178}
15179
15180HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15181{
15182 NOREF(aDone);
15183 ReturnComNotImplemented();
15184}
15185
15186HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15187 ComPtr<IProgress> &aProgress)
15188{
15189 NOREF(aSession);
15190 NOREF(aProgress);
15191 ReturnComNotImplemented();
15192}
15193
15194HRESULT Machine::finishOnlineMergeMedium()
15195{
15196 ReturnComNotImplemented();
15197}
15198
15199HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15200 std::vector<com::Utf8Str> &aValues,
15201 std::vector<LONG64> &aTimestamps,
15202 std::vector<com::Utf8Str> &aFlags)
15203{
15204 NOREF(aNames);
15205 NOREF(aValues);
15206 NOREF(aTimestamps);
15207 NOREF(aFlags);
15208 ReturnComNotImplemented();
15209}
15210
15211HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15212 const com::Utf8Str &aValue,
15213 LONG64 aTimestamp,
15214 const com::Utf8Str &aFlags)
15215{
15216 NOREF(aName);
15217 NOREF(aValue);
15218 NOREF(aTimestamp);
15219 NOREF(aFlags);
15220 ReturnComNotImplemented();
15221}
15222
15223HRESULT Machine::lockMedia()
15224{
15225 ReturnComNotImplemented();
15226}
15227
15228HRESULT Machine::unlockMedia()
15229{
15230 ReturnComNotImplemented();
15231}
15232
15233HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15234 ComPtr<IMediumAttachment> &aNewAttachment)
15235{
15236 NOREF(aAttachment);
15237 NOREF(aNewAttachment);
15238 ReturnComNotImplemented();
15239}
15240
15241HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15242 ULONG aCpuUser,
15243 ULONG aCpuKernel,
15244 ULONG aCpuIdle,
15245 ULONG aMemTotal,
15246 ULONG aMemFree,
15247 ULONG aMemBalloon,
15248 ULONG aMemShared,
15249 ULONG aMemCache,
15250 ULONG aPagedTotal,
15251 ULONG aMemAllocTotal,
15252 ULONG aMemFreeTotal,
15253 ULONG aMemBalloonTotal,
15254 ULONG aMemSharedTotal,
15255 ULONG aVmNetRx,
15256 ULONG aVmNetTx)
15257{
15258 NOREF(aValidStats);
15259 NOREF(aCpuUser);
15260 NOREF(aCpuKernel);
15261 NOREF(aCpuIdle);
15262 NOREF(aMemTotal);
15263 NOREF(aMemFree);
15264 NOREF(aMemBalloon);
15265 NOREF(aMemShared);
15266 NOREF(aMemCache);
15267 NOREF(aPagedTotal);
15268 NOREF(aMemAllocTotal);
15269 NOREF(aMemFreeTotal);
15270 NOREF(aMemBalloonTotal);
15271 NOREF(aMemSharedTotal);
15272 NOREF(aVmNetRx);
15273 NOREF(aVmNetTx);
15274 ReturnComNotImplemented();
15275}
15276
15277HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15278 com::Utf8Str &aResult)
15279{
15280 NOREF(aAuthParams);
15281 NOREF(aResult);
15282 ReturnComNotImplemented();
15283}
15284
15285HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15286{
15287 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15288
15289 AutoCaller autoCaller(this);
15290 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15291
15292 HRESULT rc = S_OK;
15293
15294 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15295 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15296 rc = getUSBDeviceFilters(usbDeviceFilters);
15297 if (FAILED(rc)) return rc;
15298
15299 NOREF(aFlags);
15300 com::Utf8Str osTypeId;
15301 ComObjPtr<GuestOSType> osType = NULL;
15302
15303 /* Get the guest os type as a string from the VB. */
15304 rc = getOSTypeId(osTypeId);
15305 if (FAILED(rc)) return rc;
15306
15307 /* Get the os type obj that coresponds, can be used to get
15308 * the defaults for this guest OS. */
15309 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15310 if (FAILED(rc)) return rc;
15311
15312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15313
15314 /* Let the OS type select 64-bit ness. */
15315 mHWData->mLongMode = osType->i_is64Bit()
15316 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15317
15318 /* Apply network adapters defaults */
15319 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15320 mNetworkAdapters[slot]->i_applyDefaults(osType);
15321
15322 /* Apply serial port defaults */
15323 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15324 mSerialPorts[slot]->i_applyDefaults(osType);
15325
15326 /* Apply parallel port defaults - not OS dependent*/
15327 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15328 mParallelPorts[slot]->i_applyDefaults();
15329
15330
15331 /* Let the OS type enable the X2APIC */
15332 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15333
15334 /* This one covers IOAPICEnabled. */
15335 mBIOSSettings->i_applyDefaults(osType);
15336
15337 /* Initialize default BIOS settings here */
15338 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15339 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15340
15341 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15342 if (FAILED(rc)) return rc;
15343
15344 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15345 if (FAILED(rc)) return rc;
15346
15347 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15348 if (FAILED(rc)) return rc;
15349
15350 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15351 if (FAILED(rc)) return rc;
15352
15353 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15354 if (FAILED(rc)) return rc;
15355
15356 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15357 if (FAILED(rc)) return rc;
15358
15359 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15360 if (FAILED(rc)) return rc;
15361
15362 BOOL mRTCUseUTC;
15363 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15364 if (FAILED(rc)) return rc;
15365
15366 setRTCUseUTC(mRTCUseUTC);
15367 if (FAILED(rc)) return rc;
15368
15369 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15370 if (FAILED(rc)) return rc;
15371
15372 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15373 if (FAILED(rc)) return rc;
15374
15375 /* Audio stuff. */
15376 AudioCodecType_T audioCodec;
15377 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15378 if (FAILED(rc)) return rc;
15379
15380 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15381 if (FAILED(rc)) return rc;
15382
15383 AudioControllerType_T audioController;
15384 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15385 if (FAILED(rc)) return rc;
15386
15387 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15388 if (FAILED(rc)) return rc;
15389
15390 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15391 if (FAILED(rc)) return rc;
15392
15393 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15394 if (FAILED(rc)) return rc;
15395
15396 /* Storage Controllers */
15397 StorageControllerType_T hdStorageControllerType;
15398 StorageBus_T hdStorageBusType;
15399 StorageControllerType_T dvdStorageControllerType;
15400 StorageBus_T dvdStorageBusType;
15401 BOOL recommendedFloppy;
15402 ComPtr<IStorageController> floppyController;
15403 ComPtr<IStorageController> hdController;
15404 ComPtr<IStorageController> dvdController;
15405 Utf8Str strFloppyName, strDVDName, strHDName;
15406
15407 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15408 strFloppyName = Bstr("Floppy 1").raw();
15409 strDVDName = Bstr("DVD 1").raw();
15410 strHDName = Bstr("HDD 1").raw();
15411
15412 /* Floppy recommended? add one. */
15413 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15414 if (FAILED(rc)) return rc;
15415 if (recommendedFloppy)
15416 {
15417 rc = addStorageController(strFloppyName,
15418 StorageBus_Floppy,
15419 floppyController);
15420 if (FAILED(rc)) return rc;
15421 }
15422
15423 /* Setup one DVD storage controller. */
15424 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15425 if (FAILED(rc)) return rc;
15426
15427 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15428 if (FAILED(rc)) return rc;
15429
15430 rc = addStorageController(strDVDName,
15431 dvdStorageBusType,
15432 dvdController);
15433 if (FAILED(rc)) return rc;
15434
15435 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15436 if (FAILED(rc)) return rc;
15437
15438 /* Setup one HDD storage controller. */
15439 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15440 if (FAILED(rc)) return rc;
15441
15442 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15443 if (FAILED(rc)) return rc;
15444
15445 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15446 {
15447 rc = addStorageController(strHDName,
15448 hdStorageBusType,
15449 hdController);
15450 if (FAILED(rc)) return rc;
15451
15452 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15453 if (FAILED(rc)) return rc;
15454 }
15455 else
15456 {
15457 /* The HD controller is the same as DVD: */
15458 hdController = dvdController;
15459 strHDName = Bstr("DVD 1").raw();
15460 }
15461
15462 /* Limit the AHCI port count if it's used because windows has trouble with
15463 * too many ports and other guest (OS X in particular) may take extra long
15464 * boot: */
15465
15466 // pParent = static_cast<Medium*>(aP)
15467 IStorageController *temp = hdController;
15468 ComObjPtr<StorageController> storageController;
15469 storageController = static_cast<StorageController *>(temp);
15470
15471 // tempHDController = aHDController;
15472 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15473 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15474 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15475 storageController->COMSETTER(PortCount)(1);
15476
15477 /* USB stuff */
15478
15479 bool ohciEnabled = false;
15480
15481 ComPtr<IUSBController> usbController;
15482 BOOL recommendedUSB3;
15483 BOOL recommendedUSB;
15484 BOOL usbProxyAvailable;
15485
15486 getUSBProxyAvailable(&usbProxyAvailable);
15487 if (FAILED(rc)) return rc;
15488
15489 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15490 if (FAILED(rc)) return rc;
15491 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15492 if (FAILED(rc)) return rc;
15493
15494 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15495 {
15496#ifdef VBOX_WITH_EXTPACK
15497 /* USB 3.0 is only available if the proper ExtPack is installed. */
15498 ExtPackManager *aManager = mParent->i_getExtPackManager();
15499 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15500 {
15501 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15502 if (FAILED(rc)) return rc;
15503
15504 /* xHci includes OHCI */
15505 ohciEnabled = true;
15506 }
15507#endif
15508 }
15509 if ( !ohciEnabled
15510 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15511 {
15512 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15513 if (FAILED(rc)) return rc;
15514 ohciEnabled = true;
15515
15516#ifdef VBOX_WITH_EXTPACK
15517 /* USB 2.0 is only available if the proper ExtPack is installed.
15518 * Note. Configuring EHCI here and providing messages about
15519 * the missing extpack isn't exactly clean, but it is a
15520 * necessary evil to patch over legacy compatability issues
15521 * introduced by the new distribution model. */
15522 ExtPackManager *manager = mParent->i_getExtPackManager();
15523 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15524 {
15525 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15526 if (FAILED(rc)) return rc;
15527 }
15528#endif
15529 }
15530
15531 /* Set recommended human interface device types: */
15532 BOOL recommendedUSBHID;
15533 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15534 if (FAILED(rc)) return rc;
15535
15536 if (recommendedUSBHID)
15537 {
15538 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15539 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15540 if (!ohciEnabled && !usbDeviceFilters.isNull())
15541 {
15542 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15543 if (FAILED(rc)) return rc;
15544 }
15545 }
15546
15547 BOOL recommendedUSBTablet;
15548 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15549 if (FAILED(rc)) return rc;
15550
15551 if (recommendedUSBTablet)
15552 {
15553 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15554 if (!ohciEnabled && !usbDeviceFilters.isNull())
15555 {
15556 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15557 if (FAILED(rc)) return rc;
15558 }
15559 }
15560 return S_OK;
15561}
15562
15563/* This isn't handled entirely by the wrapper generator yet. */
15564#ifdef VBOX_WITH_XPCOM
15565NS_DECL_CLASSINFO(SessionMachine)
15566NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15567
15568NS_DECL_CLASSINFO(SnapshotMachine)
15569NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15570#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