VirtualBox

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

Last change on this file since 74962 was 74804, checked in by vboxsync, 6 years ago

Main/Progress: Split into safe public interface and a private one which is used purely by API implementation code (for updating). Needed quite a few minor adjustments elsewhere.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 538.7 KB
Line 
1/* $Id: MachineImpl.cpp 74804 2018-10-12 15:09:44Z 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 case GraphicsControllerType_VBoxSVGA:
2024#endif
2025 break;
2026 default:
2027 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2028 }
2029
2030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2031
2032 HRESULT rc = i_checkStateDependency(MutableStateDep);
2033 if (FAILED(rc)) return rc;
2034
2035 i_setModified(IsModified_MachineData);
2036 mHWData.backup();
2037 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2038
2039 return S_OK;
2040}
2041
2042HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2043{
2044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 *aVRAMSize = mHWData->mVRAMSize;
2047
2048 return S_OK;
2049}
2050
2051HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2052{
2053 /* check VRAM limits */
2054 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2055 return setError(E_INVALIDARG,
2056 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2057 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2058
2059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2060
2061 HRESULT rc = i_checkStateDependency(MutableStateDep);
2062 if (FAILED(rc)) return rc;
2063
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mVRAMSize = aVRAMSize;
2067
2068 return S_OK;
2069}
2070
2071/** @todo this method should not be public */
2072HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2073{
2074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2077
2078 return S_OK;
2079}
2080
2081/**
2082 * Set the memory balloon size.
2083 *
2084 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2085 * we have to make sure that we never call IGuest from here.
2086 */
2087HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2088{
2089 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2090#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2091 /* check limits */
2092 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2093 return setError(E_INVALIDARG,
2094 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2095 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2096
2097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2098
2099 i_setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2102
2103 return S_OK;
2104#else
2105 NOREF(aMemoryBalloonSize);
2106 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2107#endif
2108}
2109
2110HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2111{
2112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2113
2114 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2115 return S_OK;
2116}
2117
2118HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2119{
2120#ifdef VBOX_WITH_PAGE_SHARING
2121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2124 i_setModified(IsModified_MachineData);
2125 mHWData.backup();
2126 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2127 return S_OK;
2128#else
2129 NOREF(aPageFusionEnabled);
2130 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2131#endif
2132}
2133
2134HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2135{
2136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2137
2138 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2139
2140 return S_OK;
2141}
2142
2143HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2144{
2145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2146
2147 HRESULT rc = i_checkStateDependency(MutableStateDep);
2148 if (FAILED(rc)) return rc;
2149
2150 /** @todo check validity! */
2151
2152 i_setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2155
2156 return S_OK;
2157}
2158
2159
2160HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2161{
2162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2163
2164 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2165
2166 return S_OK;
2167}
2168
2169HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2170{
2171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 HRESULT rc = i_checkStateDependency(MutableStateDep);
2174 if (FAILED(rc)) return rc;
2175
2176 /** @todo check validity! */
2177 i_setModified(IsModified_MachineData);
2178 mHWData.backup();
2179 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2180
2181 return S_OK;
2182}
2183
2184HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2185{
2186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 *aMonitorCount = mHWData->mMonitorCount;
2189
2190 return S_OK;
2191}
2192
2193HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2194{
2195 /* make sure monitor count is a sensible number */
2196 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2197 return setError(E_INVALIDARG,
2198 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2199 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2200
2201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2202
2203 HRESULT rc = i_checkStateDependency(MutableStateDep);
2204 if (FAILED(rc)) return rc;
2205
2206 i_setModified(IsModified_MachineData);
2207 mHWData.backup();
2208 mHWData->mMonitorCount = aMonitorCount;
2209
2210 return S_OK;
2211}
2212
2213HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2214{
2215 /* mBIOSSettings is constant during life time, no need to lock */
2216 aBIOSSettings = mBIOSSettings;
2217
2218 return S_OK;
2219}
2220
2221HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2222{
2223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2224
2225 switch (aProperty)
2226 {
2227 case CPUPropertyType_PAE:
2228 *aValue = mHWData->mPAEEnabled;
2229 break;
2230
2231 case CPUPropertyType_LongMode:
2232 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2233 *aValue = TRUE;
2234 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2235 *aValue = FALSE;
2236#if HC_ARCH_BITS == 64
2237 else
2238 *aValue = TRUE;
2239#else
2240 else
2241 {
2242 *aValue = FALSE;
2243
2244 ComObjPtr<GuestOSType> pGuestOSType;
2245 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2246 pGuestOSType);
2247 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2248 {
2249 if (pGuestOSType->i_is64Bit())
2250 {
2251 ComObjPtr<Host> pHost = mParent->i_host();
2252 alock.release();
2253
2254 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2255 if (FAILED(hrc2))
2256 *aValue = FALSE;
2257 }
2258 }
2259 }
2260#endif
2261 break;
2262
2263 case CPUPropertyType_TripleFaultReset:
2264 *aValue = mHWData->mTripleFaultReset;
2265 break;
2266
2267 case CPUPropertyType_APIC:
2268 *aValue = mHWData->mAPIC;
2269 break;
2270
2271 case CPUPropertyType_X2APIC:
2272 *aValue = mHWData->mX2APIC;
2273 break;
2274
2275 case CPUPropertyType_IBPBOnVMExit:
2276 *aValue = mHWData->mIBPBOnVMExit;
2277 break;
2278
2279 case CPUPropertyType_IBPBOnVMEntry:
2280 *aValue = mHWData->mIBPBOnVMEntry;
2281 break;
2282
2283 case CPUPropertyType_SpecCtrl:
2284 *aValue = mHWData->mSpecCtrl;
2285 break;
2286
2287 case CPUPropertyType_SpecCtrlByHost:
2288 *aValue = mHWData->mSpecCtrlByHost;
2289 break;
2290
2291 case CPUPropertyType_HWVirt:
2292 *aValue = mHWData->mNestedHWVirt;
2293 break;
2294
2295 default:
2296 return E_INVALIDARG;
2297 }
2298 return S_OK;
2299}
2300
2301HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2302{
2303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2304
2305 HRESULT rc = i_checkStateDependency(MutableStateDep);
2306 if (FAILED(rc)) return rc;
2307
2308 switch (aProperty)
2309 {
2310 case CPUPropertyType_PAE:
2311 i_setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mPAEEnabled = !!aValue;
2314 break;
2315
2316 case CPUPropertyType_LongMode:
2317 i_setModified(IsModified_MachineData);
2318 mHWData.backup();
2319 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2320 break;
2321
2322 case CPUPropertyType_TripleFaultReset:
2323 i_setModified(IsModified_MachineData);
2324 mHWData.backup();
2325 mHWData->mTripleFaultReset = !!aValue;
2326 break;
2327
2328 case CPUPropertyType_APIC:
2329 if (mHWData->mX2APIC)
2330 aValue = TRUE;
2331 i_setModified(IsModified_MachineData);
2332 mHWData.backup();
2333 mHWData->mAPIC = !!aValue;
2334 break;
2335
2336 case CPUPropertyType_X2APIC:
2337 i_setModified(IsModified_MachineData);
2338 mHWData.backup();
2339 mHWData->mX2APIC = !!aValue;
2340 if (aValue)
2341 mHWData->mAPIC = !!aValue;
2342 break;
2343
2344 case CPUPropertyType_IBPBOnVMExit:
2345 i_setModified(IsModified_MachineData);
2346 mHWData.backup();
2347 mHWData->mIBPBOnVMExit = !!aValue;
2348 break;
2349
2350 case CPUPropertyType_IBPBOnVMEntry:
2351 i_setModified(IsModified_MachineData);
2352 mHWData.backup();
2353 mHWData->mIBPBOnVMEntry = !!aValue;
2354 break;
2355
2356 case CPUPropertyType_SpecCtrl:
2357 i_setModified(IsModified_MachineData);
2358 mHWData.backup();
2359 mHWData->mSpecCtrl = !!aValue;
2360 break;
2361
2362 case CPUPropertyType_SpecCtrlByHost:
2363 i_setModified(IsModified_MachineData);
2364 mHWData.backup();
2365 mHWData->mSpecCtrlByHost = !!aValue;
2366 break;
2367
2368 case CPUPropertyType_HWVirt:
2369 i_setModified(IsModified_MachineData);
2370 mHWData.backup();
2371 mHWData->mNestedHWVirt = !!aValue;
2372 break;
2373
2374 default:
2375 return E_INVALIDARG;
2376 }
2377 return S_OK;
2378}
2379
2380HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2381 ULONG *aValEcx, ULONG *aValEdx)
2382{
2383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2384 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2385 {
2386 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2387 it != mHWData->mCpuIdLeafList.end();
2388 ++it)
2389 {
2390 if (aOrdinal == 0)
2391 {
2392 const settings::CpuIdLeaf &rLeaf= *it;
2393 *aIdx = rLeaf.idx;
2394 *aSubIdx = rLeaf.idxSub;
2395 *aValEax = rLeaf.uEax;
2396 *aValEbx = rLeaf.uEbx;
2397 *aValEcx = rLeaf.uEcx;
2398 *aValEdx = rLeaf.uEdx;
2399 return S_OK;
2400 }
2401 aOrdinal--;
2402 }
2403 }
2404 return E_INVALIDARG;
2405}
2406
2407HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2408{
2409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2410
2411 /*
2412 * Search the list.
2413 */
2414 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2415 {
2416 const settings::CpuIdLeaf &rLeaf= *it;
2417 if ( rLeaf.idx == aIdx
2418 && ( aSubIdx == UINT32_MAX
2419 || rLeaf.idxSub == aSubIdx) )
2420 {
2421 *aValEax = rLeaf.uEax;
2422 *aValEbx = rLeaf.uEbx;
2423 *aValEcx = rLeaf.uEcx;
2424 *aValEdx = rLeaf.uEdx;
2425 return S_OK;
2426 }
2427 }
2428
2429 return E_INVALIDARG;
2430}
2431
2432
2433HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2434{
2435 /*
2436 * Validate input before taking locks and checking state.
2437 */
2438 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2439 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2440 if ( aIdx >= UINT32_C(0x20)
2441 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2442 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2443 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2444
2445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2446 HRESULT rc = i_checkStateDependency(MutableStateDep);
2447 if (FAILED(rc)) return rc;
2448
2449 /*
2450 * Impose a maximum number of leaves.
2451 */
2452 if (mHWData->mCpuIdLeafList.size() > 256)
2453 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2454
2455 /*
2456 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2457 */
2458 i_setModified(IsModified_MachineData);
2459 mHWData.backup();
2460
2461 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2462 {
2463 settings::CpuIdLeaf &rLeaf= *it;
2464 if ( rLeaf.idx == aIdx
2465 && ( aSubIdx == UINT32_MAX
2466 || rLeaf.idxSub == aSubIdx) )
2467 it = mHWData->mCpuIdLeafList.erase(it);
2468 else
2469 ++it;
2470 }
2471
2472 settings::CpuIdLeaf NewLeaf;
2473 NewLeaf.idx = aIdx;
2474 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2475 NewLeaf.uEax = aValEax;
2476 NewLeaf.uEbx = aValEbx;
2477 NewLeaf.uEcx = aValEcx;
2478 NewLeaf.uEdx = aValEdx;
2479 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2480 return S_OK;
2481}
2482
2483HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2484{
2485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 HRESULT rc = i_checkStateDependency(MutableStateDep);
2488 if (FAILED(rc)) return rc;
2489
2490 /*
2491 * Do the removal.
2492 */
2493 bool fModified = false;
2494 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2495 {
2496 settings::CpuIdLeaf &rLeaf= *it;
2497 if ( rLeaf.idx == aIdx
2498 && ( aSubIdx == UINT32_MAX
2499 || rLeaf.idxSub == aSubIdx) )
2500 {
2501 if (!fModified)
2502 {
2503 fModified = true;
2504 i_setModified(IsModified_MachineData);
2505 mHWData.backup();
2506 }
2507 it = mHWData->mCpuIdLeafList.erase(it);
2508 }
2509 else
2510 ++it;
2511 }
2512
2513 return S_OK;
2514}
2515
2516HRESULT Machine::removeAllCPUIDLeaves()
2517{
2518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 HRESULT rc = i_checkStateDependency(MutableStateDep);
2521 if (FAILED(rc)) return rc;
2522
2523 if (mHWData->mCpuIdLeafList.size() > 0)
2524 {
2525 i_setModified(IsModified_MachineData);
2526 mHWData.backup();
2527
2528 mHWData->mCpuIdLeafList.clear();
2529 }
2530
2531 return S_OK;
2532}
2533HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2534{
2535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2536
2537 switch(aProperty)
2538 {
2539 case HWVirtExPropertyType_Enabled:
2540 *aValue = mHWData->mHWVirtExEnabled;
2541 break;
2542
2543 case HWVirtExPropertyType_VPID:
2544 *aValue = mHWData->mHWVirtExVPIDEnabled;
2545 break;
2546
2547 case HWVirtExPropertyType_NestedPaging:
2548 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2549 break;
2550
2551 case HWVirtExPropertyType_UnrestrictedExecution:
2552 *aValue = mHWData->mHWVirtExUXEnabled;
2553 break;
2554
2555 case HWVirtExPropertyType_LargePages:
2556 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2557#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2558 *aValue = FALSE;
2559#endif
2560 break;
2561
2562 case HWVirtExPropertyType_Force:
2563 *aValue = mHWData->mHWVirtExForceEnabled;
2564 break;
2565
2566 case HWVirtExPropertyType_UseNativeApi:
2567 *aValue = mHWData->mHWVirtExUseNativeApi;
2568 break;
2569
2570 default:
2571 return E_INVALIDARG;
2572 }
2573 return S_OK;
2574}
2575
2576HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2577{
2578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 HRESULT rc = i_checkStateDependency(MutableStateDep);
2581 if (FAILED(rc)) return rc;
2582
2583 switch (aProperty)
2584 {
2585 case HWVirtExPropertyType_Enabled:
2586 i_setModified(IsModified_MachineData);
2587 mHWData.backup();
2588 mHWData->mHWVirtExEnabled = !!aValue;
2589 break;
2590
2591 case HWVirtExPropertyType_VPID:
2592 i_setModified(IsModified_MachineData);
2593 mHWData.backup();
2594 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2595 break;
2596
2597 case HWVirtExPropertyType_NestedPaging:
2598 i_setModified(IsModified_MachineData);
2599 mHWData.backup();
2600 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2601 break;
2602
2603 case HWVirtExPropertyType_UnrestrictedExecution:
2604 i_setModified(IsModified_MachineData);
2605 mHWData.backup();
2606 mHWData->mHWVirtExUXEnabled = !!aValue;
2607 break;
2608
2609 case HWVirtExPropertyType_LargePages:
2610 i_setModified(IsModified_MachineData);
2611 mHWData.backup();
2612 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2613 break;
2614
2615 case HWVirtExPropertyType_Force:
2616 i_setModified(IsModified_MachineData);
2617 mHWData.backup();
2618 mHWData->mHWVirtExForceEnabled = !!aValue;
2619 break;
2620
2621 case HWVirtExPropertyType_UseNativeApi:
2622 i_setModified(IsModified_MachineData);
2623 mHWData.backup();
2624 mHWData->mHWVirtExUseNativeApi = !!aValue;
2625 break;
2626
2627 default:
2628 return E_INVALIDARG;
2629 }
2630
2631 return S_OK;
2632}
2633
2634HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2635{
2636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2637
2638 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2639
2640 return S_OK;
2641}
2642
2643HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2644{
2645 /** @todo (r=dmik):
2646 * 1. Allow to change the name of the snapshot folder containing snapshots
2647 * 2. Rename the folder on disk instead of just changing the property
2648 * value (to be smart and not to leave garbage). Note that it cannot be
2649 * done here because the change may be rolled back. Thus, the right
2650 * place is #saveSettings().
2651 */
2652
2653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2654
2655 HRESULT rc = i_checkStateDependency(MutableStateDep);
2656 if (FAILED(rc)) return rc;
2657
2658 if (!mData->mCurrentSnapshot.isNull())
2659 return setError(E_FAIL,
2660 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2661
2662 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2663
2664 if (strSnapshotFolder.isEmpty())
2665 strSnapshotFolder = "Snapshots";
2666 int vrc = i_calculateFullPath(strSnapshotFolder,
2667 strSnapshotFolder);
2668 if (RT_FAILURE(vrc))
2669 return setErrorBoth(E_FAIL, vrc,
2670 tr("Invalid snapshot folder '%s' (%Rrc)"),
2671 strSnapshotFolder.c_str(), vrc);
2672
2673 i_setModified(IsModified_MachineData);
2674 mUserData.backup();
2675
2676 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2677
2678 return S_OK;
2679}
2680
2681HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2682{
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 aMediumAttachments.resize(mMediumAttachments->size());
2686 size_t i = 0;
2687 for (MediumAttachmentList::const_iterator
2688 it = mMediumAttachments->begin();
2689 it != mMediumAttachments->end();
2690 ++it, ++i)
2691 aMediumAttachments[i] = *it;
2692
2693 return S_OK;
2694}
2695
2696HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2697{
2698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 Assert(!!mVRDEServer);
2701
2702 aVRDEServer = mVRDEServer;
2703
2704 return S_OK;
2705}
2706
2707HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2708{
2709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2710
2711 aAudioAdapter = mAudioAdapter;
2712
2713 return S_OK;
2714}
2715
2716HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2717{
2718#ifdef VBOX_WITH_VUSB
2719 clearError();
2720 MultiResult rc(S_OK);
2721
2722# ifdef VBOX_WITH_USB
2723 rc = mParent->i_host()->i_checkUSBProxyService();
2724 if (FAILED(rc)) return rc;
2725# endif
2726
2727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 aUSBControllers.resize(mUSBControllers->size());
2730 size_t i = 0;
2731 for (USBControllerList::const_iterator
2732 it = mUSBControllers->begin();
2733 it != mUSBControllers->end();
2734 ++it, ++i)
2735 aUSBControllers[i] = *it;
2736
2737 return S_OK;
2738#else
2739 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2740 * extended error info to indicate that USB is simply not available
2741 * (w/o treating it as a failure), for example, as in OSE */
2742 NOREF(aUSBControllers);
2743 ReturnComNotImplemented();
2744#endif /* VBOX_WITH_VUSB */
2745}
2746
2747HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2748{
2749#ifdef VBOX_WITH_VUSB
2750 clearError();
2751 MultiResult rc(S_OK);
2752
2753# ifdef VBOX_WITH_USB
2754 rc = mParent->i_host()->i_checkUSBProxyService();
2755 if (FAILED(rc)) return rc;
2756# endif
2757
2758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 aUSBDeviceFilters = mUSBDeviceFilters;
2761 return rc;
2762#else
2763 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2764 * extended error info to indicate that USB is simply not available
2765 * (w/o treating it as a failure), for example, as in OSE */
2766 NOREF(aUSBDeviceFilters);
2767 ReturnComNotImplemented();
2768#endif /* VBOX_WITH_VUSB */
2769}
2770
2771HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2772{
2773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 aSettingsFilePath = mData->m_strConfigFileFull;
2776
2777 return S_OK;
2778}
2779
2780HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2781{
2782 RT_NOREF(aSettingsFilePath);
2783 ReturnComNotImplemented();
2784}
2785
2786HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2787{
2788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2791 if (FAILED(rc)) return rc;
2792
2793 if (!mData->pMachineConfigFile->fileExists())
2794 // this is a new machine, and no config file exists yet:
2795 *aSettingsModified = TRUE;
2796 else
2797 *aSettingsModified = (mData->flModifications != 0);
2798
2799 return S_OK;
2800}
2801
2802HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2803{
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 *aSessionState = mData->mSession.mState;
2807
2808 return S_OK;
2809}
2810
2811HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2812{
2813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 aSessionName = mData->mSession.mName;
2816
2817 return S_OK;
2818}
2819
2820HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2821{
2822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 *aSessionPID = mData->mSession.mPID;
2825
2826 return S_OK;
2827}
2828
2829HRESULT Machine::getState(MachineState_T *aState)
2830{
2831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2832
2833 *aState = mData->mMachineState;
2834 Assert(mData->mMachineState != MachineState_Null);
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2840{
2841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2842
2843 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2844
2845 return S_OK;
2846}
2847
2848HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2849{
2850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 aStateFilePath = mSSData->strStateFilePath;
2853
2854 return S_OK;
2855}
2856
2857HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2858{
2859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 i_getLogFolder(aLogFolder);
2862
2863 return S_OK;
2864}
2865
2866HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2867{
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 aCurrentSnapshot = mData->mCurrentSnapshot;
2871
2872 return S_OK;
2873}
2874
2875HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2876{
2877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2880 ? 0
2881 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2882
2883 return S_OK;
2884}
2885
2886HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2887{
2888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2889
2890 /* Note: for machines with no snapshots, we always return FALSE
2891 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2892 * reasons :) */
2893
2894 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2895 ? FALSE
2896 : mData->mCurrentStateModified;
2897
2898 return S_OK;
2899}
2900
2901HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2902{
2903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2904
2905 aSharedFolders.resize(mHWData->mSharedFolders.size());
2906 size_t i = 0;
2907 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2908 it = mHWData->mSharedFolders.begin();
2909 it != mHWData->mSharedFolders.end();
2910 ++it, ++i)
2911 aSharedFolders[i] = *it;
2912
2913 return S_OK;
2914}
2915
2916HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2917{
2918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2919
2920 *aClipboardMode = mHWData->mClipboardMode;
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2926{
2927 HRESULT rc = S_OK;
2928
2929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2930
2931 alock.release();
2932 rc = i_onClipboardModeChange(aClipboardMode);
2933 alock.acquire();
2934 if (FAILED(rc)) return rc;
2935
2936 i_setModified(IsModified_MachineData);
2937 mHWData.backup();
2938 mHWData->mClipboardMode = aClipboardMode;
2939
2940 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2941 if (Global::IsOnline(mData->mMachineState))
2942 i_saveSettings(NULL);
2943
2944 return S_OK;
2945}
2946
2947HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2948{
2949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2950
2951 *aDnDMode = mHWData->mDnDMode;
2952
2953 return S_OK;
2954}
2955
2956HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2957{
2958 HRESULT rc = S_OK;
2959
2960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2961
2962 alock.release();
2963 rc = i_onDnDModeChange(aDnDMode);
2964
2965 alock.acquire();
2966 if (FAILED(rc)) return rc;
2967
2968 i_setModified(IsModified_MachineData);
2969 mHWData.backup();
2970 mHWData->mDnDMode = aDnDMode;
2971
2972 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2973 if (Global::IsOnline(mData->mMachineState))
2974 i_saveSettings(NULL);
2975
2976 return S_OK;
2977}
2978
2979HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2980{
2981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2982
2983 aStorageControllers.resize(mStorageControllers->size());
2984 size_t i = 0;
2985 for (StorageControllerList::const_iterator
2986 it = mStorageControllers->begin();
2987 it != mStorageControllers->end();
2988 ++it, ++i)
2989 aStorageControllers[i] = *it;
2990
2991 return S_OK;
2992}
2993
2994HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2995{
2996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 *aEnabled = mUserData->s.fTeleporterEnabled;
2999
3000 return S_OK;
3001}
3002
3003HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3004{
3005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3006
3007 /* Only allow it to be set to true when PoweredOff or Aborted.
3008 (Clearing it is always permitted.) */
3009 if ( aTeleporterEnabled
3010 && mData->mRegistered
3011 && ( !i_isSessionMachine()
3012 || ( mData->mMachineState != MachineState_PoweredOff
3013 && mData->mMachineState != MachineState_Teleported
3014 && mData->mMachineState != MachineState_Aborted
3015 )
3016 )
3017 )
3018 return setError(VBOX_E_INVALID_VM_STATE,
3019 tr("The machine is not powered off (state is %s)"),
3020 Global::stringifyMachineState(mData->mMachineState));
3021
3022 i_setModified(IsModified_MachineData);
3023 mUserData.backup();
3024 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3025
3026 return S_OK;
3027}
3028
3029HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3030{
3031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3034
3035 return S_OK;
3036}
3037
3038HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3039{
3040 if (aTeleporterPort >= _64K)
3041 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3042
3043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3044
3045 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3046 if (FAILED(rc)) return rc;
3047
3048 i_setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3051
3052 return S_OK;
3053}
3054
3055HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3056{
3057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3060
3061 return S_OK;
3062}
3063
3064HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3065{
3066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3067
3068 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3069 if (FAILED(rc)) return rc;
3070
3071 i_setModified(IsModified_MachineData);
3072 mUserData.backup();
3073 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3074
3075 return S_OK;
3076}
3077
3078HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3079{
3080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3081 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3082
3083 return S_OK;
3084}
3085
3086HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3087{
3088 /*
3089 * Hash the password first.
3090 */
3091 com::Utf8Str aT = aTeleporterPassword;
3092
3093 if (!aT.isEmpty())
3094 {
3095 if (VBoxIsPasswordHashed(&aT))
3096 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3097 VBoxHashPassword(&aT);
3098 }
3099
3100 /*
3101 * Do the update.
3102 */
3103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3104 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3105 if (SUCCEEDED(hrc))
3106 {
3107 i_setModified(IsModified_MachineData);
3108 mUserData.backup();
3109 mUserData->s.strTeleporterPassword = aT;
3110 }
3111
3112 return hrc;
3113}
3114
3115HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3116{
3117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3118
3119 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3120 return S_OK;
3121}
3122
3123HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3124{
3125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 /** @todo deal with running state change. */
3128 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3129 if (FAILED(rc)) return rc;
3130
3131 i_setModified(IsModified_MachineData);
3132 mUserData.backup();
3133 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3134 return S_OK;
3135}
3136
3137HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3138{
3139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3140
3141 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3142 return S_OK;
3143}
3144
3145HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3146{
3147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3148
3149 /** @todo deal with running state change. */
3150 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3151 if (FAILED(rc)) return rc;
3152
3153 i_setModified(IsModified_MachineData);
3154 mUserData.backup();
3155 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3156 return S_OK;
3157}
3158
3159HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3160{
3161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3162
3163 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3164 return S_OK;
3165}
3166
3167HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3168{
3169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3170
3171 /** @todo deal with running state change. */
3172 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3173 if (FAILED(rc)) return rc;
3174
3175 i_setModified(IsModified_MachineData);
3176 mUserData.backup();
3177 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3178 return S_OK;
3179}
3180
3181HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3182{
3183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3184
3185 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3186
3187 return S_OK;
3188}
3189
3190HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3191{
3192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3193
3194 /** @todo deal with running state change. */
3195 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3196 if (FAILED(rc)) return rc;
3197
3198 i_setModified(IsModified_MachineData);
3199 mUserData.backup();
3200 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3201
3202 return S_OK;
3203}
3204
3205HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3206{
3207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3208
3209 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3210 return S_OK;
3211}
3212
3213HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3214{
3215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3216
3217 /** @todo deal with running state change. */
3218 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3219 if (FAILED(rc)) return rc;
3220
3221 i_setModified(IsModified_MachineData);
3222 mUserData.backup();
3223 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3224 return S_OK;
3225}
3226
3227HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3228{
3229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3230
3231 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3232
3233 return S_OK;
3234}
3235
3236HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3237{
3238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3239
3240 /* Only allow it to be set to true when PoweredOff or Aborted.
3241 (Clearing it is always permitted.) */
3242 if ( aRTCUseUTC
3243 && mData->mRegistered
3244 && ( !i_isSessionMachine()
3245 || ( mData->mMachineState != MachineState_PoweredOff
3246 && mData->mMachineState != MachineState_Teleported
3247 && mData->mMachineState != MachineState_Aborted
3248 )
3249 )
3250 )
3251 return setError(VBOX_E_INVALID_VM_STATE,
3252 tr("The machine is not powered off (state is %s)"),
3253 Global::stringifyMachineState(mData->mMachineState));
3254
3255 i_setModified(IsModified_MachineData);
3256 mUserData.backup();
3257 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3258
3259 return S_OK;
3260}
3261
3262HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3263{
3264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3265
3266 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3267
3268 return S_OK;
3269}
3270
3271HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3272{
3273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3274
3275 HRESULT rc = i_checkStateDependency(MutableStateDep);
3276 if (FAILED(rc)) return rc;
3277
3278 i_setModified(IsModified_MachineData);
3279 mHWData.backup();
3280 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3281
3282 return S_OK;
3283}
3284
3285HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3286{
3287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3288
3289 *aIOCacheSize = mHWData->mIOCacheSize;
3290
3291 return S_OK;
3292}
3293
3294HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3295{
3296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3297
3298 HRESULT rc = i_checkStateDependency(MutableStateDep);
3299 if (FAILED(rc)) return rc;
3300
3301 i_setModified(IsModified_MachineData);
3302 mHWData.backup();
3303 mHWData->mIOCacheSize = aIOCacheSize;
3304
3305 return S_OK;
3306}
3307
3308
3309/**
3310 * @note Locks objects!
3311 */
3312HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3313 LockType_T aLockType)
3314{
3315 /* check the session state */
3316 SessionState_T state;
3317 HRESULT rc = aSession->COMGETTER(State)(&state);
3318 if (FAILED(rc)) return rc;
3319
3320 if (state != SessionState_Unlocked)
3321 return setError(VBOX_E_INVALID_OBJECT_STATE,
3322 tr("The given session is busy"));
3323
3324 // get the client's IInternalSessionControl interface
3325 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3326 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3327 E_INVALIDARG);
3328
3329 // session name (only used in some code paths)
3330 Utf8Str strSessionName;
3331
3332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3333
3334 if (!mData->mRegistered)
3335 return setError(E_UNEXPECTED,
3336 tr("The machine '%s' is not registered"),
3337 mUserData->s.strName.c_str());
3338
3339 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3340
3341 SessionState_T oldState = mData->mSession.mState;
3342 /* Hack: in case the session is closing and there is a progress object
3343 * which allows waiting for the session to be closed, take the opportunity
3344 * and do a limited wait (max. 1 second). This helps a lot when the system
3345 * is busy and thus session closing can take a little while. */
3346 if ( mData->mSession.mState == SessionState_Unlocking
3347 && mData->mSession.mProgress)
3348 {
3349 alock.release();
3350 mData->mSession.mProgress->WaitForCompletion(1000);
3351 alock.acquire();
3352 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3353 }
3354
3355 // try again now
3356 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3357 // (i.e. session machine exists)
3358 && (aLockType == LockType_Shared) // caller wants a shared link to the
3359 // existing session that holds the write lock:
3360 )
3361 {
3362 // OK, share the session... we are now dealing with three processes:
3363 // 1) VBoxSVC (where this code runs);
3364 // 2) process C: the caller's client process (who wants a shared session);
3365 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3366
3367 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3368 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3369 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3370 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3371 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3372
3373 /*
3374 * Release the lock before calling the client process. It's safe here
3375 * since the only thing to do after we get the lock again is to add
3376 * the remote control to the list (which doesn't directly influence
3377 * anything).
3378 */
3379 alock.release();
3380
3381 // get the console of the session holding the write lock (this is a remote call)
3382 ComPtr<IConsole> pConsoleW;
3383 if (mData->mSession.mLockType == LockType_VM)
3384 {
3385 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3386 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3387 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3388 if (FAILED(rc))
3389 // the failure may occur w/o any error info (from RPC), so provide one
3390 return setError(VBOX_E_VM_ERROR,
3391 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3392 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3393 }
3394
3395 // share the session machine and W's console with the caller's session
3396 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3397 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3398 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3399
3400 if (FAILED(rc))
3401 // the failure may occur w/o any error info (from RPC), so provide one
3402 return setError(VBOX_E_VM_ERROR,
3403 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3404 alock.acquire();
3405
3406 // need to revalidate the state after acquiring the lock again
3407 if (mData->mSession.mState != SessionState_Locked)
3408 {
3409 pSessionControl->Uninitialize();
3410 return setError(VBOX_E_INVALID_SESSION_STATE,
3411 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3412 mUserData->s.strName.c_str());
3413 }
3414
3415 // add the caller's session to the list
3416 mData->mSession.mRemoteControls.push_back(pSessionControl);
3417 }
3418 else if ( mData->mSession.mState == SessionState_Locked
3419 || mData->mSession.mState == SessionState_Unlocking
3420 )
3421 {
3422 // sharing not permitted, or machine still unlocking:
3423 return setError(VBOX_E_INVALID_OBJECT_STATE,
3424 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3425 mUserData->s.strName.c_str());
3426 }
3427 else
3428 {
3429 // machine is not locked: then write-lock the machine (create the session machine)
3430
3431 // must not be busy
3432 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3433
3434 // get the caller's session PID
3435 RTPROCESS pid = NIL_RTPROCESS;
3436 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3437 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3438 Assert(pid != NIL_RTPROCESS);
3439
3440 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3441
3442 if (fLaunchingVMProcess)
3443 {
3444 if (mData->mSession.mPID == NIL_RTPROCESS)
3445 {
3446 // two or more clients racing for a lock, the one which set the
3447 // session state to Spawning will win, the others will get an
3448 // error as we can't decide here if waiting a little would help
3449 // (only for shared locks this would avoid an error)
3450 return setError(VBOX_E_INVALID_OBJECT_STATE,
3451 tr("The machine '%s' already has a lock request pending"),
3452 mUserData->s.strName.c_str());
3453 }
3454
3455 // this machine is awaiting for a spawning session to be opened:
3456 // then the calling process must be the one that got started by
3457 // LaunchVMProcess()
3458
3459 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3460 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3461
3462#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3463 /* Hardened windows builds spawns three processes when a VM is
3464 launched, the 3rd one is the one that will end up here. */
3465 RTPROCESS ppid;
3466 int rc = RTProcQueryParent(pid, &ppid);
3467 if (RT_SUCCESS(rc))
3468 rc = RTProcQueryParent(ppid, &ppid);
3469 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3470 || rc == VERR_ACCESS_DENIED)
3471 {
3472 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3473 mData->mSession.mPID = pid;
3474 }
3475#endif
3476
3477 if (mData->mSession.mPID != pid)
3478 return setError(E_ACCESSDENIED,
3479 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3480 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3481 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3482 }
3483
3484 // create the mutable SessionMachine from the current machine
3485 ComObjPtr<SessionMachine> sessionMachine;
3486 sessionMachine.createObject();
3487 rc = sessionMachine->init(this);
3488 AssertComRC(rc);
3489
3490 /* NOTE: doing return from this function after this point but
3491 * before the end is forbidden since it may call SessionMachine::uninit()
3492 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3493 * lock while still holding the Machine lock in alock so that a deadlock
3494 * is possible due to the wrong lock order. */
3495
3496 if (SUCCEEDED(rc))
3497 {
3498 /*
3499 * Set the session state to Spawning to protect against subsequent
3500 * attempts to open a session and to unregister the machine after
3501 * we release the lock.
3502 */
3503 SessionState_T origState = mData->mSession.mState;
3504 mData->mSession.mState = SessionState_Spawning;
3505
3506#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3507 /* Get the client token ID to be passed to the client process */
3508 Utf8Str strTokenId;
3509 sessionMachine->i_getTokenId(strTokenId);
3510 Assert(!strTokenId.isEmpty());
3511#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3512 /* Get the client token to be passed to the client process */
3513 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3514 /* The token is now "owned" by pToken, fix refcount */
3515 if (!pToken.isNull())
3516 pToken->Release();
3517#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3518
3519 /*
3520 * Release the lock before calling the client process -- it will call
3521 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3522 * because the state is Spawning, so that LaunchVMProcess() and
3523 * LockMachine() calls will fail. This method, called before we
3524 * acquire the lock again, will fail because of the wrong PID.
3525 *
3526 * Note that mData->mSession.mRemoteControls accessed outside
3527 * the lock may not be modified when state is Spawning, so it's safe.
3528 */
3529 alock.release();
3530
3531 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3532#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3533 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3534#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3535 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3536 /* Now the token is owned by the client process. */
3537 pToken.setNull();
3538#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3539 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3540
3541 /* The failure may occur w/o any error info (from RPC), so provide one */
3542 if (FAILED(rc))
3543 setError(VBOX_E_VM_ERROR,
3544 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3545
3546 // get session name, either to remember or to compare against
3547 // the already known session name.
3548 {
3549 Bstr bstrSessionName;
3550 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3551 if (SUCCEEDED(rc2))
3552 strSessionName = bstrSessionName;
3553 }
3554
3555 if ( SUCCEEDED(rc)
3556 && fLaunchingVMProcess
3557 )
3558 {
3559 /* complete the remote session initialization */
3560
3561 /* get the console from the direct session */
3562 ComPtr<IConsole> console;
3563 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3564 ComAssertComRC(rc);
3565
3566 if (SUCCEEDED(rc) && !console)
3567 {
3568 ComAssert(!!console);
3569 rc = E_FAIL;
3570 }
3571
3572 /* assign machine & console to the remote session */
3573 if (SUCCEEDED(rc))
3574 {
3575 /*
3576 * after LaunchVMProcess(), the first and the only
3577 * entry in remoteControls is that remote session
3578 */
3579 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3580 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3581 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3582
3583 /* The failure may occur w/o any error info (from RPC), so provide one */
3584 if (FAILED(rc))
3585 setError(VBOX_E_VM_ERROR,
3586 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3587 }
3588
3589 if (FAILED(rc))
3590 pSessionControl->Uninitialize();
3591 }
3592
3593 /* acquire the lock again */
3594 alock.acquire();
3595
3596 /* Restore the session state */
3597 mData->mSession.mState = origState;
3598 }
3599
3600 // finalize spawning anyway (this is why we don't return on errors above)
3601 if (fLaunchingVMProcess)
3602 {
3603 Assert(mData->mSession.mName == strSessionName);
3604 /* Note that the progress object is finalized later */
3605 /** @todo Consider checking mData->mSession.mProgress for cancellation
3606 * around here. */
3607
3608 /* We don't reset mSession.mPID here because it is necessary for
3609 * SessionMachine::uninit() to reap the child process later. */
3610
3611 if (FAILED(rc))
3612 {
3613 /* Close the remote session, remove the remote control from the list
3614 * and reset session state to Closed (@note keep the code in sync
3615 * with the relevant part in checkForSpawnFailure()). */
3616
3617 Assert(mData->mSession.mRemoteControls.size() == 1);
3618 if (mData->mSession.mRemoteControls.size() == 1)
3619 {
3620 ErrorInfoKeeper eik;
3621 mData->mSession.mRemoteControls.front()->Uninitialize();
3622 }
3623
3624 mData->mSession.mRemoteControls.clear();
3625 mData->mSession.mState = SessionState_Unlocked;
3626 }
3627 }
3628 else
3629 {
3630 /* memorize PID of the directly opened session */
3631 if (SUCCEEDED(rc))
3632 mData->mSession.mPID = pid;
3633 }
3634
3635 if (SUCCEEDED(rc))
3636 {
3637 mData->mSession.mLockType = aLockType;
3638 /* memorize the direct session control and cache IUnknown for it */
3639 mData->mSession.mDirectControl = pSessionControl;
3640 mData->mSession.mState = SessionState_Locked;
3641 if (!fLaunchingVMProcess)
3642 mData->mSession.mName = strSessionName;
3643 /* associate the SessionMachine with this Machine */
3644 mData->mSession.mMachine = sessionMachine;
3645
3646 /* request an IUnknown pointer early from the remote party for later
3647 * identity checks (it will be internally cached within mDirectControl
3648 * at least on XPCOM) */
3649 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3650 NOREF(unk);
3651 }
3652
3653 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3654 * would break the lock order */
3655 alock.release();
3656
3657 /* uninitialize the created session machine on failure */
3658 if (FAILED(rc))
3659 sessionMachine->uninit();
3660 }
3661
3662 if (SUCCEEDED(rc))
3663 {
3664 /*
3665 * tell the client watcher thread to update the set of
3666 * machines that have open sessions
3667 */
3668 mParent->i_updateClientWatcher();
3669
3670 if (oldState != SessionState_Locked)
3671 /* fire an event */
3672 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3673 }
3674
3675 return rc;
3676}
3677
3678/**
3679 * @note Locks objects!
3680 */
3681HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3682 const com::Utf8Str &aName,
3683 const com::Utf8Str &aEnvironment,
3684 ComPtr<IProgress> &aProgress)
3685{
3686 Utf8Str strFrontend(aName);
3687 /* "emergencystop" doesn't need the session, so skip the checks/interface
3688 * retrieval. This code doesn't quite fit in here, but introducing a
3689 * special API method would be even more effort, and would require explicit
3690 * support by every API client. It's better to hide the feature a bit. */
3691 if (strFrontend != "emergencystop")
3692 CheckComArgNotNull(aSession);
3693
3694 HRESULT rc = S_OK;
3695 if (strFrontend.isEmpty())
3696 {
3697 Bstr bstrFrontend;
3698 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3699 if (FAILED(rc))
3700 return rc;
3701 strFrontend = bstrFrontend;
3702 if (strFrontend.isEmpty())
3703 {
3704 ComPtr<ISystemProperties> systemProperties;
3705 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3706 if (FAILED(rc))
3707 return rc;
3708 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3709 if (FAILED(rc))
3710 return rc;
3711 strFrontend = bstrFrontend;
3712 }
3713 /* paranoia - emergencystop is not a valid default */
3714 if (strFrontend == "emergencystop")
3715 strFrontend = Utf8Str::Empty;
3716 }
3717 /* default frontend: Qt GUI */
3718 if (strFrontend.isEmpty())
3719 strFrontend = "GUI/Qt";
3720
3721 if (strFrontend != "emergencystop")
3722 {
3723 /* check the session state */
3724 SessionState_T state;
3725 rc = aSession->COMGETTER(State)(&state);
3726 if (FAILED(rc))
3727 return rc;
3728
3729 if (state != SessionState_Unlocked)
3730 return setError(VBOX_E_INVALID_OBJECT_STATE,
3731 tr("The given session is busy"));
3732
3733 /* get the IInternalSessionControl interface */
3734 ComPtr<IInternalSessionControl> control(aSession);
3735 ComAssertMsgRet(!control.isNull(),
3736 ("No IInternalSessionControl interface"),
3737 E_INVALIDARG);
3738
3739 /* get the teleporter enable state for the progress object init. */
3740 BOOL fTeleporterEnabled;
3741 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3742 if (FAILED(rc))
3743 return rc;
3744
3745 /* create a progress object */
3746 ComObjPtr<ProgressProxy> progress;
3747 progress.createObject();
3748 rc = progress->init(mParent,
3749 static_cast<IMachine*>(this),
3750 Bstr(tr("Starting VM")).raw(),
3751 TRUE /* aCancelable */,
3752 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3753 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3754 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3755 2 /* uFirstOperationWeight */,
3756 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3757
3758 if (SUCCEEDED(rc))
3759 {
3760 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3761 if (SUCCEEDED(rc))
3762 {
3763 aProgress = progress;
3764
3765 /* signal the client watcher thread */
3766 mParent->i_updateClientWatcher();
3767
3768 /* fire an event */
3769 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3770 }
3771 }
3772 }
3773 else
3774 {
3775 /* no progress object - either instant success or failure */
3776 aProgress = NULL;
3777
3778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3779
3780 if (mData->mSession.mState != SessionState_Locked)
3781 return setError(VBOX_E_INVALID_OBJECT_STATE,
3782 tr("The machine '%s' is not locked by a session"),
3783 mUserData->s.strName.c_str());
3784
3785 /* must have a VM process associated - do not kill normal API clients
3786 * with an open session */
3787 if (!Global::IsOnline(mData->mMachineState))
3788 return setError(VBOX_E_INVALID_OBJECT_STATE,
3789 tr("The machine '%s' does not have a VM process"),
3790 mUserData->s.strName.c_str());
3791
3792 /* forcibly terminate the VM process */
3793 if (mData->mSession.mPID != NIL_RTPROCESS)
3794 RTProcTerminate(mData->mSession.mPID);
3795
3796 /* signal the client watcher thread, as most likely the client has
3797 * been terminated */
3798 mParent->i_updateClientWatcher();
3799 }
3800
3801 return rc;
3802}
3803
3804HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3805{
3806 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3807 return setError(E_INVALIDARG,
3808 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3809 aPosition, SchemaDefs::MaxBootPosition);
3810
3811 if (aDevice == DeviceType_USB)
3812 return setError(E_NOTIMPL,
3813 tr("Booting from USB device is currently not supported"));
3814
3815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3816
3817 HRESULT rc = i_checkStateDependency(MutableStateDep);
3818 if (FAILED(rc)) return rc;
3819
3820 i_setModified(IsModified_MachineData);
3821 mHWData.backup();
3822 mHWData->mBootOrder[aPosition - 1] = aDevice;
3823
3824 return S_OK;
3825}
3826
3827HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3828{
3829 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3830 return setError(E_INVALIDARG,
3831 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3832 aPosition, SchemaDefs::MaxBootPosition);
3833
3834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3835
3836 *aDevice = mHWData->mBootOrder[aPosition - 1];
3837
3838 return S_OK;
3839}
3840
3841HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3842 LONG aControllerPort,
3843 LONG aDevice,
3844 DeviceType_T aType,
3845 const ComPtr<IMedium> &aMedium)
3846{
3847 IMedium *aM = aMedium;
3848 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3849 aName.c_str(), aControllerPort, aDevice, aType, aM));
3850
3851 // request the host lock first, since might be calling Host methods for getting host drives;
3852 // next, protect the media tree all the while we're in here, as well as our member variables
3853 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3854 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3855
3856 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3857 if (FAILED(rc)) return rc;
3858
3859 /// @todo NEWMEDIA implicit machine registration
3860 if (!mData->mRegistered)
3861 return setError(VBOX_E_INVALID_OBJECT_STATE,
3862 tr("Cannot attach storage devices to an unregistered machine"));
3863
3864 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3865
3866 /* Check for an existing controller. */
3867 ComObjPtr<StorageController> ctl;
3868 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3869 if (FAILED(rc)) return rc;
3870
3871 StorageControllerType_T ctrlType;
3872 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3873 if (FAILED(rc))
3874 return setError(E_FAIL,
3875 tr("Could not get type of controller '%s'"),
3876 aName.c_str());
3877
3878 bool fSilent = false;
3879 Utf8Str strReconfig;
3880
3881 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3882 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3883 if ( mData->mMachineState == MachineState_Paused
3884 && strReconfig == "1")
3885 fSilent = true;
3886
3887 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3888 bool fHotplug = false;
3889 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3890 fHotplug = true;
3891
3892 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3893 return setError(VBOX_E_INVALID_VM_STATE,
3894 tr("Controller '%s' does not support hotplugging"),
3895 aName.c_str());
3896
3897 // check that the port and device are not out of range
3898 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3899 if (FAILED(rc)) return rc;
3900
3901 /* check if the device slot is already busy */
3902 MediumAttachment *pAttachTemp;
3903 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3904 aName,
3905 aControllerPort,
3906 aDevice)))
3907 {
3908 Medium *pMedium = pAttachTemp->i_getMedium();
3909 if (pMedium)
3910 {
3911 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3912 return setError(VBOX_E_OBJECT_IN_USE,
3913 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3914 pMedium->i_getLocationFull().c_str(),
3915 aControllerPort,
3916 aDevice,
3917 aName.c_str());
3918 }
3919 else
3920 return setError(VBOX_E_OBJECT_IN_USE,
3921 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3922 aControllerPort, aDevice, aName.c_str());
3923 }
3924
3925 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3926 if (aMedium && medium.isNull())
3927 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3928
3929 AutoCaller mediumCaller(medium);
3930 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3931
3932 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3933
3934 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3935 && !medium.isNull()
3936 )
3937 return setError(VBOX_E_OBJECT_IN_USE,
3938 tr("Medium '%s' is already attached to this virtual machine"),
3939 medium->i_getLocationFull().c_str());
3940
3941 if (!medium.isNull())
3942 {
3943 MediumType_T mtype = medium->i_getType();
3944 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3945 // For DVDs it's not written to the config file, so needs no global config
3946 // version bump. For floppies it's a new attribute "type", which is ignored
3947 // by older VirtualBox version, so needs no global config version bump either.
3948 // For hard disks this type is not accepted.
3949 if (mtype == MediumType_MultiAttach)
3950 {
3951 // This type is new with VirtualBox 4.0 and therefore requires settings
3952 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3953 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3954 // two reasons: The medium type is a property of the media registry tree, which
3955 // can reside in the global config file (for pre-4.0 media); we would therefore
3956 // possibly need to bump the global config version. We don't want to do that though
3957 // because that might make downgrading to pre-4.0 impossible.
3958 // As a result, we can only use these two new types if the medium is NOT in the
3959 // global registry:
3960 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3961 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3962 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3963 )
3964 return setError(VBOX_E_INVALID_OBJECT_STATE,
3965 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3966 "to machines that were created with VirtualBox 4.0 or later"),
3967 medium->i_getLocationFull().c_str());
3968 }
3969 }
3970
3971 bool fIndirect = false;
3972 if (!medium.isNull())
3973 fIndirect = medium->i_isReadOnly();
3974 bool associate = true;
3975
3976 do
3977 {
3978 if ( aType == DeviceType_HardDisk
3979 && mMediumAttachments.isBackedUp())
3980 {
3981 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3982
3983 /* check if the medium was attached to the VM before we started
3984 * changing attachments in which case the attachment just needs to
3985 * be restored */
3986 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3987 {
3988 AssertReturn(!fIndirect, E_FAIL);
3989
3990 /* see if it's the same bus/channel/device */
3991 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3992 {
3993 /* the simplest case: restore the whole attachment
3994 * and return, nothing else to do */
3995 mMediumAttachments->push_back(pAttachTemp);
3996
3997 /* Reattach the medium to the VM. */
3998 if (fHotplug || fSilent)
3999 {
4000 mediumLock.release();
4001 treeLock.release();
4002 alock.release();
4003
4004 MediumLockList *pMediumLockList(new MediumLockList());
4005
4006 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4007 medium /* pToLockWrite */,
4008 false /* fMediumLockWriteAll */,
4009 NULL,
4010 *pMediumLockList);
4011 alock.acquire();
4012 if (FAILED(rc))
4013 delete pMediumLockList;
4014 else
4015 {
4016 mData->mSession.mLockedMedia.Unlock();
4017 alock.release();
4018 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4019 mData->mSession.mLockedMedia.Lock();
4020 alock.acquire();
4021 }
4022 alock.release();
4023
4024 if (SUCCEEDED(rc))
4025 {
4026 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4027 /* Remove lock list in case of error. */
4028 if (FAILED(rc))
4029 {
4030 mData->mSession.mLockedMedia.Unlock();
4031 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4032 mData->mSession.mLockedMedia.Lock();
4033 }
4034 }
4035 }
4036
4037 return S_OK;
4038 }
4039
4040 /* bus/channel/device differ; we need a new attachment object,
4041 * but don't try to associate it again */
4042 associate = false;
4043 break;
4044 }
4045 }
4046
4047 /* go further only if the attachment is to be indirect */
4048 if (!fIndirect)
4049 break;
4050
4051 /* perform the so called smart attachment logic for indirect
4052 * attachments. Note that smart attachment is only applicable to base
4053 * hard disks. */
4054
4055 if (medium->i_getParent().isNull())
4056 {
4057 /* first, investigate the backup copy of the current hard disk
4058 * attachments to make it possible to re-attach existing diffs to
4059 * another device slot w/o losing their contents */
4060 if (mMediumAttachments.isBackedUp())
4061 {
4062 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4063
4064 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4065 uint32_t foundLevel = 0;
4066
4067 for (MediumAttachmentList::const_iterator
4068 it = oldAtts.begin();
4069 it != oldAtts.end();
4070 ++it)
4071 {
4072 uint32_t level = 0;
4073 MediumAttachment *pAttach = *it;
4074 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4075 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4076 if (pMedium.isNull())
4077 continue;
4078
4079 if (pMedium->i_getBase(&level) == medium)
4080 {
4081 /* skip the hard disk if its currently attached (we
4082 * cannot attach the same hard disk twice) */
4083 if (i_findAttachment(*mMediumAttachments.data(),
4084 pMedium))
4085 continue;
4086
4087 /* matched device, channel and bus (i.e. attached to the
4088 * same place) will win and immediately stop the search;
4089 * otherwise the attachment that has the youngest
4090 * descendant of medium will be used
4091 */
4092 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4093 {
4094 /* the simplest case: restore the whole attachment
4095 * and return, nothing else to do */
4096 mMediumAttachments->push_back(*it);
4097
4098 /* Reattach the medium to the VM. */
4099 if (fHotplug || fSilent)
4100 {
4101 mediumLock.release();
4102 treeLock.release();
4103 alock.release();
4104
4105 MediumLockList *pMediumLockList(new MediumLockList());
4106
4107 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4108 medium /* pToLockWrite */,
4109 false /* fMediumLockWriteAll */,
4110 NULL,
4111 *pMediumLockList);
4112 alock.acquire();
4113 if (FAILED(rc))
4114 delete pMediumLockList;
4115 else
4116 {
4117 mData->mSession.mLockedMedia.Unlock();
4118 alock.release();
4119 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4120 mData->mSession.mLockedMedia.Lock();
4121 alock.acquire();
4122 }
4123 alock.release();
4124
4125 if (SUCCEEDED(rc))
4126 {
4127 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4128 /* Remove lock list in case of error. */
4129 if (FAILED(rc))
4130 {
4131 mData->mSession.mLockedMedia.Unlock();
4132 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4133 mData->mSession.mLockedMedia.Lock();
4134 }
4135 }
4136 }
4137
4138 return S_OK;
4139 }
4140 else if ( foundIt == oldAtts.end()
4141 || level > foundLevel /* prefer younger */
4142 )
4143 {
4144 foundIt = it;
4145 foundLevel = level;
4146 }
4147 }
4148 }
4149
4150 if (foundIt != oldAtts.end())
4151 {
4152 /* use the previously attached hard disk */
4153 medium = (*foundIt)->i_getMedium();
4154 mediumCaller.attach(medium);
4155 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4156 mediumLock.attach(medium);
4157 /* not implicit, doesn't require association with this VM */
4158 fIndirect = false;
4159 associate = false;
4160 /* go right to the MediumAttachment creation */
4161 break;
4162 }
4163 }
4164
4165 /* must give up the medium lock and medium tree lock as below we
4166 * go over snapshots, which needs a lock with higher lock order. */
4167 mediumLock.release();
4168 treeLock.release();
4169
4170 /* then, search through snapshots for the best diff in the given
4171 * hard disk's chain to base the new diff on */
4172
4173 ComObjPtr<Medium> base;
4174 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4175 while (snap)
4176 {
4177 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4178
4179 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4180
4181 MediumAttachment *pAttachFound = NULL;
4182 uint32_t foundLevel = 0;
4183
4184 for (MediumAttachmentList::const_iterator
4185 it = snapAtts.begin();
4186 it != snapAtts.end();
4187 ++it)
4188 {
4189 MediumAttachment *pAttach = *it;
4190 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4191 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4192 if (pMedium.isNull())
4193 continue;
4194
4195 uint32_t level = 0;
4196 if (pMedium->i_getBase(&level) == medium)
4197 {
4198 /* matched device, channel and bus (i.e. attached to the
4199 * same place) will win and immediately stop the search;
4200 * otherwise the attachment that has the youngest
4201 * descendant of medium will be used
4202 */
4203 if ( pAttach->i_getDevice() == aDevice
4204 && pAttach->i_getPort() == aControllerPort
4205 && pAttach->i_getControllerName() == aName
4206 )
4207 {
4208 pAttachFound = pAttach;
4209 break;
4210 }
4211 else if ( !pAttachFound
4212 || level > foundLevel /* prefer younger */
4213 )
4214 {
4215 pAttachFound = pAttach;
4216 foundLevel = level;
4217 }
4218 }
4219 }
4220
4221 if (pAttachFound)
4222 {
4223 base = pAttachFound->i_getMedium();
4224 break;
4225 }
4226
4227 snap = snap->i_getParent();
4228 }
4229
4230 /* re-lock medium tree and the medium, as we need it below */
4231 treeLock.acquire();
4232 mediumLock.acquire();
4233
4234 /* found a suitable diff, use it as a base */
4235 if (!base.isNull())
4236 {
4237 medium = base;
4238 mediumCaller.attach(medium);
4239 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4240 mediumLock.attach(medium);
4241 }
4242 }
4243
4244 Utf8Str strFullSnapshotFolder;
4245 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4246
4247 ComObjPtr<Medium> diff;
4248 diff.createObject();
4249 // store this diff in the same registry as the parent
4250 Guid uuidRegistryParent;
4251 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4252 {
4253 // parent image has no registry: this can happen if we're attaching a new immutable
4254 // image that has not yet been attached (medium then points to the base and we're
4255 // creating the diff image for the immutable, and the parent is not yet registered);
4256 // put the parent in the machine registry then
4257 mediumLock.release();
4258 treeLock.release();
4259 alock.release();
4260 i_addMediumToRegistry(medium);
4261 alock.acquire();
4262 treeLock.acquire();
4263 mediumLock.acquire();
4264 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4265 }
4266 rc = diff->init(mParent,
4267 medium->i_getPreferredDiffFormat(),
4268 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4269 uuidRegistryParent,
4270 DeviceType_HardDisk);
4271 if (FAILED(rc)) return rc;
4272
4273 /* Apply the normal locking logic to the entire chain. */
4274 MediumLockList *pMediumLockList(new MediumLockList());
4275 mediumLock.release();
4276 treeLock.release();
4277 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4278 diff /* pToLockWrite */,
4279 false /* fMediumLockWriteAll */,
4280 medium,
4281 *pMediumLockList);
4282 treeLock.acquire();
4283 mediumLock.acquire();
4284 if (SUCCEEDED(rc))
4285 {
4286 mediumLock.release();
4287 treeLock.release();
4288 rc = pMediumLockList->Lock();
4289 treeLock.acquire();
4290 mediumLock.acquire();
4291 if (FAILED(rc))
4292 setError(rc,
4293 tr("Could not lock medium when creating diff '%s'"),
4294 diff->i_getLocationFull().c_str());
4295 else
4296 {
4297 /* will release the lock before the potentially lengthy
4298 * operation, so protect with the special state */
4299 MachineState_T oldState = mData->mMachineState;
4300 i_setMachineState(MachineState_SettingUp);
4301
4302 mediumLock.release();
4303 treeLock.release();
4304 alock.release();
4305
4306 rc = medium->i_createDiffStorage(diff,
4307 medium->i_getPreferredDiffVariant(),
4308 pMediumLockList,
4309 NULL /* aProgress */,
4310 true /* aWait */);
4311
4312 alock.acquire();
4313 treeLock.acquire();
4314 mediumLock.acquire();
4315
4316 i_setMachineState(oldState);
4317 }
4318 }
4319
4320 /* Unlock the media and free the associated memory. */
4321 delete pMediumLockList;
4322
4323 if (FAILED(rc)) return rc;
4324
4325 /* use the created diff for the actual attachment */
4326 medium = diff;
4327 mediumCaller.attach(medium);
4328 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4329 mediumLock.attach(medium);
4330 }
4331 while (0);
4332
4333 ComObjPtr<MediumAttachment> attachment;
4334 attachment.createObject();
4335 rc = attachment->init(this,
4336 medium,
4337 aName,
4338 aControllerPort,
4339 aDevice,
4340 aType,
4341 fIndirect,
4342 false /* fPassthrough */,
4343 false /* fTempEject */,
4344 false /* fNonRotational */,
4345 false /* fDiscard */,
4346 fHotplug /* fHotPluggable */,
4347 Utf8Str::Empty);
4348 if (FAILED(rc)) return rc;
4349
4350 if (associate && !medium.isNull())
4351 {
4352 // as the last step, associate the medium to the VM
4353 rc = medium->i_addBackReference(mData->mUuid);
4354 // here we can fail because of Deleting, or being in process of creating a Diff
4355 if (FAILED(rc)) return rc;
4356
4357 mediumLock.release();
4358 treeLock.release();
4359 alock.release();
4360 i_addMediumToRegistry(medium);
4361 alock.acquire();
4362 treeLock.acquire();
4363 mediumLock.acquire();
4364 }
4365
4366 /* success: finally remember the attachment */
4367 i_setModified(IsModified_Storage);
4368 mMediumAttachments.backup();
4369 mMediumAttachments->push_back(attachment);
4370
4371 mediumLock.release();
4372 treeLock.release();
4373 alock.release();
4374
4375 if (fHotplug || fSilent)
4376 {
4377 if (!medium.isNull())
4378 {
4379 MediumLockList *pMediumLockList(new MediumLockList());
4380
4381 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4382 medium /* pToLockWrite */,
4383 false /* fMediumLockWriteAll */,
4384 NULL,
4385 *pMediumLockList);
4386 alock.acquire();
4387 if (FAILED(rc))
4388 delete pMediumLockList;
4389 else
4390 {
4391 mData->mSession.mLockedMedia.Unlock();
4392 alock.release();
4393 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4394 mData->mSession.mLockedMedia.Lock();
4395 alock.acquire();
4396 }
4397 alock.release();
4398 }
4399
4400 if (SUCCEEDED(rc))
4401 {
4402 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4403 /* Remove lock list in case of error. */
4404 if (FAILED(rc))
4405 {
4406 mData->mSession.mLockedMedia.Unlock();
4407 mData->mSession.mLockedMedia.Remove(attachment);
4408 mData->mSession.mLockedMedia.Lock();
4409 }
4410 }
4411 }
4412
4413 /* Save modified registries, but skip this machine as it's the caller's
4414 * job to save its settings like all other settings changes. */
4415 mParent->i_unmarkRegistryModified(i_getId());
4416 mParent->i_saveModifiedRegistries();
4417
4418 return rc;
4419}
4420
4421HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4422 LONG aDevice)
4423{
4424 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4425 aName.c_str(), aControllerPort, aDevice));
4426
4427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4428
4429 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4430 if (FAILED(rc)) return rc;
4431
4432 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4433
4434 /* Check for an existing controller. */
4435 ComObjPtr<StorageController> ctl;
4436 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4437 if (FAILED(rc)) return rc;
4438
4439 StorageControllerType_T ctrlType;
4440 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4441 if (FAILED(rc))
4442 return setError(E_FAIL,
4443 tr("Could not get type of controller '%s'"),
4444 aName.c_str());
4445
4446 bool fSilent = false;
4447 Utf8Str strReconfig;
4448
4449 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4450 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4451 if ( mData->mMachineState == MachineState_Paused
4452 && strReconfig == "1")
4453 fSilent = true;
4454
4455 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4456 bool fHotplug = false;
4457 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4458 fHotplug = true;
4459
4460 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4461 return setError(VBOX_E_INVALID_VM_STATE,
4462 tr("Controller '%s' does not support hotplugging"),
4463 aName.c_str());
4464
4465 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4466 aName,
4467 aControllerPort,
4468 aDevice);
4469 if (!pAttach)
4470 return setError(VBOX_E_OBJECT_NOT_FOUND,
4471 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4472 aDevice, aControllerPort, aName.c_str());
4473
4474 if (fHotplug && !pAttach->i_getHotPluggable())
4475 return setError(VBOX_E_NOT_SUPPORTED,
4476 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4477 aDevice, aControllerPort, aName.c_str());
4478
4479 /*
4480 * The VM has to detach the device before we delete any implicit diffs.
4481 * If this fails we can roll back without loosing data.
4482 */
4483 if (fHotplug || fSilent)
4484 {
4485 alock.release();
4486 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4487 alock.acquire();
4488 }
4489 if (FAILED(rc)) return rc;
4490
4491 /* If we are here everything went well and we can delete the implicit now. */
4492 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4493
4494 alock.release();
4495
4496 /* Save modified registries, but skip this machine as it's the caller's
4497 * job to save its settings like all other settings changes. */
4498 mParent->i_unmarkRegistryModified(i_getId());
4499 mParent->i_saveModifiedRegistries();
4500
4501 return rc;
4502}
4503
4504HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4505 LONG aDevice, BOOL aPassthrough)
4506{
4507 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4508 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4509
4510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4511
4512 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4513 if (FAILED(rc)) return rc;
4514
4515 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4516
4517 /* Check for an existing controller. */
4518 ComObjPtr<StorageController> ctl;
4519 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4520 if (FAILED(rc)) return rc;
4521
4522 StorageControllerType_T ctrlType;
4523 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4524 if (FAILED(rc))
4525 return setError(E_FAIL,
4526 tr("Could not get type of controller '%s'"),
4527 aName.c_str());
4528
4529 bool fSilent = false;
4530 Utf8Str strReconfig;
4531
4532 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4533 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4534 if ( mData->mMachineState == MachineState_Paused
4535 && strReconfig == "1")
4536 fSilent = true;
4537
4538 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4539 bool fHotplug = false;
4540 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4541 fHotplug = true;
4542
4543 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4544 return setError(VBOX_E_INVALID_VM_STATE,
4545 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4546 aName.c_str());
4547
4548 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4549 aName,
4550 aControllerPort,
4551 aDevice);
4552 if (!pAttach)
4553 return setError(VBOX_E_OBJECT_NOT_FOUND,
4554 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4555 aDevice, aControllerPort, aName.c_str());
4556
4557
4558 i_setModified(IsModified_Storage);
4559 mMediumAttachments.backup();
4560
4561 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4562
4563 if (pAttach->i_getType() != DeviceType_DVD)
4564 return setError(E_INVALIDARG,
4565 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4566 aDevice, aControllerPort, aName.c_str());
4567 pAttach->i_updatePassthrough(!!aPassthrough);
4568
4569 attLock.release();
4570 alock.release();
4571 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4572
4573 return rc;
4574}
4575
4576HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4577 LONG aDevice, BOOL aTemporaryEject)
4578{
4579
4580 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4581 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4582
4583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4584
4585 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4586 if (FAILED(rc)) return rc;
4587
4588 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4589 aName,
4590 aControllerPort,
4591 aDevice);
4592 if (!pAttach)
4593 return setError(VBOX_E_OBJECT_NOT_FOUND,
4594 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4595 aDevice, aControllerPort, aName.c_str());
4596
4597
4598 i_setModified(IsModified_Storage);
4599 mMediumAttachments.backup();
4600
4601 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4602
4603 if (pAttach->i_getType() != DeviceType_DVD)
4604 return setError(E_INVALIDARG,
4605 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4606 aDevice, aControllerPort, aName.c_str());
4607 pAttach->i_updateTempEject(!!aTemporaryEject);
4608
4609 return S_OK;
4610}
4611
4612HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4613 LONG aDevice, BOOL aNonRotational)
4614{
4615
4616 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4617 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4618
4619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4620
4621 HRESULT rc = i_checkStateDependency(MutableStateDep);
4622 if (FAILED(rc)) return rc;
4623
4624 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4625
4626 if (Global::IsOnlineOrTransient(mData->mMachineState))
4627 return setError(VBOX_E_INVALID_VM_STATE,
4628 tr("Invalid machine state: %s"),
4629 Global::stringifyMachineState(mData->mMachineState));
4630
4631 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4632 aName,
4633 aControllerPort,
4634 aDevice);
4635 if (!pAttach)
4636 return setError(VBOX_E_OBJECT_NOT_FOUND,
4637 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4638 aDevice, aControllerPort, aName.c_str());
4639
4640
4641 i_setModified(IsModified_Storage);
4642 mMediumAttachments.backup();
4643
4644 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4645
4646 if (pAttach->i_getType() != DeviceType_HardDisk)
4647 return setError(E_INVALIDARG,
4648 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"),
4649 aDevice, aControllerPort, aName.c_str());
4650 pAttach->i_updateNonRotational(!!aNonRotational);
4651
4652 return S_OK;
4653}
4654
4655HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4656 LONG aDevice, BOOL aDiscard)
4657{
4658
4659 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4660 aName.c_str(), aControllerPort, aDevice, aDiscard));
4661
4662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4663
4664 HRESULT rc = i_checkStateDependency(MutableStateDep);
4665 if (FAILED(rc)) return rc;
4666
4667 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4668
4669 if (Global::IsOnlineOrTransient(mData->mMachineState))
4670 return setError(VBOX_E_INVALID_VM_STATE,
4671 tr("Invalid machine state: %s"),
4672 Global::stringifyMachineState(mData->mMachineState));
4673
4674 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4675 aName,
4676 aControllerPort,
4677 aDevice);
4678 if (!pAttach)
4679 return setError(VBOX_E_OBJECT_NOT_FOUND,
4680 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4681 aDevice, aControllerPort, aName.c_str());
4682
4683
4684 i_setModified(IsModified_Storage);
4685 mMediumAttachments.backup();
4686
4687 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4688
4689 if (pAttach->i_getType() != DeviceType_HardDisk)
4690 return setError(E_INVALIDARG,
4691 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"),
4692 aDevice, aControllerPort, aName.c_str());
4693 pAttach->i_updateDiscard(!!aDiscard);
4694
4695 return S_OK;
4696}
4697
4698HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4699 LONG aDevice, BOOL aHotPluggable)
4700{
4701 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4702 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4703
4704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4705
4706 HRESULT rc = i_checkStateDependency(MutableStateDep);
4707 if (FAILED(rc)) return rc;
4708
4709 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4710
4711 if (Global::IsOnlineOrTransient(mData->mMachineState))
4712 return setError(VBOX_E_INVALID_VM_STATE,
4713 tr("Invalid machine state: %s"),
4714 Global::stringifyMachineState(mData->mMachineState));
4715
4716 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4717 aName,
4718 aControllerPort,
4719 aDevice);
4720 if (!pAttach)
4721 return setError(VBOX_E_OBJECT_NOT_FOUND,
4722 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4723 aDevice, aControllerPort, aName.c_str());
4724
4725 /* Check for an existing controller. */
4726 ComObjPtr<StorageController> ctl;
4727 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4728 if (FAILED(rc)) return rc;
4729
4730 StorageControllerType_T ctrlType;
4731 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4732 if (FAILED(rc))
4733 return setError(E_FAIL,
4734 tr("Could not get type of controller '%s'"),
4735 aName.c_str());
4736
4737 if (!i_isControllerHotplugCapable(ctrlType))
4738 return setError(VBOX_E_NOT_SUPPORTED,
4739 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4740 aName.c_str());
4741
4742 i_setModified(IsModified_Storage);
4743 mMediumAttachments.backup();
4744
4745 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4746
4747 if (pAttach->i_getType() == DeviceType_Floppy)
4748 return setError(E_INVALIDARG,
4749 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"),
4750 aDevice, aControllerPort, aName.c_str());
4751 pAttach->i_updateHotPluggable(!!aHotPluggable);
4752
4753 return S_OK;
4754}
4755
4756HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4757 LONG aDevice)
4758{
4759 int rc = S_OK;
4760 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4761 aName.c_str(), aControllerPort, aDevice));
4762
4763 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4764
4765 return rc;
4766}
4767
4768HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4769 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4770{
4771 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4772 aName.c_str(), aControllerPort, aDevice));
4773
4774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4775
4776 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4777 if (FAILED(rc)) return rc;
4778
4779 if (Global::IsOnlineOrTransient(mData->mMachineState))
4780 return setError(VBOX_E_INVALID_VM_STATE,
4781 tr("Invalid machine state: %s"),
4782 Global::stringifyMachineState(mData->mMachineState));
4783
4784 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4785 aName,
4786 aControllerPort,
4787 aDevice);
4788 if (!pAttach)
4789 return setError(VBOX_E_OBJECT_NOT_FOUND,
4790 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4791 aDevice, aControllerPort, aName.c_str());
4792
4793
4794 i_setModified(IsModified_Storage);
4795 mMediumAttachments.backup();
4796
4797 IBandwidthGroup *iB = aBandwidthGroup;
4798 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4799 if (aBandwidthGroup && group.isNull())
4800 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4801
4802 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4803
4804 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4805 if (strBandwidthGroupOld.isNotEmpty())
4806 {
4807 /* Get the bandwidth group object and release it - this must not fail. */
4808 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4809 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4810 Assert(SUCCEEDED(rc));
4811
4812 pBandwidthGroupOld->i_release();
4813 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4814 }
4815
4816 if (!group.isNull())
4817 {
4818 group->i_reference();
4819 pAttach->i_updateBandwidthGroup(group->i_getName());
4820 }
4821
4822 return S_OK;
4823}
4824
4825HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4826 LONG aControllerPort,
4827 LONG aDevice,
4828 DeviceType_T aType)
4829{
4830 HRESULT rc = S_OK;
4831
4832 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4833 aName.c_str(), aControllerPort, aDevice, aType));
4834
4835 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4836
4837 return rc;
4838}
4839
4840
4841HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4842 LONG aControllerPort,
4843 LONG aDevice,
4844 BOOL aForce)
4845{
4846 int rc = S_OK;
4847 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4848 aName.c_str(), aControllerPort, aForce));
4849
4850 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4851
4852 return rc;
4853}
4854
4855HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4856 LONG aControllerPort,
4857 LONG aDevice,
4858 const ComPtr<IMedium> &aMedium,
4859 BOOL aForce)
4860{
4861 int rc = S_OK;
4862 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4863 aName.c_str(), aControllerPort, aDevice, aForce));
4864
4865 // request the host lock first, since might be calling Host methods for getting host drives;
4866 // next, protect the media tree all the while we're in here, as well as our member variables
4867 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4868 this->lockHandle(),
4869 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4870
4871 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4872 aName,
4873 aControllerPort,
4874 aDevice);
4875 if (pAttach.isNull())
4876 return setError(VBOX_E_OBJECT_NOT_FOUND,
4877 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4878 aDevice, aControllerPort, aName.c_str());
4879
4880 /* Remember previously mounted medium. The medium before taking the
4881 * backup is not necessarily the same thing. */
4882 ComObjPtr<Medium> oldmedium;
4883 oldmedium = pAttach->i_getMedium();
4884
4885 IMedium *iM = aMedium;
4886 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4887 if (aMedium && pMedium.isNull())
4888 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4889
4890 AutoCaller mediumCaller(pMedium);
4891 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4892
4893 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4894 if (pMedium)
4895 {
4896 DeviceType_T mediumType = pAttach->i_getType();
4897 switch (mediumType)
4898 {
4899 case DeviceType_DVD:
4900 case DeviceType_Floppy:
4901 break;
4902
4903 default:
4904 return setError(VBOX_E_INVALID_OBJECT_STATE,
4905 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4906 aControllerPort,
4907 aDevice,
4908 aName.c_str());
4909 }
4910 }
4911
4912 i_setModified(IsModified_Storage);
4913 mMediumAttachments.backup();
4914
4915 {
4916 // The backup operation makes the pAttach reference point to the
4917 // old settings. Re-get the correct reference.
4918 pAttach = i_findAttachment(*mMediumAttachments.data(),
4919 aName,
4920 aControllerPort,
4921 aDevice);
4922 if (!oldmedium.isNull())
4923 oldmedium->i_removeBackReference(mData->mUuid);
4924 if (!pMedium.isNull())
4925 {
4926 pMedium->i_addBackReference(mData->mUuid);
4927
4928 mediumLock.release();
4929 multiLock.release();
4930 i_addMediumToRegistry(pMedium);
4931 multiLock.acquire();
4932 mediumLock.acquire();
4933 }
4934
4935 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4936 pAttach->i_updateMedium(pMedium);
4937 }
4938
4939 i_setModified(IsModified_Storage);
4940
4941 mediumLock.release();
4942 multiLock.release();
4943 rc = i_onMediumChange(pAttach, aForce);
4944 multiLock.acquire();
4945 mediumLock.acquire();
4946
4947 /* On error roll back this change only. */
4948 if (FAILED(rc))
4949 {
4950 if (!pMedium.isNull())
4951 pMedium->i_removeBackReference(mData->mUuid);
4952 pAttach = i_findAttachment(*mMediumAttachments.data(),
4953 aName,
4954 aControllerPort,
4955 aDevice);
4956 /* If the attachment is gone in the meantime, bail out. */
4957 if (pAttach.isNull())
4958 return rc;
4959 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4960 if (!oldmedium.isNull())
4961 oldmedium->i_addBackReference(mData->mUuid);
4962 pAttach->i_updateMedium(oldmedium);
4963 }
4964
4965 mediumLock.release();
4966 multiLock.release();
4967
4968 /* Save modified registries, but skip this machine as it's the caller's
4969 * job to save its settings like all other settings changes. */
4970 mParent->i_unmarkRegistryModified(i_getId());
4971 mParent->i_saveModifiedRegistries();
4972
4973 return rc;
4974}
4975HRESULT Machine::getMedium(const com::Utf8Str &aName,
4976 LONG aControllerPort,
4977 LONG aDevice,
4978 ComPtr<IMedium> &aMedium)
4979{
4980 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4981 aName.c_str(), aControllerPort, aDevice));
4982
4983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4984
4985 aMedium = NULL;
4986
4987 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4988 aName,
4989 aControllerPort,
4990 aDevice);
4991 if (pAttach.isNull())
4992 return setError(VBOX_E_OBJECT_NOT_FOUND,
4993 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4994 aDevice, aControllerPort, aName.c_str());
4995
4996 aMedium = pAttach->i_getMedium();
4997
4998 return S_OK;
4999}
5000
5001HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
5002{
5003
5004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5005
5006 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5007
5008 return S_OK;
5009}
5010
5011HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5012{
5013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5014
5015 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5016
5017 return S_OK;
5018}
5019
5020HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5021{
5022 /* Do not assert if slot is out of range, just return the advertised
5023 status. testdriver/vbox.py triggers this in logVmInfo. */
5024 if (aSlot >= mNetworkAdapters.size())
5025 return setError(E_INVALIDARG,
5026 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5027 aSlot, mNetworkAdapters.size());
5028
5029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5030
5031 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5032
5033 return S_OK;
5034}
5035
5036HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5037{
5038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5039
5040 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5041 size_t i = 0;
5042 for (settings::StringsMap::const_iterator
5043 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5044 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5045 ++it, ++i)
5046 aKeys[i] = it->first;
5047
5048 return S_OK;
5049}
5050
5051 /**
5052 * @note Locks this object for reading.
5053 */
5054HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5055 com::Utf8Str &aValue)
5056{
5057 /* start with nothing found */
5058 aValue = "";
5059
5060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5061
5062 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5063 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5064 // found:
5065 aValue = it->second; // source is a Utf8Str
5066
5067 /* return the result to caller (may be empty) */
5068 return S_OK;
5069}
5070
5071 /**
5072 * @note Locks mParent for writing + this object for writing.
5073 */
5074HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5075{
5076 /* Because non-ASCII characters in aKey have caused problems in the settings
5077 * they are rejected unless the key should be deleted. */
5078 if (!aValue.isEmpty())
5079 {
5080 for (size_t i = 0; i < aKey.length(); ++i)
5081 {
5082 char ch = aKey[i];
5083 if (!RTLocCIsPrint(ch))
5084 return E_INVALIDARG;
5085 }
5086 }
5087
5088 Utf8Str strOldValue; // empty
5089
5090 // locking note: we only hold the read lock briefly to look up the old value,
5091 // then release it and call the onExtraCanChange callbacks. There is a small
5092 // chance of a race insofar as the callback might be called twice if two callers
5093 // change the same key at the same time, but that's a much better solution
5094 // than the deadlock we had here before. The actual changing of the extradata
5095 // is then performed under the write lock and race-free.
5096
5097 // look up the old value first; if nothing has changed then we need not do anything
5098 {
5099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5100
5101 // For snapshots don't even think about allowing changes, extradata
5102 // is global for a machine, so there is nothing snapshot specific.
5103 if (i_isSnapshotMachine())
5104 return setError(VBOX_E_INVALID_VM_STATE,
5105 tr("Cannot set extradata for a snapshot"));
5106
5107 // check if the right IMachine instance is used
5108 if (mData->mRegistered && !i_isSessionMachine())
5109 return setError(VBOX_E_INVALID_VM_STATE,
5110 tr("Cannot set extradata for an immutable machine"));
5111
5112 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5113 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5114 strOldValue = it->second;
5115 }
5116
5117 bool fChanged;
5118 if ((fChanged = (strOldValue != aValue)))
5119 {
5120 // ask for permission from all listeners outside the locks;
5121 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5122 // lock to copy the list of callbacks to invoke
5123 Bstr error;
5124 Bstr bstrValue(aValue);
5125
5126 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5127 {
5128 const char *sep = error.isEmpty() ? "" : ": ";
5129 CBSTR err = error.raw();
5130 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5131 return setError(E_ACCESSDENIED,
5132 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5133 aKey.c_str(),
5134 aValue.c_str(),
5135 sep,
5136 err);
5137 }
5138
5139 // data is changing and change not vetoed: then write it out under the lock
5140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5141
5142 if (aValue.isEmpty())
5143 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5144 else
5145 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5146 // creates a new key if needed
5147
5148 bool fNeedsGlobalSaveSettings = false;
5149 // This saving of settings is tricky: there is no "old state" for the
5150 // extradata items at all (unlike all other settings), so the old/new
5151 // settings comparison would give a wrong result!
5152 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5153
5154 if (fNeedsGlobalSaveSettings)
5155 {
5156 // save the global settings; for that we should hold only the VirtualBox lock
5157 alock.release();
5158 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5159 mParent->i_saveSettings();
5160 }
5161 }
5162
5163 // fire notification outside the lock
5164 if (fChanged)
5165 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5166
5167 return S_OK;
5168}
5169
5170HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5171{
5172 aProgress = NULL;
5173 NOREF(aSettingsFilePath);
5174 ReturnComNotImplemented();
5175}
5176
5177HRESULT Machine::saveSettings()
5178{
5179 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5180
5181 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5182 if (FAILED(rc)) return rc;
5183
5184 /* the settings file path may never be null */
5185 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5186
5187 /* save all VM data excluding snapshots */
5188 bool fNeedsGlobalSaveSettings = false;
5189 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5190 mlock.release();
5191
5192 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5193 {
5194 // save the global settings; for that we should hold only the VirtualBox lock
5195 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5196 rc = mParent->i_saveSettings();
5197 }
5198
5199 return rc;
5200}
5201
5202
5203HRESULT Machine::discardSettings()
5204{
5205 /*
5206 * We need to take the machine list lock here as well as the machine one
5207 * or we'll get into trouble should any media stuff require rolling back.
5208 *
5209 * Details:
5210 *
5211 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5212 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5213 * 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]
5214 * 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
5215 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5216 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5217 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5218 * 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
5219 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5220 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5221 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5222 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5223 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5224 * 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]
5225 * 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] (*)
5226 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5227 * 0:005> k
5228 * # Child-SP RetAddr Call Site
5229 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5230 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5231 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5232 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5233 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5234 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5235 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5236 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5237 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5238 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5239 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5240 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5241 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5242 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5243 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5244 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5245 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5246 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5247 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5248 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5249 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5250 *
5251 */
5252 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5254
5255 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5256 if (FAILED(rc)) return rc;
5257
5258 /*
5259 * during this rollback, the session will be notified if data has
5260 * been actually changed
5261 */
5262 i_rollback(true /* aNotify */);
5263
5264 return S_OK;
5265}
5266
5267/** @note Locks objects! */
5268HRESULT Machine::unregister(AutoCaller &autoCaller,
5269 CleanupMode_T aCleanupMode,
5270 std::vector<ComPtr<IMedium> > &aMedia)
5271{
5272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5273
5274 Guid id(i_getId());
5275
5276 if (mData->mSession.mState != SessionState_Unlocked)
5277 return setError(VBOX_E_INVALID_OBJECT_STATE,
5278 tr("Cannot unregister the machine '%s' while it is locked"),
5279 mUserData->s.strName.c_str());
5280
5281 // wait for state dependents to drop to zero
5282 i_ensureNoStateDependencies();
5283
5284 if (!mData->mAccessible)
5285 {
5286 // inaccessible maschines can only be unregistered; uninitialize ourselves
5287 // here because currently there may be no unregistered that are inaccessible
5288 // (this state combination is not supported). Note releasing the caller and
5289 // leaving the lock before calling uninit()
5290 alock.release();
5291 autoCaller.release();
5292
5293 uninit();
5294
5295 mParent->i_unregisterMachine(this, id);
5296 // calls VirtualBox::i_saveSettings()
5297
5298 return S_OK;
5299 }
5300
5301 HRESULT rc = S_OK;
5302
5303 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5304 // discard saved state
5305 if (mData->mMachineState == MachineState_Saved)
5306 {
5307 // add the saved state file to the list of files the caller should delete
5308 Assert(!mSSData->strStateFilePath.isEmpty());
5309 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5310
5311 mSSData->strStateFilePath.setNull();
5312
5313 // unconditionally set the machine state to powered off, we now
5314 // know no session has locked the machine
5315 mData->mMachineState = MachineState_PoweredOff;
5316 }
5317
5318 size_t cSnapshots = 0;
5319 if (mData->mFirstSnapshot)
5320 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5321 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5322 // fail now before we start detaching media
5323 return setError(VBOX_E_INVALID_OBJECT_STATE,
5324 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5325 mUserData->s.strName.c_str(), cSnapshots);
5326
5327 // This list collects the medium objects from all medium attachments
5328 // which we will detach from the machine and its snapshots, in a specific
5329 // order which allows for closing all media without getting "media in use"
5330 // errors, simply by going through the list from the front to the back:
5331 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5332 // and must be closed before the parent media from the snapshots, or closing the parents
5333 // will fail because they still have children);
5334 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5335 // the root ("first") snapshot of the machine.
5336 MediaList llMedia;
5337
5338 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5339 && mMediumAttachments->size()
5340 )
5341 {
5342 // we have media attachments: detach them all and add the Medium objects to our list
5343 if (aCleanupMode != CleanupMode_UnregisterOnly)
5344 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5345 else
5346 return setError(VBOX_E_INVALID_OBJECT_STATE,
5347 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5348 mUserData->s.strName.c_str(), mMediumAttachments->size());
5349 }
5350
5351 if (cSnapshots)
5352 {
5353 // add the media from the medium attachments of the snapshots to llMedia
5354 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5355 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5356 // into the children first
5357
5358 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5359 MachineState_T oldState = mData->mMachineState;
5360 mData->mMachineState = MachineState_DeletingSnapshot;
5361
5362 // make a copy of the first snapshot so the refcount does not drop to 0
5363 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5364 // because of the AutoCaller voodoo)
5365 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5366
5367 // GO!
5368 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5369
5370 mData->mMachineState = oldState;
5371 }
5372
5373 if (FAILED(rc))
5374 {
5375 i_rollbackMedia();
5376 return rc;
5377 }
5378
5379 // commit all the media changes made above
5380 i_commitMedia();
5381
5382 mData->mRegistered = false;
5383
5384 // machine lock no longer needed
5385 alock.release();
5386
5387 // return media to caller
5388 aMedia.resize(llMedia.size());
5389 size_t i = 0;
5390 for (MediaList::const_iterator
5391 it = llMedia.begin();
5392 it != llMedia.end();
5393 ++it, ++i)
5394 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5395
5396 mParent->i_unregisterMachine(this, id);
5397 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5398
5399 return S_OK;
5400}
5401
5402/**
5403 * Task record for deleting a machine config.
5404 */
5405class Machine::DeleteConfigTask
5406 : public Machine::Task
5407{
5408public:
5409 DeleteConfigTask(Machine *m,
5410 Progress *p,
5411 const Utf8Str &t,
5412 const RTCList<ComPtr<IMedium> > &llMediums,
5413 const StringsList &llFilesToDelete)
5414 : Task(m, p, t),
5415 m_llMediums(llMediums),
5416 m_llFilesToDelete(llFilesToDelete)
5417 {}
5418
5419private:
5420 void handler()
5421 {
5422 try
5423 {
5424 m_pMachine->i_deleteConfigHandler(*this);
5425 }
5426 catch (...)
5427 {
5428 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5429 }
5430 }
5431
5432 RTCList<ComPtr<IMedium> > m_llMediums;
5433 StringsList m_llFilesToDelete;
5434
5435 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5436};
5437
5438/**
5439 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5440 * SessionMachine::taskHandler().
5441 *
5442 * @note Locks this object for writing.
5443 *
5444 * @param task
5445 * @return
5446 */
5447void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5448{
5449 LogFlowThisFuncEnter();
5450
5451 AutoCaller autoCaller(this);
5452 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5453 if (FAILED(autoCaller.rc()))
5454 {
5455 /* we might have been uninitialized because the session was accidentally
5456 * closed by the client, so don't assert */
5457 HRESULT rc = setError(E_FAIL,
5458 tr("The session has been accidentally closed"));
5459 task.m_pProgress->i_notifyComplete(rc);
5460 LogFlowThisFuncLeave();
5461 return;
5462 }
5463
5464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5465
5466 HRESULT rc = S_OK;
5467
5468 try
5469 {
5470 ULONG uLogHistoryCount = 3;
5471 ComPtr<ISystemProperties> systemProperties;
5472 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5473 if (FAILED(rc)) throw rc;
5474
5475 if (!systemProperties.isNull())
5476 {
5477 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5478 if (FAILED(rc)) throw rc;
5479 }
5480
5481 MachineState_T oldState = mData->mMachineState;
5482 i_setMachineState(MachineState_SettingUp);
5483 alock.release();
5484 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5485 {
5486 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5487 {
5488 AutoCaller mac(pMedium);
5489 if (FAILED(mac.rc())) throw mac.rc();
5490 Utf8Str strLocation = pMedium->i_getLocationFull();
5491 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5492 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5493 if (FAILED(rc)) throw rc;
5494 }
5495 if (pMedium->i_isMediumFormatFile())
5496 {
5497 ComPtr<IProgress> pProgress2;
5498 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5499 if (FAILED(rc)) throw rc;
5500 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5501 if (FAILED(rc)) throw rc;
5502 }
5503
5504 /* Close the medium, deliberately without checking the return
5505 * code, and without leaving any trace in the error info, as
5506 * a failure here is a very minor issue, which shouldn't happen
5507 * as above we even managed to delete the medium. */
5508 {
5509 ErrorInfoKeeper eik;
5510 pMedium->Close();
5511 }
5512 }
5513 i_setMachineState(oldState);
5514 alock.acquire();
5515
5516 // delete the files pushed on the task list by Machine::Delete()
5517 // (this includes saved states of the machine and snapshots and
5518 // medium storage files from the IMedium list passed in, and the
5519 // machine XML file)
5520 for (StringsList::const_iterator
5521 it = task.m_llFilesToDelete.begin();
5522 it != task.m_llFilesToDelete.end();
5523 ++it)
5524 {
5525 const Utf8Str &strFile = *it;
5526 LogFunc(("Deleting file %s\n", strFile.c_str()));
5527 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5528 if (FAILED(rc)) throw rc;
5529
5530 int vrc = RTFileDelete(strFile.c_str());
5531 if (RT_FAILURE(vrc))
5532 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5533 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5534 }
5535
5536 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5537 if (FAILED(rc)) throw rc;
5538
5539 /* delete the settings only when the file actually exists */
5540 if (mData->pMachineConfigFile->fileExists())
5541 {
5542 /* Delete any backup or uncommitted XML files. Ignore failures.
5543 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5544 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5545 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5546 RTFileDelete(otherXml.c_str());
5547 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5548 RTFileDelete(otherXml.c_str());
5549
5550 /* delete the Logs folder, nothing important should be left
5551 * there (we don't check for errors because the user might have
5552 * some private files there that we don't want to delete) */
5553 Utf8Str logFolder;
5554 getLogFolder(logFolder);
5555 Assert(logFolder.length());
5556 if (RTDirExists(logFolder.c_str()))
5557 {
5558 /* Delete all VBox.log[.N] files from the Logs folder
5559 * (this must be in sync with the rotation logic in
5560 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5561 * files that may have been created by the GUI. */
5562 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5563 logFolder.c_str(), RTPATH_DELIMITER);
5564 RTFileDelete(log.c_str());
5565 log = Utf8StrFmt("%s%cVBox.png",
5566 logFolder.c_str(), RTPATH_DELIMITER);
5567 RTFileDelete(log.c_str());
5568 for (int i = uLogHistoryCount; i > 0; i--)
5569 {
5570 log = Utf8StrFmt("%s%cVBox.log.%d",
5571 logFolder.c_str(), RTPATH_DELIMITER, i);
5572 RTFileDelete(log.c_str());
5573 log = Utf8StrFmt("%s%cVBox.png.%d",
5574 logFolder.c_str(), RTPATH_DELIMITER, i);
5575 RTFileDelete(log.c_str());
5576 }
5577#if defined(RT_OS_WINDOWS)
5578 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5579 RTFileDelete(log.c_str());
5580 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5581 RTFileDelete(log.c_str());
5582#endif
5583
5584 RTDirRemove(logFolder.c_str());
5585 }
5586
5587 /* delete the Snapshots folder, nothing important should be left
5588 * there (we don't check for errors because the user might have
5589 * some private files there that we don't want to delete) */
5590 Utf8Str strFullSnapshotFolder;
5591 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5592 Assert(!strFullSnapshotFolder.isEmpty());
5593 if (RTDirExists(strFullSnapshotFolder.c_str()))
5594 RTDirRemove(strFullSnapshotFolder.c_str());
5595
5596 // delete the directory that contains the settings file, but only
5597 // if it matches the VM name
5598 Utf8Str settingsDir;
5599 if (i_isInOwnDir(&settingsDir))
5600 RTDirRemove(settingsDir.c_str());
5601 }
5602
5603 alock.release();
5604
5605 mParent->i_saveModifiedRegistries();
5606 }
5607 catch (HRESULT aRC) { rc = aRC; }
5608
5609 task.m_pProgress->i_notifyComplete(rc);
5610
5611 LogFlowThisFuncLeave();
5612}
5613
5614HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5615{
5616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5617
5618 HRESULT rc = i_checkStateDependency(MutableStateDep);
5619 if (FAILED(rc)) return rc;
5620
5621 if (mData->mRegistered)
5622 return setError(VBOX_E_INVALID_VM_STATE,
5623 tr("Cannot delete settings of a registered machine"));
5624
5625 // collect files to delete
5626 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5627 if (mData->pMachineConfigFile->fileExists())
5628 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5629
5630 RTCList<ComPtr<IMedium> > llMediums;
5631 for (size_t i = 0; i < aMedia.size(); ++i)
5632 {
5633 IMedium *pIMedium(aMedia[i]);
5634 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5635 if (pMedium.isNull())
5636 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5637 SafeArray<BSTR> ids;
5638 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5639 if (FAILED(rc)) return rc;
5640 /* At this point the medium should not have any back references
5641 * anymore. If it has it is attached to another VM and *must* not
5642 * deleted. */
5643 if (ids.size() < 1)
5644 llMediums.append(pMedium);
5645 }
5646
5647 ComObjPtr<Progress> pProgress;
5648 pProgress.createObject();
5649 rc = pProgress->init(i_getVirtualBox(),
5650 static_cast<IMachine*>(this) /* aInitiator */,
5651 tr("Deleting files"),
5652 true /* fCancellable */,
5653 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5654 tr("Collecting file inventory"));
5655 if (FAILED(rc))
5656 return rc;
5657
5658 /* create and start the task on a separate thread (note that it will not
5659 * start working until we release alock) */
5660 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5661 rc = pTask->createThread();
5662 if (FAILED(rc))
5663 return rc;
5664
5665 pProgress.queryInterfaceTo(aProgress.asOutParam());
5666
5667 LogFlowFuncLeave();
5668
5669 return S_OK;
5670}
5671
5672HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5673{
5674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5675
5676 ComObjPtr<Snapshot> pSnapshot;
5677 HRESULT rc;
5678
5679 if (aNameOrId.isEmpty())
5680 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5681 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5682 else
5683 {
5684 Guid uuid(aNameOrId);
5685 if (uuid.isValid())
5686 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5687 else
5688 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5689 }
5690 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5691
5692 return rc;
5693}
5694
5695HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5696{
5697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5698
5699 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5700 if (FAILED(rc)) return rc;
5701
5702 ComObjPtr<SharedFolder> sharedFolder;
5703 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5704 if (SUCCEEDED(rc))
5705 return setError(VBOX_E_OBJECT_IN_USE,
5706 tr("Shared folder named '%s' already exists"),
5707 aName.c_str());
5708
5709 sharedFolder.createObject();
5710 rc = sharedFolder->init(i_getMachine(),
5711 aName,
5712 aHostPath,
5713 !!aWritable,
5714 !!aAutomount,
5715 true /* fFailOnError */);
5716 if (FAILED(rc)) return rc;
5717
5718 i_setModified(IsModified_SharedFolders);
5719 mHWData.backup();
5720 mHWData->mSharedFolders.push_back(sharedFolder);
5721
5722 /* inform the direct session if any */
5723 alock.release();
5724 i_onSharedFolderChange();
5725
5726 return S_OK;
5727}
5728
5729HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5730{
5731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5732
5733 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5734 if (FAILED(rc)) return rc;
5735
5736 ComObjPtr<SharedFolder> sharedFolder;
5737 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5738 if (FAILED(rc)) return rc;
5739
5740 i_setModified(IsModified_SharedFolders);
5741 mHWData.backup();
5742 mHWData->mSharedFolders.remove(sharedFolder);
5743
5744 /* inform the direct session if any */
5745 alock.release();
5746 i_onSharedFolderChange();
5747
5748 return S_OK;
5749}
5750
5751HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5752{
5753 /* start with No */
5754 *aCanShow = FALSE;
5755
5756 ComPtr<IInternalSessionControl> directControl;
5757 {
5758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5759
5760 if (mData->mSession.mState != SessionState_Locked)
5761 return setError(VBOX_E_INVALID_VM_STATE,
5762 tr("Machine is not locked for session (session state: %s)"),
5763 Global::stringifySessionState(mData->mSession.mState));
5764
5765 if (mData->mSession.mLockType == LockType_VM)
5766 directControl = mData->mSession.mDirectControl;
5767 }
5768
5769 /* ignore calls made after #OnSessionEnd() is called */
5770 if (!directControl)
5771 return S_OK;
5772
5773 LONG64 dummy;
5774 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5775}
5776
5777HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5778{
5779 ComPtr<IInternalSessionControl> directControl;
5780 {
5781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5782
5783 if (mData->mSession.mState != SessionState_Locked)
5784 return setError(E_FAIL,
5785 tr("Machine is not locked for session (session state: %s)"),
5786 Global::stringifySessionState(mData->mSession.mState));
5787
5788 if (mData->mSession.mLockType == LockType_VM)
5789 directControl = mData->mSession.mDirectControl;
5790 }
5791
5792 /* ignore calls made after #OnSessionEnd() is called */
5793 if (!directControl)
5794 return S_OK;
5795
5796 BOOL dummy;
5797 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5798}
5799
5800#ifdef VBOX_WITH_GUEST_PROPS
5801/**
5802 * Look up a guest property in VBoxSVC's internal structures.
5803 */
5804HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5805 com::Utf8Str &aValue,
5806 LONG64 *aTimestamp,
5807 com::Utf8Str &aFlags) const
5808{
5809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5810
5811 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5812 if (it != mHWData->mGuestProperties.end())
5813 {
5814 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5815 aValue = it->second.strValue;
5816 *aTimestamp = it->second.mTimestamp;
5817 GuestPropWriteFlags(it->second.mFlags, szFlags);
5818 aFlags = Utf8Str(szFlags);
5819 }
5820
5821 return S_OK;
5822}
5823
5824/**
5825 * Query the VM that a guest property belongs to for the property.
5826 * @returns E_ACCESSDENIED if the VM process is not available or not
5827 * currently handling queries and the lookup should then be done in
5828 * VBoxSVC.
5829 */
5830HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5831 com::Utf8Str &aValue,
5832 LONG64 *aTimestamp,
5833 com::Utf8Str &aFlags) const
5834{
5835 HRESULT rc = S_OK;
5836 BSTR bValue = NULL;
5837 BSTR bFlags = NULL;
5838
5839 ComPtr<IInternalSessionControl> directControl;
5840 {
5841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5842 if (mData->mSession.mLockType == LockType_VM)
5843 directControl = mData->mSession.mDirectControl;
5844 }
5845
5846 /* ignore calls made after #OnSessionEnd() is called */
5847 if (!directControl)
5848 rc = E_ACCESSDENIED;
5849 else
5850 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5851 0 /* accessMode */,
5852 &bValue, aTimestamp, &bFlags);
5853
5854 aValue = bValue;
5855 aFlags = bFlags;
5856
5857 return rc;
5858}
5859#endif // VBOX_WITH_GUEST_PROPS
5860
5861HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5862 com::Utf8Str &aValue,
5863 LONG64 *aTimestamp,
5864 com::Utf8Str &aFlags)
5865{
5866#ifndef VBOX_WITH_GUEST_PROPS
5867 ReturnComNotImplemented();
5868#else // VBOX_WITH_GUEST_PROPS
5869
5870 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5871
5872 if (rc == E_ACCESSDENIED)
5873 /* The VM is not running or the service is not (yet) accessible */
5874 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5875 return rc;
5876#endif // VBOX_WITH_GUEST_PROPS
5877}
5878
5879HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5880{
5881 LONG64 dummyTimestamp;
5882 com::Utf8Str dummyFlags;
5883 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5884 return rc;
5885
5886}
5887HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5888{
5889 com::Utf8Str dummyFlags;
5890 com::Utf8Str dummyValue;
5891 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5892 return rc;
5893}
5894
5895#ifdef VBOX_WITH_GUEST_PROPS
5896/**
5897 * Set a guest property in VBoxSVC's internal structures.
5898 */
5899HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5900 const com::Utf8Str &aFlags, bool fDelete)
5901{
5902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5903 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5904 if (FAILED(rc)) return rc;
5905
5906 try
5907 {
5908 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5909 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5910 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5911
5912 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5913 if (it == mHWData->mGuestProperties.end())
5914 {
5915 if (!fDelete)
5916 {
5917 i_setModified(IsModified_MachineData);
5918 mHWData.backupEx();
5919
5920 RTTIMESPEC time;
5921 HWData::GuestProperty prop;
5922 prop.strValue = Bstr(aValue).raw();
5923 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5924 prop.mFlags = fFlags;
5925 mHWData->mGuestProperties[aName] = prop;
5926 }
5927 }
5928 else
5929 {
5930 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5931 {
5932 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5933 }
5934 else
5935 {
5936 i_setModified(IsModified_MachineData);
5937 mHWData.backupEx();
5938
5939 /* The backupEx() operation invalidates our iterator,
5940 * so get a new one. */
5941 it = mHWData->mGuestProperties.find(aName);
5942 Assert(it != mHWData->mGuestProperties.end());
5943
5944 if (!fDelete)
5945 {
5946 RTTIMESPEC time;
5947 it->second.strValue = aValue;
5948 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5949 it->second.mFlags = fFlags;
5950 }
5951 else
5952 mHWData->mGuestProperties.erase(it);
5953 }
5954 }
5955
5956 if (SUCCEEDED(rc))
5957 {
5958 alock.release();
5959
5960 mParent->i_onGuestPropertyChange(mData->mUuid,
5961 Bstr(aName).raw(),
5962 Bstr(aValue).raw(),
5963 Bstr(aFlags).raw());
5964 }
5965 }
5966 catch (std::bad_alloc &)
5967 {
5968 rc = E_OUTOFMEMORY;
5969 }
5970
5971 return rc;
5972}
5973
5974/**
5975 * Set a property on the VM that that property belongs to.
5976 * @returns E_ACCESSDENIED if the VM process is not available or not
5977 * currently handling queries and the setting should then be done in
5978 * VBoxSVC.
5979 */
5980HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5981 const com::Utf8Str &aFlags, bool fDelete)
5982{
5983 HRESULT rc;
5984
5985 try
5986 {
5987 ComPtr<IInternalSessionControl> directControl;
5988 {
5989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5990 if (mData->mSession.mLockType == LockType_VM)
5991 directControl = mData->mSession.mDirectControl;
5992 }
5993
5994 BSTR dummy = NULL; /* will not be changed (setter) */
5995 LONG64 dummy64;
5996 if (!directControl)
5997 rc = E_ACCESSDENIED;
5998 else
5999 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6000 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
6001 fDelete? 2: 1 /* accessMode */,
6002 &dummy, &dummy64, &dummy);
6003 }
6004 catch (std::bad_alloc &)
6005 {
6006 rc = E_OUTOFMEMORY;
6007 }
6008
6009 return rc;
6010}
6011#endif // VBOX_WITH_GUEST_PROPS
6012
6013HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6014 const com::Utf8Str &aFlags)
6015{
6016#ifndef VBOX_WITH_GUEST_PROPS
6017 ReturnComNotImplemented();
6018#else // VBOX_WITH_GUEST_PROPS
6019 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6020 if (rc == E_ACCESSDENIED)
6021 /* The VM is not running or the service is not (yet) accessible */
6022 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6023 return rc;
6024#endif // VBOX_WITH_GUEST_PROPS
6025}
6026
6027HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6028{
6029 return setGuestProperty(aProperty, aValue, "");
6030}
6031
6032HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6033{
6034#ifndef VBOX_WITH_GUEST_PROPS
6035 ReturnComNotImplemented();
6036#else // VBOX_WITH_GUEST_PROPS
6037 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6038 if (rc == E_ACCESSDENIED)
6039 /* The VM is not running or the service is not (yet) accessible */
6040 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6041 return rc;
6042#endif // VBOX_WITH_GUEST_PROPS
6043}
6044
6045#ifdef VBOX_WITH_GUEST_PROPS
6046/**
6047 * Enumerate the guest properties in VBoxSVC's internal structures.
6048 */
6049HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6050 std::vector<com::Utf8Str> &aNames,
6051 std::vector<com::Utf8Str> &aValues,
6052 std::vector<LONG64> &aTimestamps,
6053 std::vector<com::Utf8Str> &aFlags)
6054{
6055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6056 Utf8Str strPatterns(aPatterns);
6057
6058 /*
6059 * Look for matching patterns and build up a list.
6060 */
6061 HWData::GuestPropertyMap propMap;
6062 for (HWData::GuestPropertyMap::const_iterator
6063 it = mHWData->mGuestProperties.begin();
6064 it != mHWData->mGuestProperties.end();
6065 ++it)
6066 {
6067 if ( strPatterns.isEmpty()
6068 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6069 RTSTR_MAX,
6070 it->first.c_str(),
6071 RTSTR_MAX,
6072 NULL)
6073 )
6074 propMap.insert(*it);
6075 }
6076
6077 alock.release();
6078
6079 /*
6080 * And build up the arrays for returning the property information.
6081 */
6082 size_t cEntries = propMap.size();
6083
6084 aNames.resize(cEntries);
6085 aValues.resize(cEntries);
6086 aTimestamps.resize(cEntries);
6087 aFlags.resize(cEntries);
6088
6089 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6090 size_t i = 0;
6091 for (HWData::GuestPropertyMap::const_iterator
6092 it = propMap.begin();
6093 it != propMap.end();
6094 ++it, ++i)
6095 {
6096 aNames[i] = it->first;
6097 aValues[i] = it->second.strValue;
6098 aTimestamps[i] = it->second.mTimestamp;
6099 GuestPropWriteFlags(it->second.mFlags, szFlags);
6100 aFlags[i] = Utf8Str(szFlags);
6101 }
6102
6103 return S_OK;
6104}
6105
6106/**
6107 * Enumerate the properties managed by a VM.
6108 * @returns E_ACCESSDENIED if the VM process is not available or not
6109 * currently handling queries and the setting should then be done in
6110 * VBoxSVC.
6111 */
6112HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6113 std::vector<com::Utf8Str> &aNames,
6114 std::vector<com::Utf8Str> &aValues,
6115 std::vector<LONG64> &aTimestamps,
6116 std::vector<com::Utf8Str> &aFlags)
6117{
6118 HRESULT rc;
6119 ComPtr<IInternalSessionControl> directControl;
6120 {
6121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6122 if (mData->mSession.mLockType == LockType_VM)
6123 directControl = mData->mSession.mDirectControl;
6124 }
6125
6126 com::SafeArray<BSTR> bNames;
6127 com::SafeArray<BSTR> bValues;
6128 com::SafeArray<LONG64> bTimestamps;
6129 com::SafeArray<BSTR> bFlags;
6130
6131 if (!directControl)
6132 rc = E_ACCESSDENIED;
6133 else
6134 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6135 ComSafeArrayAsOutParam(bNames),
6136 ComSafeArrayAsOutParam(bValues),
6137 ComSafeArrayAsOutParam(bTimestamps),
6138 ComSafeArrayAsOutParam(bFlags));
6139 size_t i;
6140 aNames.resize(bNames.size());
6141 for (i = 0; i < bNames.size(); ++i)
6142 aNames[i] = Utf8Str(bNames[i]);
6143 aValues.resize(bValues.size());
6144 for (i = 0; i < bValues.size(); ++i)
6145 aValues[i] = Utf8Str(bValues[i]);
6146 aTimestamps.resize(bTimestamps.size());
6147 for (i = 0; i < bTimestamps.size(); ++i)
6148 aTimestamps[i] = bTimestamps[i];
6149 aFlags.resize(bFlags.size());
6150 for (i = 0; i < bFlags.size(); ++i)
6151 aFlags[i] = Utf8Str(bFlags[i]);
6152
6153 return rc;
6154}
6155#endif // VBOX_WITH_GUEST_PROPS
6156HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6157 std::vector<com::Utf8Str> &aNames,
6158 std::vector<com::Utf8Str> &aValues,
6159 std::vector<LONG64> &aTimestamps,
6160 std::vector<com::Utf8Str> &aFlags)
6161{
6162#ifndef VBOX_WITH_GUEST_PROPS
6163 ReturnComNotImplemented();
6164#else // VBOX_WITH_GUEST_PROPS
6165
6166 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6167
6168 if (rc == E_ACCESSDENIED)
6169 /* The VM is not running or the service is not (yet) accessible */
6170 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6171 return rc;
6172#endif // VBOX_WITH_GUEST_PROPS
6173}
6174
6175HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6176 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6177{
6178 MediumAttachmentList atts;
6179
6180 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6181 if (FAILED(rc)) return rc;
6182
6183 aMediumAttachments.resize(atts.size());
6184 size_t i = 0;
6185 for (MediumAttachmentList::const_iterator
6186 it = atts.begin();
6187 it != atts.end();
6188 ++it, ++i)
6189 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6190
6191 return S_OK;
6192}
6193
6194HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6195 LONG aControllerPort,
6196 LONG aDevice,
6197 ComPtr<IMediumAttachment> &aAttachment)
6198{
6199 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6200 aName.c_str(), aControllerPort, aDevice));
6201
6202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6203
6204 aAttachment = NULL;
6205
6206 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6207 aName,
6208 aControllerPort,
6209 aDevice);
6210 if (pAttach.isNull())
6211 return setError(VBOX_E_OBJECT_NOT_FOUND,
6212 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6213 aDevice, aControllerPort, aName.c_str());
6214
6215 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6216
6217 return S_OK;
6218}
6219
6220
6221HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6222 StorageBus_T aConnectionType,
6223 ComPtr<IStorageController> &aController)
6224{
6225 if ( (aConnectionType <= StorageBus_Null)
6226 || (aConnectionType > StorageBus_PCIe))
6227 return setError(E_INVALIDARG,
6228 tr("Invalid connection type: %d"),
6229 aConnectionType);
6230
6231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6232
6233 HRESULT rc = i_checkStateDependency(MutableStateDep);
6234 if (FAILED(rc)) return rc;
6235
6236 /* try to find one with the name first. */
6237 ComObjPtr<StorageController> ctrl;
6238
6239 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6240 if (SUCCEEDED(rc))
6241 return setError(VBOX_E_OBJECT_IN_USE,
6242 tr("Storage controller named '%s' already exists"),
6243 aName.c_str());
6244
6245 ctrl.createObject();
6246
6247 /* get a new instance number for the storage controller */
6248 ULONG ulInstance = 0;
6249 bool fBootable = true;
6250 for (StorageControllerList::const_iterator
6251 it = mStorageControllers->begin();
6252 it != mStorageControllers->end();
6253 ++it)
6254 {
6255 if ((*it)->i_getStorageBus() == aConnectionType)
6256 {
6257 ULONG ulCurInst = (*it)->i_getInstance();
6258
6259 if (ulCurInst >= ulInstance)
6260 ulInstance = ulCurInst + 1;
6261
6262 /* Only one controller of each type can be marked as bootable. */
6263 if ((*it)->i_getBootable())
6264 fBootable = false;
6265 }
6266 }
6267
6268 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6269 if (FAILED(rc)) return rc;
6270
6271 i_setModified(IsModified_Storage);
6272 mStorageControllers.backup();
6273 mStorageControllers->push_back(ctrl);
6274
6275 ctrl.queryInterfaceTo(aController.asOutParam());
6276
6277 /* inform the direct session if any */
6278 alock.release();
6279 i_onStorageControllerChange();
6280
6281 return S_OK;
6282}
6283
6284HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6285 ComPtr<IStorageController> &aStorageController)
6286{
6287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6288
6289 ComObjPtr<StorageController> ctrl;
6290
6291 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6292 if (SUCCEEDED(rc))
6293 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6294
6295 return rc;
6296}
6297
6298HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6299 ULONG aInstance,
6300 ComPtr<IStorageController> &aStorageController)
6301{
6302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6303
6304 for (StorageControllerList::const_iterator
6305 it = mStorageControllers->begin();
6306 it != mStorageControllers->end();
6307 ++it)
6308 {
6309 if ( (*it)->i_getStorageBus() == aConnectionType
6310 && (*it)->i_getInstance() == aInstance)
6311 {
6312 (*it).queryInterfaceTo(aStorageController.asOutParam());
6313 return S_OK;
6314 }
6315 }
6316
6317 return setError(VBOX_E_OBJECT_NOT_FOUND,
6318 tr("Could not find a storage controller with instance number '%lu'"),
6319 aInstance);
6320}
6321
6322HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6323{
6324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6325
6326 HRESULT rc = i_checkStateDependency(MutableStateDep);
6327 if (FAILED(rc)) return rc;
6328
6329 ComObjPtr<StorageController> ctrl;
6330
6331 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6332 if (SUCCEEDED(rc))
6333 {
6334 /* Ensure that only one controller of each type is marked as bootable. */
6335 if (aBootable == TRUE)
6336 {
6337 for (StorageControllerList::const_iterator
6338 it = mStorageControllers->begin();
6339 it != mStorageControllers->end();
6340 ++it)
6341 {
6342 ComObjPtr<StorageController> aCtrl = (*it);
6343
6344 if ( (aCtrl->i_getName() != aName)
6345 && aCtrl->i_getBootable() == TRUE
6346 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6347 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6348 {
6349 aCtrl->i_setBootable(FALSE);
6350 break;
6351 }
6352 }
6353 }
6354
6355 if (SUCCEEDED(rc))
6356 {
6357 ctrl->i_setBootable(aBootable);
6358 i_setModified(IsModified_Storage);
6359 }
6360 }
6361
6362 if (SUCCEEDED(rc))
6363 {
6364 /* inform the direct session if any */
6365 alock.release();
6366 i_onStorageControllerChange();
6367 }
6368
6369 return rc;
6370}
6371
6372HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6373{
6374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6375
6376 HRESULT rc = i_checkStateDependency(MutableStateDep);
6377 if (FAILED(rc)) return rc;
6378
6379 ComObjPtr<StorageController> ctrl;
6380 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6381 if (FAILED(rc)) return rc;
6382
6383 {
6384 /* find all attached devices to the appropriate storage controller and detach them all */
6385 // make a temporary list because detachDevice invalidates iterators into
6386 // mMediumAttachments
6387 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6388
6389 for (MediumAttachmentList::const_iterator
6390 it = llAttachments2.begin();
6391 it != llAttachments2.end();
6392 ++it)
6393 {
6394 MediumAttachment *pAttachTemp = *it;
6395
6396 AutoCaller localAutoCaller(pAttachTemp);
6397 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6398
6399 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6400
6401 if (pAttachTemp->i_getControllerName() == aName)
6402 {
6403 rc = i_detachDevice(pAttachTemp, alock, NULL);
6404 if (FAILED(rc)) return rc;
6405 }
6406 }
6407 }
6408
6409 /* We can remove it now. */
6410 i_setModified(IsModified_Storage);
6411 mStorageControllers.backup();
6412
6413 ctrl->i_unshare();
6414
6415 mStorageControllers->remove(ctrl);
6416
6417 /* inform the direct session if any */
6418 alock.release();
6419 i_onStorageControllerChange();
6420
6421 return S_OK;
6422}
6423
6424HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6425 ComPtr<IUSBController> &aController)
6426{
6427 if ( (aType <= USBControllerType_Null)
6428 || (aType >= USBControllerType_Last))
6429 return setError(E_INVALIDARG,
6430 tr("Invalid USB controller type: %d"),
6431 aType);
6432
6433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6434
6435 HRESULT rc = i_checkStateDependency(MutableStateDep);
6436 if (FAILED(rc)) return rc;
6437
6438 /* try to find one with the same type first. */
6439 ComObjPtr<USBController> ctrl;
6440
6441 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6442 if (SUCCEEDED(rc))
6443 return setError(VBOX_E_OBJECT_IN_USE,
6444 tr("USB controller named '%s' already exists"),
6445 aName.c_str());
6446
6447 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6448 ULONG maxInstances;
6449 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6450 if (FAILED(rc))
6451 return rc;
6452
6453 ULONG cInstances = i_getUSBControllerCountByType(aType);
6454 if (cInstances >= maxInstances)
6455 return setError(E_INVALIDARG,
6456 tr("Too many USB controllers of this type"));
6457
6458 ctrl.createObject();
6459
6460 rc = ctrl->init(this, aName, aType);
6461 if (FAILED(rc)) return rc;
6462
6463 i_setModified(IsModified_USB);
6464 mUSBControllers.backup();
6465 mUSBControllers->push_back(ctrl);
6466
6467 ctrl.queryInterfaceTo(aController.asOutParam());
6468
6469 /* inform the direct session if any */
6470 alock.release();
6471 i_onUSBControllerChange();
6472
6473 return S_OK;
6474}
6475
6476HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6477{
6478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6479
6480 ComObjPtr<USBController> ctrl;
6481
6482 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6483 if (SUCCEEDED(rc))
6484 ctrl.queryInterfaceTo(aController.asOutParam());
6485
6486 return rc;
6487}
6488
6489HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6490 ULONG *aControllers)
6491{
6492 if ( (aType <= USBControllerType_Null)
6493 || (aType >= USBControllerType_Last))
6494 return setError(E_INVALIDARG,
6495 tr("Invalid USB controller type: %d"),
6496 aType);
6497
6498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6499
6500 ComObjPtr<USBController> ctrl;
6501
6502 *aControllers = i_getUSBControllerCountByType(aType);
6503
6504 return S_OK;
6505}
6506
6507HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6508{
6509
6510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 HRESULT rc = i_checkStateDependency(MutableStateDep);
6513 if (FAILED(rc)) return rc;
6514
6515 ComObjPtr<USBController> ctrl;
6516 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6517 if (FAILED(rc)) return rc;
6518
6519 i_setModified(IsModified_USB);
6520 mUSBControllers.backup();
6521
6522 ctrl->i_unshare();
6523
6524 mUSBControllers->remove(ctrl);
6525
6526 /* inform the direct session if any */
6527 alock.release();
6528 i_onUSBControllerChange();
6529
6530 return S_OK;
6531}
6532
6533HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6534 ULONG *aOriginX,
6535 ULONG *aOriginY,
6536 ULONG *aWidth,
6537 ULONG *aHeight,
6538 BOOL *aEnabled)
6539{
6540 uint32_t u32OriginX= 0;
6541 uint32_t u32OriginY= 0;
6542 uint32_t u32Width = 0;
6543 uint32_t u32Height = 0;
6544 uint16_t u16Flags = 0;
6545
6546 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6547 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6548 if (RT_FAILURE(vrc))
6549 {
6550#ifdef RT_OS_WINDOWS
6551 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6552 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6553 * So just assign fEnable to TRUE again.
6554 * The right fix would be to change GUI API wrappers to make sure that parameters
6555 * are changed only if API succeeds.
6556 */
6557 *aEnabled = TRUE;
6558#endif
6559 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6560 tr("Saved guest size is not available (%Rrc)"),
6561 vrc);
6562 }
6563
6564 *aOriginX = u32OriginX;
6565 *aOriginY = u32OriginY;
6566 *aWidth = u32Width;
6567 *aHeight = u32Height;
6568 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6569
6570 return S_OK;
6571}
6572
6573HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6574 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6575{
6576 if (aScreenId != 0)
6577 return E_NOTIMPL;
6578
6579 if ( aBitmapFormat != BitmapFormat_BGR0
6580 && aBitmapFormat != BitmapFormat_BGRA
6581 && aBitmapFormat != BitmapFormat_RGBA
6582 && aBitmapFormat != BitmapFormat_PNG)
6583 return setError(E_NOTIMPL,
6584 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6585
6586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6587
6588 uint8_t *pu8Data = NULL;
6589 uint32_t cbData = 0;
6590 uint32_t u32Width = 0;
6591 uint32_t u32Height = 0;
6592
6593 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6594
6595 if (RT_FAILURE(vrc))
6596 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6597 tr("Saved thumbnail data is not available (%Rrc)"),
6598 vrc);
6599
6600 HRESULT hr = S_OK;
6601
6602 *aWidth = u32Width;
6603 *aHeight = u32Height;
6604
6605 if (cbData > 0)
6606 {
6607 /* Convert pixels to the format expected by the API caller. */
6608 if (aBitmapFormat == BitmapFormat_BGR0)
6609 {
6610 /* [0] B, [1] G, [2] R, [3] 0. */
6611 aData.resize(cbData);
6612 memcpy(&aData.front(), pu8Data, cbData);
6613 }
6614 else if (aBitmapFormat == BitmapFormat_BGRA)
6615 {
6616 /* [0] B, [1] G, [2] R, [3] A. */
6617 aData.resize(cbData);
6618 for (uint32_t i = 0; i < cbData; i += 4)
6619 {
6620 aData[i] = pu8Data[i];
6621 aData[i + 1] = pu8Data[i + 1];
6622 aData[i + 2] = pu8Data[i + 2];
6623 aData[i + 3] = 0xff;
6624 }
6625 }
6626 else if (aBitmapFormat == BitmapFormat_RGBA)
6627 {
6628 /* [0] R, [1] G, [2] B, [3] A. */
6629 aData.resize(cbData);
6630 for (uint32_t i = 0; i < cbData; i += 4)
6631 {
6632 aData[i] = pu8Data[i + 2];
6633 aData[i + 1] = pu8Data[i + 1];
6634 aData[i + 2] = pu8Data[i];
6635 aData[i + 3] = 0xff;
6636 }
6637 }
6638 else if (aBitmapFormat == BitmapFormat_PNG)
6639 {
6640 uint8_t *pu8PNG = NULL;
6641 uint32_t cbPNG = 0;
6642 uint32_t cxPNG = 0;
6643 uint32_t cyPNG = 0;
6644
6645 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6646
6647 if (RT_SUCCESS(vrc))
6648 {
6649 aData.resize(cbPNG);
6650 if (cbPNG)
6651 memcpy(&aData.front(), pu8PNG, cbPNG);
6652 }
6653 else
6654 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6655 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6656 vrc);
6657
6658 RTMemFree(pu8PNG);
6659 }
6660 }
6661
6662 freeSavedDisplayScreenshot(pu8Data);
6663
6664 return hr;
6665}
6666
6667HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6668 ULONG *aWidth,
6669 ULONG *aHeight,
6670 std::vector<BitmapFormat_T> &aBitmapFormats)
6671{
6672 if (aScreenId != 0)
6673 return E_NOTIMPL;
6674
6675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6676
6677 uint8_t *pu8Data = NULL;
6678 uint32_t cbData = 0;
6679 uint32_t u32Width = 0;
6680 uint32_t u32Height = 0;
6681
6682 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6683
6684 if (RT_FAILURE(vrc))
6685 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6686 tr("Saved screenshot data is not available (%Rrc)"),
6687 vrc);
6688
6689 *aWidth = u32Width;
6690 *aHeight = u32Height;
6691 aBitmapFormats.resize(1);
6692 aBitmapFormats[0] = BitmapFormat_PNG;
6693
6694 freeSavedDisplayScreenshot(pu8Data);
6695
6696 return S_OK;
6697}
6698
6699HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6700 BitmapFormat_T aBitmapFormat,
6701 ULONG *aWidth,
6702 ULONG *aHeight,
6703 std::vector<BYTE> &aData)
6704{
6705 if (aScreenId != 0)
6706 return E_NOTIMPL;
6707
6708 if (aBitmapFormat != BitmapFormat_PNG)
6709 return E_NOTIMPL;
6710
6711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6712
6713 uint8_t *pu8Data = NULL;
6714 uint32_t cbData = 0;
6715 uint32_t u32Width = 0;
6716 uint32_t u32Height = 0;
6717
6718 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6719
6720 if (RT_FAILURE(vrc))
6721 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6722 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6723 vrc);
6724
6725 *aWidth = u32Width;
6726 *aHeight = u32Height;
6727
6728 aData.resize(cbData);
6729 if (cbData)
6730 memcpy(&aData.front(), pu8Data, cbData);
6731
6732 freeSavedDisplayScreenshot(pu8Data);
6733
6734 return S_OK;
6735}
6736
6737HRESULT Machine::hotPlugCPU(ULONG aCpu)
6738{
6739 HRESULT rc = S_OK;
6740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6741
6742 if (!mHWData->mCPUHotPlugEnabled)
6743 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6744
6745 if (aCpu >= mHWData->mCPUCount)
6746 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6747
6748 if (mHWData->mCPUAttached[aCpu])
6749 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6750
6751 alock.release();
6752 rc = i_onCPUChange(aCpu, false);
6753 alock.acquire();
6754 if (FAILED(rc)) return rc;
6755
6756 i_setModified(IsModified_MachineData);
6757 mHWData.backup();
6758 mHWData->mCPUAttached[aCpu] = true;
6759
6760 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6761 if (Global::IsOnline(mData->mMachineState))
6762 i_saveSettings(NULL);
6763
6764 return S_OK;
6765}
6766
6767HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6768{
6769 HRESULT rc = S_OK;
6770
6771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6772
6773 if (!mHWData->mCPUHotPlugEnabled)
6774 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6775
6776 if (aCpu >= SchemaDefs::MaxCPUCount)
6777 return setError(E_INVALIDARG,
6778 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6779 SchemaDefs::MaxCPUCount);
6780
6781 if (!mHWData->mCPUAttached[aCpu])
6782 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6783
6784 /* CPU 0 can't be detached */
6785 if (aCpu == 0)
6786 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6787
6788 alock.release();
6789 rc = i_onCPUChange(aCpu, true);
6790 alock.acquire();
6791 if (FAILED(rc)) return rc;
6792
6793 i_setModified(IsModified_MachineData);
6794 mHWData.backup();
6795 mHWData->mCPUAttached[aCpu] = false;
6796
6797 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6798 if (Global::IsOnline(mData->mMachineState))
6799 i_saveSettings(NULL);
6800
6801 return S_OK;
6802}
6803
6804HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6805{
6806 *aAttached = false;
6807
6808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6809
6810 /* If hotplug is enabled the CPU is always enabled. */
6811 if (!mHWData->mCPUHotPlugEnabled)
6812 {
6813 if (aCpu < mHWData->mCPUCount)
6814 *aAttached = true;
6815 }
6816 else
6817 {
6818 if (aCpu < SchemaDefs::MaxCPUCount)
6819 *aAttached = mHWData->mCPUAttached[aCpu];
6820 }
6821
6822 return S_OK;
6823}
6824
6825HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6826{
6827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6828
6829 Utf8Str log = i_getLogFilename(aIdx);
6830 if (!RTFileExists(log.c_str()))
6831 log.setNull();
6832 aFilename = log;
6833
6834 return S_OK;
6835}
6836
6837HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6838{
6839 if (aSize < 0)
6840 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6841
6842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6843
6844 HRESULT rc = S_OK;
6845 Utf8Str log = i_getLogFilename(aIdx);
6846
6847 /* do not unnecessarily hold the lock while doing something which does
6848 * not need the lock and potentially takes a long time. */
6849 alock.release();
6850
6851 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6852 * keeps the SOAP reply size under 1M for the webservice (we're using
6853 * base64 encoded strings for binary data for years now, avoiding the
6854 * expansion of each byte array element to approx. 25 bytes of XML. */
6855 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6856 aData.resize(cbData);
6857
6858 RTFILE LogFile;
6859 int vrc = RTFileOpen(&LogFile, log.c_str(),
6860 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6861 if (RT_SUCCESS(vrc))
6862 {
6863 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6864 if (RT_SUCCESS(vrc))
6865 aData.resize(cbData);
6866 else
6867 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6868 tr("Could not read log file '%s' (%Rrc)"),
6869 log.c_str(), vrc);
6870 RTFileClose(LogFile);
6871 }
6872 else
6873 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6874 tr("Could not open log file '%s' (%Rrc)"),
6875 log.c_str(), vrc);
6876
6877 if (FAILED(rc))
6878 aData.resize(0);
6879
6880 return rc;
6881}
6882
6883
6884/**
6885 * Currently this method doesn't attach device to the running VM,
6886 * just makes sure it's plugged on next VM start.
6887 */
6888HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6889{
6890 // lock scope
6891 {
6892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6893
6894 HRESULT rc = i_checkStateDependency(MutableStateDep);
6895 if (FAILED(rc)) return rc;
6896
6897 ChipsetType_T aChipset = ChipsetType_PIIX3;
6898 COMGETTER(ChipsetType)(&aChipset);
6899
6900 if (aChipset != ChipsetType_ICH9)
6901 {
6902 return setError(E_INVALIDARG,
6903 tr("Host PCI attachment only supported with ICH9 chipset"));
6904 }
6905
6906 // check if device with this host PCI address already attached
6907 for (HWData::PCIDeviceAssignmentList::const_iterator
6908 it = mHWData->mPCIDeviceAssignments.begin();
6909 it != mHWData->mPCIDeviceAssignments.end();
6910 ++it)
6911 {
6912 LONG iHostAddress = -1;
6913 ComPtr<PCIDeviceAttachment> pAttach;
6914 pAttach = *it;
6915 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6916 if (iHostAddress == aHostAddress)
6917 return setError(E_INVALIDARG,
6918 tr("Device with host PCI address already attached to this VM"));
6919 }
6920
6921 ComObjPtr<PCIDeviceAttachment> pda;
6922 char name[32];
6923
6924 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6925 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6926 pda.createObject();
6927 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6928 i_setModified(IsModified_MachineData);
6929 mHWData.backup();
6930 mHWData->mPCIDeviceAssignments.push_back(pda);
6931 }
6932
6933 return S_OK;
6934}
6935
6936/**
6937 * Currently this method doesn't detach device from the running VM,
6938 * just makes sure it's not plugged on next VM start.
6939 */
6940HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6941{
6942 ComObjPtr<PCIDeviceAttachment> pAttach;
6943 bool fRemoved = false;
6944 HRESULT rc;
6945
6946 // lock scope
6947 {
6948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6949
6950 rc = i_checkStateDependency(MutableStateDep);
6951 if (FAILED(rc)) return rc;
6952
6953 for (HWData::PCIDeviceAssignmentList::const_iterator
6954 it = mHWData->mPCIDeviceAssignments.begin();
6955 it != mHWData->mPCIDeviceAssignments.end();
6956 ++it)
6957 {
6958 LONG iHostAddress = -1;
6959 pAttach = *it;
6960 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6961 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6962 {
6963 i_setModified(IsModified_MachineData);
6964 mHWData.backup();
6965 mHWData->mPCIDeviceAssignments.remove(pAttach);
6966 fRemoved = true;
6967 break;
6968 }
6969 }
6970 }
6971
6972
6973 /* Fire event outside of the lock */
6974 if (fRemoved)
6975 {
6976 Assert(!pAttach.isNull());
6977 ComPtr<IEventSource> es;
6978 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6979 Assert(SUCCEEDED(rc));
6980 Bstr mid;
6981 rc = this->COMGETTER(Id)(mid.asOutParam());
6982 Assert(SUCCEEDED(rc));
6983 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6984 }
6985
6986 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6987 tr("No host PCI device %08x attached"),
6988 aHostAddress
6989 );
6990}
6991
6992HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6993{
6994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6995
6996 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6997 size_t i = 0;
6998 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6999 it = mHWData->mPCIDeviceAssignments.begin();
7000 it != mHWData->mPCIDeviceAssignments.end();
7001 ++it, ++i)
7002 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
7003
7004 return S_OK;
7005}
7006
7007HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7008{
7009 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7010
7011 return S_OK;
7012}
7013
7014HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7015{
7016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7017
7018 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7019
7020 return S_OK;
7021}
7022
7023HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7024{
7025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7026 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7027 if (SUCCEEDED(hrc))
7028 {
7029 hrc = mHWData.backupEx();
7030 if (SUCCEEDED(hrc))
7031 {
7032 i_setModified(IsModified_MachineData);
7033 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7034 }
7035 }
7036 return hrc;
7037}
7038
7039HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7040{
7041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7042 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7043 return S_OK;
7044}
7045
7046HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7047{
7048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7049 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7050 if (SUCCEEDED(hrc))
7051 {
7052 hrc = mHWData.backupEx();
7053 if (SUCCEEDED(hrc))
7054 {
7055 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7056 if (SUCCEEDED(hrc))
7057 i_setModified(IsModified_MachineData);
7058 }
7059 }
7060 return hrc;
7061}
7062
7063HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7064{
7065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7066
7067 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7068
7069 return S_OK;
7070}
7071
7072HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7073{
7074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7075 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7076 if (SUCCEEDED(hrc))
7077 {
7078 hrc = mHWData.backupEx();
7079 if (SUCCEEDED(hrc))
7080 {
7081 i_setModified(IsModified_MachineData);
7082 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7083 }
7084 }
7085 return hrc;
7086}
7087
7088HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7089{
7090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7091
7092 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7093
7094 return S_OK;
7095}
7096
7097HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7098{
7099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7100
7101 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7102 if ( SUCCEEDED(hrc)
7103 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7104 {
7105 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7106 int vrc;
7107
7108 if (aAutostartEnabled)
7109 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7110 else
7111 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7112
7113 if (RT_SUCCESS(vrc))
7114 {
7115 hrc = mHWData.backupEx();
7116 if (SUCCEEDED(hrc))
7117 {
7118 i_setModified(IsModified_MachineData);
7119 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7120 }
7121 }
7122 else if (vrc == VERR_NOT_SUPPORTED)
7123 hrc = setError(VBOX_E_NOT_SUPPORTED,
7124 tr("The VM autostart feature is not supported on this platform"));
7125 else if (vrc == VERR_PATH_NOT_FOUND)
7126 hrc = setError(E_FAIL,
7127 tr("The path to the autostart database is not set"));
7128 else
7129 hrc = setError(E_UNEXPECTED,
7130 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7131 aAutostartEnabled ? "Adding" : "Removing",
7132 mUserData->s.strName.c_str(), vrc);
7133 }
7134 return hrc;
7135}
7136
7137HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7138{
7139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7140
7141 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7142
7143 return S_OK;
7144}
7145
7146HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7147{
7148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7149 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7150 if (SUCCEEDED(hrc))
7151 {
7152 hrc = mHWData.backupEx();
7153 if (SUCCEEDED(hrc))
7154 {
7155 i_setModified(IsModified_MachineData);
7156 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7157 }
7158 }
7159 return hrc;
7160}
7161
7162HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7163{
7164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7165
7166 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7167
7168 return S_OK;
7169}
7170
7171HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7172{
7173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7174 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7175 if ( SUCCEEDED(hrc)
7176 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7177 {
7178 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7179 int vrc;
7180
7181 if (aAutostopType != AutostopType_Disabled)
7182 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7183 else
7184 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7185
7186 if (RT_SUCCESS(vrc))
7187 {
7188 hrc = mHWData.backupEx();
7189 if (SUCCEEDED(hrc))
7190 {
7191 i_setModified(IsModified_MachineData);
7192 mHWData->mAutostart.enmAutostopType = aAutostopType;
7193 }
7194 }
7195 else if (vrc == VERR_NOT_SUPPORTED)
7196 hrc = setError(VBOX_E_NOT_SUPPORTED,
7197 tr("The VM autostop feature is not supported on this platform"));
7198 else if (vrc == VERR_PATH_NOT_FOUND)
7199 hrc = setError(E_FAIL,
7200 tr("The path to the autostart database is not set"));
7201 else
7202 hrc = setError(E_UNEXPECTED,
7203 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7204 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7205 mUserData->s.strName.c_str(), vrc);
7206 }
7207 return hrc;
7208}
7209
7210HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7211{
7212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7213
7214 aDefaultFrontend = mHWData->mDefaultFrontend;
7215
7216 return S_OK;
7217}
7218
7219HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7220{
7221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7222 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7223 if (SUCCEEDED(hrc))
7224 {
7225 hrc = mHWData.backupEx();
7226 if (SUCCEEDED(hrc))
7227 {
7228 i_setModified(IsModified_MachineData);
7229 mHWData->mDefaultFrontend = aDefaultFrontend;
7230 }
7231 }
7232 return hrc;
7233}
7234
7235HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7236{
7237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7238 size_t cbIcon = mUserData->s.ovIcon.size();
7239 aIcon.resize(cbIcon);
7240 if (cbIcon)
7241 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7242 return S_OK;
7243}
7244
7245HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7246{
7247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7248 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7249 if (SUCCEEDED(hrc))
7250 {
7251 i_setModified(IsModified_MachineData);
7252 mUserData.backup();
7253 size_t cbIcon = aIcon.size();
7254 mUserData->s.ovIcon.resize(cbIcon);
7255 if (cbIcon)
7256 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7257 }
7258 return hrc;
7259}
7260
7261HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7262{
7263#ifdef VBOX_WITH_USB
7264 *aUSBProxyAvailable = true;
7265#else
7266 *aUSBProxyAvailable = false;
7267#endif
7268 return S_OK;
7269}
7270
7271HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7272{
7273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7274
7275 aVMProcessPriority = mUserData->s.strVMPriority;
7276
7277 return S_OK;
7278}
7279
7280HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7281{
7282 RT_NOREF(aVMProcessPriority);
7283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7284 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7285 if (SUCCEEDED(hrc))
7286 {
7287 /** @todo r=klaus: currently this is marked as not implemented, as
7288 * the code for setting the priority of the process is not there
7289 * (neither when starting the VM nor at runtime). */
7290 ReturnComNotImplemented();
7291#if 0
7292 hrc = mUserData.backupEx();
7293 if (SUCCEEDED(hrc))
7294 {
7295 i_setModified(IsModified_MachineData);
7296 mUserData->s.strVMPriority = aVMProcessPriority;
7297 }
7298#endif
7299 }
7300 return hrc;
7301}
7302
7303HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7304 ComPtr<IProgress> &aProgress)
7305{
7306 ComObjPtr<Progress> pP;
7307 Progress *ppP = pP;
7308 IProgress *iP = static_cast<IProgress *>(ppP);
7309 IProgress **pProgress = &iP;
7310
7311 IMachine *pTarget = aTarget;
7312
7313 /* Convert the options. */
7314 RTCList<CloneOptions_T> optList;
7315 if (aOptions.size())
7316 for (size_t i = 0; i < aOptions.size(); ++i)
7317 optList.append(aOptions[i]);
7318
7319 if (optList.contains(CloneOptions_Link))
7320 {
7321 if (!i_isSnapshotMachine())
7322 return setError(E_INVALIDARG,
7323 tr("Linked clone can only be created from a snapshot"));
7324 if (aMode != CloneMode_MachineState)
7325 return setError(E_INVALIDARG,
7326 tr("Linked clone can only be created for a single machine state"));
7327 }
7328 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7329
7330 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7331
7332 HRESULT rc = pWorker->start(pProgress);
7333
7334 pP = static_cast<Progress *>(*pProgress);
7335 pP.queryInterfaceTo(aProgress.asOutParam());
7336
7337 return rc;
7338
7339}
7340
7341HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7342 const com::Utf8Str &aType,
7343 ComPtr<IProgress> &aProgress)
7344{
7345 LogFlowThisFuncEnter();
7346
7347 ComObjPtr<Progress> progress;
7348
7349 progress.createObject();
7350
7351 HRESULT rc = S_OK;
7352 Utf8Str targetPath = aTargetPath;
7353 Utf8Str type = aType;
7354
7355 /* Initialize our worker task */
7356 MachineMoveVM* task = NULL;
7357 try
7358 {
7359 task = new MachineMoveVM(this, targetPath, type, progress);
7360 }
7361 catch(...)
7362 {
7363 delete task;
7364 return rc;
7365 }
7366
7367 /*
7368 * task pointer will be owned by the ThreadTask class.
7369 * There is no need to call operator "delete" in the end.
7370 */
7371 rc = task->init();
7372 if (SUCCEEDED(rc))
7373 {
7374 rc = task->createThread();
7375 if (FAILED(rc))
7376 {
7377 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7378 }
7379
7380 /* Return progress to the caller */
7381 progress.queryInterfaceTo(aProgress.asOutParam());
7382 }
7383
7384 LogFlowThisFuncLeave();
7385 return rc;
7386
7387}
7388
7389HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7390{
7391 NOREF(aProgress);
7392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7393
7394 // This check should always fail.
7395 HRESULT rc = i_checkStateDependency(MutableStateDep);
7396 if (FAILED(rc)) return rc;
7397
7398 AssertFailedReturn(E_NOTIMPL);
7399}
7400
7401HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7402{
7403 NOREF(aSavedStateFile);
7404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7405
7406 // This check should always fail.
7407 HRESULT rc = i_checkStateDependency(MutableStateDep);
7408 if (FAILED(rc)) return rc;
7409
7410 AssertFailedReturn(E_NOTIMPL);
7411}
7412
7413HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7414{
7415 NOREF(aFRemoveFile);
7416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7417
7418 // This check should always fail.
7419 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7420 if (FAILED(rc)) return rc;
7421
7422 AssertFailedReturn(E_NOTIMPL);
7423}
7424
7425// public methods for internal purposes
7426/////////////////////////////////////////////////////////////////////////////
7427
7428/**
7429 * Adds the given IsModified_* flag to the dirty flags of the machine.
7430 * This must be called either during i_loadSettings or under the machine write lock.
7431 * @param fl Flag
7432 * @param fAllowStateModification If state modifications are allowed.
7433 */
7434void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7435{
7436 mData->flModifications |= fl;
7437 if (fAllowStateModification && i_isStateModificationAllowed())
7438 mData->mCurrentStateModified = true;
7439}
7440
7441/**
7442 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7443 * care of the write locking.
7444 *
7445 * @param fModification The flag to add.
7446 * @param fAllowStateModification If state modifications are allowed.
7447 */
7448void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7449{
7450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7451 i_setModified(fModification, fAllowStateModification);
7452}
7453
7454/**
7455 * Saves the registry entry of this machine to the given configuration node.
7456 *
7457 * @param data Machine registry data.
7458 *
7459 * @note locks this object for reading.
7460 */
7461HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7462{
7463 AutoLimitedCaller autoCaller(this);
7464 AssertComRCReturnRC(autoCaller.rc());
7465
7466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7467
7468 data.uuid = mData->mUuid;
7469 data.strSettingsFile = mData->m_strConfigFile;
7470
7471 return S_OK;
7472}
7473
7474/**
7475 * Calculates the absolute path of the given path taking the directory of the
7476 * machine settings file as the current directory.
7477 *
7478 * @param strPath Path to calculate the absolute path for.
7479 * @param aResult Where to put the result (used only on success, can be the
7480 * same Utf8Str instance as passed in @a aPath).
7481 * @return IPRT result.
7482 *
7483 * @note Locks this object for reading.
7484 */
7485int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7486{
7487 AutoCaller autoCaller(this);
7488 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7489
7490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7491
7492 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7493
7494 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7495
7496 strSettingsDir.stripFilename();
7497 char folder[RTPATH_MAX];
7498 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7499 if (RT_SUCCESS(vrc))
7500 aResult = folder;
7501
7502 return vrc;
7503}
7504
7505/**
7506 * Copies strSource to strTarget, making it relative to the machine folder
7507 * if it is a subdirectory thereof, or simply copying it otherwise.
7508 *
7509 * @param strSource Path to evaluate and copy.
7510 * @param strTarget Buffer to receive target path.
7511 *
7512 * @note Locks this object for reading.
7513 */
7514void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7515 Utf8Str &strTarget)
7516{
7517 AutoCaller autoCaller(this);
7518 AssertComRCReturn(autoCaller.rc(), (void)0);
7519
7520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7521
7522 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7523 // use strTarget as a temporary buffer to hold the machine settings dir
7524 strTarget = mData->m_strConfigFileFull;
7525 strTarget.stripFilename();
7526 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7527 {
7528 // is relative: then append what's left
7529 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7530 // for empty paths (only possible for subdirs) use "." to avoid
7531 // triggering default settings for not present config attributes.
7532 if (strTarget.isEmpty())
7533 strTarget = ".";
7534 }
7535 else
7536 // is not relative: then overwrite
7537 strTarget = strSource;
7538}
7539
7540/**
7541 * Returns the full path to the machine's log folder in the
7542 * \a aLogFolder argument.
7543 */
7544void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7545{
7546 AutoCaller autoCaller(this);
7547 AssertComRCReturnVoid(autoCaller.rc());
7548
7549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7550
7551 char szTmp[RTPATH_MAX];
7552 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7553 if (RT_SUCCESS(vrc))
7554 {
7555 if (szTmp[0] && !mUserData.isNull())
7556 {
7557 char szTmp2[RTPATH_MAX];
7558 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7559 if (RT_SUCCESS(vrc))
7560 aLogFolder = Utf8StrFmt("%s%c%s",
7561 szTmp2,
7562 RTPATH_DELIMITER,
7563 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7564 }
7565 else
7566 vrc = VERR_PATH_IS_RELATIVE;
7567 }
7568
7569 if (RT_FAILURE(vrc))
7570 {
7571 // fallback if VBOX_USER_LOGHOME is not set or invalid
7572 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7573 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7574 aLogFolder.append(RTPATH_DELIMITER);
7575 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7576 }
7577}
7578
7579/**
7580 * Returns the full path to the machine's log file for an given index.
7581 */
7582Utf8Str Machine::i_getLogFilename(ULONG idx)
7583{
7584 Utf8Str logFolder;
7585 getLogFolder(logFolder);
7586 Assert(logFolder.length());
7587
7588 Utf8Str log;
7589 if (idx == 0)
7590 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7591#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7592 else if (idx == 1)
7593 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7594 else
7595 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7596#else
7597 else
7598 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7599#endif
7600 return log;
7601}
7602
7603/**
7604 * Returns the full path to the machine's hardened log file.
7605 */
7606Utf8Str Machine::i_getHardeningLogFilename(void)
7607{
7608 Utf8Str strFilename;
7609 getLogFolder(strFilename);
7610 Assert(strFilename.length());
7611 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7612 return strFilename;
7613}
7614
7615
7616/**
7617 * Composes a unique saved state filename based on the current system time. The filename is
7618 * granular to the second so this will work so long as no more than one snapshot is taken on
7619 * a machine per second.
7620 *
7621 * Before version 4.1, we used this formula for saved state files:
7622 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7623 * which no longer works because saved state files can now be shared between the saved state of the
7624 * "saved" machine and an online snapshot, and the following would cause problems:
7625 * 1) save machine
7626 * 2) create online snapshot from that machine state --> reusing saved state file
7627 * 3) save machine again --> filename would be reused, breaking the online snapshot
7628 *
7629 * So instead we now use a timestamp.
7630 *
7631 * @param strStateFilePath
7632 */
7633
7634void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7635{
7636 AutoCaller autoCaller(this);
7637 AssertComRCReturnVoid(autoCaller.rc());
7638
7639 {
7640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7641 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7642 }
7643
7644 RTTIMESPEC ts;
7645 RTTimeNow(&ts);
7646 RTTIME time;
7647 RTTimeExplode(&time, &ts);
7648
7649 strStateFilePath += RTPATH_DELIMITER;
7650 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7651 time.i32Year, time.u8Month, time.u8MonthDay,
7652 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7653}
7654
7655/**
7656 * Returns the full path to the default video capture file.
7657 */
7658void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7659{
7660 AutoCaller autoCaller(this);
7661 AssertComRCReturnVoid(autoCaller.rc());
7662
7663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7664
7665 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7666 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7667 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7668}
7669
7670/**
7671 * Returns whether at least one USB controller is present for the VM.
7672 */
7673bool Machine::i_isUSBControllerPresent()
7674{
7675 AutoCaller autoCaller(this);
7676 AssertComRCReturn(autoCaller.rc(), false);
7677
7678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7679
7680 return (mUSBControllers->size() > 0);
7681}
7682
7683/**
7684 * @note Locks this object for writing, calls the client process
7685 * (inside the lock).
7686 */
7687HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7688 const Utf8Str &strFrontend,
7689 const Utf8Str &strEnvironment,
7690 ProgressProxy *aProgress)
7691{
7692 LogFlowThisFuncEnter();
7693
7694 AssertReturn(aControl, E_FAIL);
7695 AssertReturn(aProgress, E_FAIL);
7696 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7697
7698 AutoCaller autoCaller(this);
7699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7700
7701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7702
7703 if (!mData->mRegistered)
7704 return setError(E_UNEXPECTED,
7705 tr("The machine '%s' is not registered"),
7706 mUserData->s.strName.c_str());
7707
7708 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7709
7710 /* The process started when launching a VM with separate UI/VM processes is always
7711 * the UI process, i.e. needs special handling as it won't claim the session. */
7712 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7713
7714 if (fSeparate)
7715 {
7716 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7717 return setError(VBOX_E_INVALID_OBJECT_STATE,
7718 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7719 mUserData->s.strName.c_str());
7720 }
7721 else
7722 {
7723 if ( mData->mSession.mState == SessionState_Locked
7724 || mData->mSession.mState == SessionState_Spawning
7725 || mData->mSession.mState == SessionState_Unlocking)
7726 return setError(VBOX_E_INVALID_OBJECT_STATE,
7727 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7728 mUserData->s.strName.c_str());
7729
7730 /* may not be busy */
7731 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7732 }
7733
7734 /* get the path to the executable */
7735 char szPath[RTPATH_MAX];
7736 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7737 size_t cchBufLeft = strlen(szPath);
7738 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7739 szPath[cchBufLeft] = 0;
7740 char *pszNamePart = szPath + cchBufLeft;
7741 cchBufLeft = sizeof(szPath) - cchBufLeft;
7742
7743 int vrc = VINF_SUCCESS;
7744 RTPROCESS pid = NIL_RTPROCESS;
7745
7746 RTENV env = RTENV_DEFAULT;
7747
7748 if (!strEnvironment.isEmpty())
7749 {
7750 char *newEnvStr = NULL;
7751
7752 do
7753 {
7754 /* clone the current environment */
7755 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7756 AssertRCBreakStmt(vrc2, vrc = vrc2);
7757
7758 newEnvStr = RTStrDup(strEnvironment.c_str());
7759 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7760
7761 /* put new variables to the environment
7762 * (ignore empty variable names here since RTEnv API
7763 * intentionally doesn't do that) */
7764 char *var = newEnvStr;
7765 for (char *p = newEnvStr; *p; ++p)
7766 {
7767 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7768 {
7769 *p = '\0';
7770 if (*var)
7771 {
7772 char *val = strchr(var, '=');
7773 if (val)
7774 {
7775 *val++ = '\0';
7776 vrc2 = RTEnvSetEx(env, var, val);
7777 }
7778 else
7779 vrc2 = RTEnvUnsetEx(env, var);
7780 if (RT_FAILURE(vrc2))
7781 break;
7782 }
7783 var = p + 1;
7784 }
7785 }
7786 if (RT_SUCCESS(vrc2) && *var)
7787 vrc2 = RTEnvPutEx(env, var);
7788
7789 AssertRCBreakStmt(vrc2, vrc = vrc2);
7790 }
7791 while (0);
7792
7793 if (newEnvStr != NULL)
7794 RTStrFree(newEnvStr);
7795 }
7796
7797 /* Hardening logging */
7798#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7799 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7800 {
7801 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7802 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7803 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7804 {
7805 Utf8Str strStartupLogDir = strHardeningLogFile;
7806 strStartupLogDir.stripFilename();
7807 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7808 file without stripping the file. */
7809 }
7810 strSupHardeningLogArg.append(strHardeningLogFile);
7811
7812 /* Remove legacy log filename to avoid confusion. */
7813 Utf8Str strOldStartupLogFile;
7814 getLogFolder(strOldStartupLogFile);
7815 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7816 RTFileDelete(strOldStartupLogFile.c_str());
7817 }
7818 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7819#else
7820 const char *pszSupHardeningLogArg = NULL;
7821#endif
7822
7823 Utf8Str strCanonicalName;
7824
7825#ifdef VBOX_WITH_QTGUI
7826 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7827 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7828 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7829 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7830 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7831 {
7832 strCanonicalName = "GUI/Qt";
7833# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7834 /* Modify the base path so that we don't need to use ".." below. */
7835 RTPathStripTrailingSlash(szPath);
7836 RTPathStripFilename(szPath);
7837 cchBufLeft = strlen(szPath);
7838 pszNamePart = szPath + cchBufLeft;
7839 cchBufLeft = sizeof(szPath) - cchBufLeft;
7840
7841# define OSX_APP_NAME "VirtualBoxVM"
7842# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7843
7844 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7845 if ( strAppOverride.contains(".")
7846 || strAppOverride.contains("/")
7847 || strAppOverride.contains("\\")
7848 || strAppOverride.contains(":"))
7849 strAppOverride.setNull();
7850 Utf8Str strAppPath;
7851 if (!strAppOverride.isEmpty())
7852 {
7853 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7854 Utf8Str strFullPath(szPath);
7855 strFullPath.append(strAppPath);
7856 /* there is a race, but people using this deserve the failure */
7857 if (!RTFileExists(strFullPath.c_str()))
7858 strAppOverride.setNull();
7859 }
7860 if (strAppOverride.isEmpty())
7861 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7862 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7863 strcpy(pszNamePart, strAppPath.c_str());
7864# else
7865# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7866 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7867# else
7868 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7869# endif
7870 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7871 strcpy(pszNamePart, s_szVirtualBox_exe);
7872# endif
7873
7874 Utf8Str idStr = mData->mUuid.toString();
7875 const char *apszArgs[] =
7876 {
7877 szPath,
7878 "--comment", mUserData->s.strName.c_str(),
7879 "--startvm", idStr.c_str(),
7880 "--no-startvm-errormsgbox",
7881 NULL, /* For "--separate". */
7882 NULL, /* For "--sup-startup-log". */
7883 NULL
7884 };
7885 unsigned iArg = 6;
7886 if (fSeparate)
7887 apszArgs[iArg++] = "--separate";
7888 apszArgs[iArg++] = pszSupHardeningLogArg;
7889
7890 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7891 }
7892#else /* !VBOX_WITH_QTGUI */
7893 if (0)
7894 ;
7895#endif /* VBOX_WITH_QTGUI */
7896
7897 else
7898
7899#ifdef VBOX_WITH_VBOXSDL
7900 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7901 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7902 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7903 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7904 {
7905 strCanonicalName = "GUI/SDL";
7906 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7907 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7908 strcpy(pszNamePart, s_szVBoxSDL_exe);
7909
7910 Utf8Str idStr = mData->mUuid.toString();
7911 const char *apszArgs[] =
7912 {
7913 szPath,
7914 "--comment", mUserData->s.strName.c_str(),
7915 "--startvm", idStr.c_str(),
7916 NULL, /* For "--separate". */
7917 NULL, /* For "--sup-startup-log". */
7918 NULL
7919 };
7920 unsigned iArg = 5;
7921 if (fSeparate)
7922 apszArgs[iArg++] = "--separate";
7923 apszArgs[iArg++] = pszSupHardeningLogArg;
7924
7925 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7926 }
7927#else /* !VBOX_WITH_VBOXSDL */
7928 if (0)
7929 ;
7930#endif /* !VBOX_WITH_VBOXSDL */
7931
7932 else
7933
7934#ifdef VBOX_WITH_HEADLESS
7935 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7936 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7937 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7938 )
7939 {
7940 strCanonicalName = "headless";
7941 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7942 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7943 * and a VM works even if the server has not been installed.
7944 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7945 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7946 * differently in 4.0 and 3.x.
7947 */
7948 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7949 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7950 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7951
7952 Utf8Str idStr = mData->mUuid.toString();
7953 const char *apszArgs[] =
7954 {
7955 szPath,
7956 "--comment", mUserData->s.strName.c_str(),
7957 "--startvm", idStr.c_str(),
7958 "--vrde", "config",
7959 NULL, /* For "--capture". */
7960 NULL, /* For "--sup-startup-log". */
7961 NULL
7962 };
7963 unsigned iArg = 7;
7964 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7965 apszArgs[iArg++] = "--capture";
7966 apszArgs[iArg++] = pszSupHardeningLogArg;
7967
7968# ifdef RT_OS_WINDOWS
7969 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7970# else
7971 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7972# endif
7973 }
7974#else /* !VBOX_WITH_HEADLESS */
7975 if (0)
7976 ;
7977#endif /* !VBOX_WITH_HEADLESS */
7978 else
7979 {
7980 RTEnvDestroy(env);
7981 return setError(E_INVALIDARG,
7982 tr("Invalid frontend name: '%s'"),
7983 strFrontend.c_str());
7984 }
7985
7986 RTEnvDestroy(env);
7987
7988 if (RT_FAILURE(vrc))
7989 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7990 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7991 mUserData->s.strName.c_str(), vrc);
7992
7993 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7994
7995 if (!fSeparate)
7996 {
7997 /*
7998 * Note that we don't release the lock here before calling the client,
7999 * because it doesn't need to call us back if called with a NULL argument.
8000 * Releasing the lock here is dangerous because we didn't prepare the
8001 * launch data yet, but the client we've just started may happen to be
8002 * too fast and call LockMachine() that will fail (because of PID, etc.),
8003 * so that the Machine will never get out of the Spawning session state.
8004 */
8005
8006 /* inform the session that it will be a remote one */
8007 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8008#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8009 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8010#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8011 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8012#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8013 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8014
8015 if (FAILED(rc))
8016 {
8017 /* restore the session state */
8018 mData->mSession.mState = SessionState_Unlocked;
8019 alock.release();
8020 mParent->i_addProcessToReap(pid);
8021 /* The failure may occur w/o any error info (from RPC), so provide one */
8022 return setError(VBOX_E_VM_ERROR,
8023 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8024 }
8025
8026 /* attach launch data to the machine */
8027 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8028 mData->mSession.mRemoteControls.push_back(aControl);
8029 mData->mSession.mProgress = aProgress;
8030 mData->mSession.mPID = pid;
8031 mData->mSession.mState = SessionState_Spawning;
8032 Assert(strCanonicalName.isNotEmpty());
8033 mData->mSession.mName = strCanonicalName;
8034 }
8035 else
8036 {
8037 /* For separate UI process we declare the launch as completed instantly, as the
8038 * actual headless VM start may or may not come. No point in remembering anything
8039 * yet, as what matters for us is when the headless VM gets started. */
8040 aProgress->i_notifyComplete(S_OK);
8041 }
8042
8043 alock.release();
8044 mParent->i_addProcessToReap(pid);
8045
8046 LogFlowThisFuncLeave();
8047 return S_OK;
8048}
8049
8050/**
8051 * Returns @c true if the given session machine instance has an open direct
8052 * session (and optionally also for direct sessions which are closing) and
8053 * returns the session control machine instance if so.
8054 *
8055 * Note that when the method returns @c false, the arguments remain unchanged.
8056 *
8057 * @param aMachine Session machine object.
8058 * @param aControl Direct session control object (optional).
8059 * @param aRequireVM If true then only allow VM sessions.
8060 * @param aAllowClosing If true then additionally a session which is currently
8061 * being closed will also be allowed.
8062 *
8063 * @note locks this object for reading.
8064 */
8065bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8066 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8067 bool aRequireVM /*= false*/,
8068 bool aAllowClosing /*= false*/)
8069{
8070 AutoLimitedCaller autoCaller(this);
8071 AssertComRCReturn(autoCaller.rc(), false);
8072
8073 /* just return false for inaccessible machines */
8074 if (getObjectState().getState() != ObjectState::Ready)
8075 return false;
8076
8077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8078
8079 if ( ( mData->mSession.mState == SessionState_Locked
8080 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8081 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8082 )
8083 {
8084 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8085
8086 aMachine = mData->mSession.mMachine;
8087
8088 if (aControl != NULL)
8089 *aControl = mData->mSession.mDirectControl;
8090
8091 return true;
8092 }
8093
8094 return false;
8095}
8096
8097/**
8098 * Returns @c true if the given machine has an spawning direct session.
8099 *
8100 * @note locks this object for reading.
8101 */
8102bool Machine::i_isSessionSpawning()
8103{
8104 AutoLimitedCaller autoCaller(this);
8105 AssertComRCReturn(autoCaller.rc(), false);
8106
8107 /* just return false for inaccessible machines */
8108 if (getObjectState().getState() != ObjectState::Ready)
8109 return false;
8110
8111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8112
8113 if (mData->mSession.mState == SessionState_Spawning)
8114 return true;
8115
8116 return false;
8117}
8118
8119/**
8120 * Called from the client watcher thread to check for unexpected client process
8121 * death during Session_Spawning state (e.g. before it successfully opened a
8122 * direct session).
8123 *
8124 * On Win32 and on OS/2, this method is called only when we've got the
8125 * direct client's process termination notification, so it always returns @c
8126 * true.
8127 *
8128 * On other platforms, this method returns @c true if the client process is
8129 * terminated and @c false if it's still alive.
8130 *
8131 * @note Locks this object for writing.
8132 */
8133bool Machine::i_checkForSpawnFailure()
8134{
8135 AutoCaller autoCaller(this);
8136 if (!autoCaller.isOk())
8137 {
8138 /* nothing to do */
8139 LogFlowThisFunc(("Already uninitialized!\n"));
8140 return true;
8141 }
8142
8143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8144
8145 if (mData->mSession.mState != SessionState_Spawning)
8146 {
8147 /* nothing to do */
8148 LogFlowThisFunc(("Not spawning any more!\n"));
8149 return true;
8150 }
8151
8152 HRESULT rc = S_OK;
8153
8154 /* PID not yet initialized, skip check. */
8155 if (mData->mSession.mPID == NIL_RTPROCESS)
8156 return false;
8157
8158 RTPROCSTATUS status;
8159 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8160
8161 if (vrc != VERR_PROCESS_RUNNING)
8162 {
8163 Utf8Str strExtraInfo;
8164
8165#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8166 /* If the startup logfile exists and is of non-zero length, tell the
8167 user to look there for more details to encourage them to attach it
8168 when reporting startup issues. */
8169 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8170 uint64_t cbStartupLogFile = 0;
8171 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8172 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8173 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8174#endif
8175
8176 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8177 rc = setError(E_FAIL,
8178 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8179 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8180 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8181 rc = setError(E_FAIL,
8182 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8183 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8184 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8185 rc = setError(E_FAIL,
8186 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8187 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8188 else
8189 rc = setErrorBoth(E_FAIL, vrc,
8190 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8191 i_getName().c_str(), vrc, strExtraInfo.c_str());
8192 }
8193
8194 if (FAILED(rc))
8195 {
8196 /* Close the remote session, remove the remote control from the list
8197 * and reset session state to Closed (@note keep the code in sync with
8198 * the relevant part in LockMachine()). */
8199
8200 Assert(mData->mSession.mRemoteControls.size() == 1);
8201 if (mData->mSession.mRemoteControls.size() == 1)
8202 {
8203 ErrorInfoKeeper eik;
8204 mData->mSession.mRemoteControls.front()->Uninitialize();
8205 }
8206
8207 mData->mSession.mRemoteControls.clear();
8208 mData->mSession.mState = SessionState_Unlocked;
8209
8210 /* finalize the progress after setting the state */
8211 if (!mData->mSession.mProgress.isNull())
8212 {
8213 mData->mSession.mProgress->notifyComplete(rc);
8214 mData->mSession.mProgress.setNull();
8215 }
8216
8217 mData->mSession.mPID = NIL_RTPROCESS;
8218
8219 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8220 return true;
8221 }
8222
8223 return false;
8224}
8225
8226/**
8227 * Checks whether the machine can be registered. If so, commits and saves
8228 * all settings.
8229 *
8230 * @note Must be called from mParent's write lock. Locks this object and
8231 * children for writing.
8232 */
8233HRESULT Machine::i_prepareRegister()
8234{
8235 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8236
8237 AutoLimitedCaller autoCaller(this);
8238 AssertComRCReturnRC(autoCaller.rc());
8239
8240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8241
8242 /* wait for state dependents to drop to zero */
8243 i_ensureNoStateDependencies();
8244
8245 if (!mData->mAccessible)
8246 return setError(VBOX_E_INVALID_OBJECT_STATE,
8247 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8248 mUserData->s.strName.c_str(),
8249 mData->mUuid.toString().c_str());
8250
8251 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8252
8253 if (mData->mRegistered)
8254 return setError(VBOX_E_INVALID_OBJECT_STATE,
8255 tr("The machine '%s' with UUID {%s} is already registered"),
8256 mUserData->s.strName.c_str(),
8257 mData->mUuid.toString().c_str());
8258
8259 HRESULT rc = S_OK;
8260
8261 // Ensure the settings are saved. If we are going to be registered and
8262 // no config file exists yet, create it by calling i_saveSettings() too.
8263 if ( (mData->flModifications)
8264 || (!mData->pMachineConfigFile->fileExists())
8265 )
8266 {
8267 rc = i_saveSettings(NULL);
8268 // no need to check whether VirtualBox.xml needs saving too since
8269 // we can't have a machine XML file rename pending
8270 if (FAILED(rc)) return rc;
8271 }
8272
8273 /* more config checking goes here */
8274
8275 if (SUCCEEDED(rc))
8276 {
8277 /* we may have had implicit modifications we want to fix on success */
8278 i_commit();
8279
8280 mData->mRegistered = true;
8281 }
8282 else
8283 {
8284 /* we may have had implicit modifications we want to cancel on failure*/
8285 i_rollback(false /* aNotify */);
8286 }
8287
8288 return rc;
8289}
8290
8291/**
8292 * Increases the number of objects dependent on the machine state or on the
8293 * registered state. Guarantees that these two states will not change at least
8294 * until #i_releaseStateDependency() is called.
8295 *
8296 * Depending on the @a aDepType value, additional state checks may be made.
8297 * These checks will set extended error info on failure. See
8298 * #i_checkStateDependency() for more info.
8299 *
8300 * If this method returns a failure, the dependency is not added and the caller
8301 * is not allowed to rely on any particular machine state or registration state
8302 * value and may return the failed result code to the upper level.
8303 *
8304 * @param aDepType Dependency type to add.
8305 * @param aState Current machine state (NULL if not interested).
8306 * @param aRegistered Current registered state (NULL if not interested).
8307 *
8308 * @note Locks this object for writing.
8309 */
8310HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8311 MachineState_T *aState /* = NULL */,
8312 BOOL *aRegistered /* = NULL */)
8313{
8314 AutoCaller autoCaller(this);
8315 AssertComRCReturnRC(autoCaller.rc());
8316
8317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8318
8319 HRESULT rc = i_checkStateDependency(aDepType);
8320 if (FAILED(rc)) return rc;
8321
8322 {
8323 if (mData->mMachineStateChangePending != 0)
8324 {
8325 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8326 * drop to zero so don't add more. It may make sense to wait a bit
8327 * and retry before reporting an error (since the pending state
8328 * transition should be really quick) but let's just assert for
8329 * now to see if it ever happens on practice. */
8330
8331 AssertFailed();
8332
8333 return setError(E_ACCESSDENIED,
8334 tr("Machine state change is in progress. Please retry the operation later."));
8335 }
8336
8337 ++mData->mMachineStateDeps;
8338 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8339 }
8340
8341 if (aState)
8342 *aState = mData->mMachineState;
8343 if (aRegistered)
8344 *aRegistered = mData->mRegistered;
8345
8346 return S_OK;
8347}
8348
8349/**
8350 * Decreases the number of objects dependent on the machine state.
8351 * Must always complete the #i_addStateDependency() call after the state
8352 * dependency is no more necessary.
8353 */
8354void Machine::i_releaseStateDependency()
8355{
8356 AutoCaller autoCaller(this);
8357 AssertComRCReturnVoid(autoCaller.rc());
8358
8359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8360
8361 /* releaseStateDependency() w/o addStateDependency()? */
8362 AssertReturnVoid(mData->mMachineStateDeps != 0);
8363 -- mData->mMachineStateDeps;
8364
8365 if (mData->mMachineStateDeps == 0)
8366 {
8367 /* inform i_ensureNoStateDependencies() that there are no more deps */
8368 if (mData->mMachineStateChangePending != 0)
8369 {
8370 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8371 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8372 }
8373 }
8374}
8375
8376Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8377{
8378 /* start with nothing found */
8379 Utf8Str strResult("");
8380
8381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8382
8383 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8384 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8385 // found:
8386 strResult = it->second; // source is a Utf8Str
8387
8388 return strResult;
8389}
8390
8391// protected methods
8392/////////////////////////////////////////////////////////////////////////////
8393
8394/**
8395 * Performs machine state checks based on the @a aDepType value. If a check
8396 * fails, this method will set extended error info, otherwise it will return
8397 * S_OK. It is supposed, that on failure, the caller will immediately return
8398 * the return value of this method to the upper level.
8399 *
8400 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8401 *
8402 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8403 * current state of this machine object allows to change settings of the
8404 * machine (i.e. the machine is not registered, or registered but not running
8405 * and not saved). It is useful to call this method from Machine setters
8406 * before performing any change.
8407 *
8408 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8409 * as for MutableStateDep except that if the machine is saved, S_OK is also
8410 * returned. This is useful in setters which allow changing machine
8411 * properties when it is in the saved state.
8412 *
8413 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8414 * if the current state of this machine object allows to change runtime
8415 * changeable settings of the machine (i.e. the machine is not registered, or
8416 * registered but either running or not running and not saved). It is useful
8417 * to call this method from Machine setters before performing any changes to
8418 * runtime changeable settings.
8419 *
8420 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8421 * the same as for MutableOrRunningStateDep except that if the machine is
8422 * saved, S_OK is also returned. This is useful in setters which allow
8423 * changing runtime and saved state changeable machine properties.
8424 *
8425 * @param aDepType Dependency type to check.
8426 *
8427 * @note Non Machine based classes should use #i_addStateDependency() and
8428 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8429 * template.
8430 *
8431 * @note This method must be called from under this object's read or write
8432 * lock.
8433 */
8434HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8435{
8436 switch (aDepType)
8437 {
8438 case AnyStateDep:
8439 {
8440 break;
8441 }
8442 case MutableStateDep:
8443 {
8444 if ( mData->mRegistered
8445 && ( !i_isSessionMachine()
8446 || ( mData->mMachineState != MachineState_Aborted
8447 && mData->mMachineState != MachineState_Teleported
8448 && mData->mMachineState != MachineState_PoweredOff
8449 )
8450 )
8451 )
8452 return setError(VBOX_E_INVALID_VM_STATE,
8453 tr("The machine is not mutable (state is %s)"),
8454 Global::stringifyMachineState(mData->mMachineState));
8455 break;
8456 }
8457 case MutableOrSavedStateDep:
8458 {
8459 if ( mData->mRegistered
8460 && ( !i_isSessionMachine()
8461 || ( mData->mMachineState != MachineState_Aborted
8462 && mData->mMachineState != MachineState_Teleported
8463 && mData->mMachineState != MachineState_Saved
8464 && mData->mMachineState != MachineState_PoweredOff
8465 )
8466 )
8467 )
8468 return setError(VBOX_E_INVALID_VM_STATE,
8469 tr("The machine is not mutable or saved (state is %s)"),
8470 Global::stringifyMachineState(mData->mMachineState));
8471 break;
8472 }
8473 case MutableOrRunningStateDep:
8474 {
8475 if ( mData->mRegistered
8476 && ( !i_isSessionMachine()
8477 || ( mData->mMachineState != MachineState_Aborted
8478 && mData->mMachineState != MachineState_Teleported
8479 && mData->mMachineState != MachineState_PoweredOff
8480 && !Global::IsOnline(mData->mMachineState)
8481 )
8482 )
8483 )
8484 return setError(VBOX_E_INVALID_VM_STATE,
8485 tr("The machine is not mutable or running (state is %s)"),
8486 Global::stringifyMachineState(mData->mMachineState));
8487 break;
8488 }
8489 case MutableOrSavedOrRunningStateDep:
8490 {
8491 if ( mData->mRegistered
8492 && ( !i_isSessionMachine()
8493 || ( mData->mMachineState != MachineState_Aborted
8494 && mData->mMachineState != MachineState_Teleported
8495 && mData->mMachineState != MachineState_Saved
8496 && mData->mMachineState != MachineState_PoweredOff
8497 && !Global::IsOnline(mData->mMachineState)
8498 )
8499 )
8500 )
8501 return setError(VBOX_E_INVALID_VM_STATE,
8502 tr("The machine is not mutable, saved or running (state is %s)"),
8503 Global::stringifyMachineState(mData->mMachineState));
8504 break;
8505 }
8506 }
8507
8508 return S_OK;
8509}
8510
8511/**
8512 * Helper to initialize all associated child objects and allocate data
8513 * structures.
8514 *
8515 * This method must be called as a part of the object's initialization procedure
8516 * (usually done in the #init() method).
8517 *
8518 * @note Must be called only from #init() or from #i_registeredInit().
8519 */
8520HRESULT Machine::initDataAndChildObjects()
8521{
8522 AutoCaller autoCaller(this);
8523 AssertComRCReturnRC(autoCaller.rc());
8524 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8525 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8526
8527 AssertReturn(!mData->mAccessible, E_FAIL);
8528
8529 /* allocate data structures */
8530 mSSData.allocate();
8531 mUserData.allocate();
8532 mHWData.allocate();
8533 mMediumAttachments.allocate();
8534 mStorageControllers.allocate();
8535 mUSBControllers.allocate();
8536
8537 /* initialize mOSTypeId */
8538 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8539
8540/** @todo r=bird: init() methods never fails, right? Why don't we make them
8541 * return void then! */
8542
8543 /* create associated BIOS settings object */
8544 unconst(mBIOSSettings).createObject();
8545 mBIOSSettings->init(this);
8546
8547 /* create an associated VRDE object (default is disabled) */
8548 unconst(mVRDEServer).createObject();
8549 mVRDEServer->init(this);
8550
8551 /* create associated serial port objects */
8552 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8553 {
8554 unconst(mSerialPorts[slot]).createObject();
8555 mSerialPorts[slot]->init(this, slot);
8556 }
8557
8558 /* create associated parallel port objects */
8559 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8560 {
8561 unconst(mParallelPorts[slot]).createObject();
8562 mParallelPorts[slot]->init(this, slot);
8563 }
8564
8565 /* create the audio adapter object (always present, default is disabled) */
8566 unconst(mAudioAdapter).createObject();
8567 mAudioAdapter->init(this);
8568
8569 /* create the USB device filters object (always present) */
8570 unconst(mUSBDeviceFilters).createObject();
8571 mUSBDeviceFilters->init(this);
8572
8573 /* create associated network adapter objects */
8574 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8575 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8576 {
8577 unconst(mNetworkAdapters[slot]).createObject();
8578 mNetworkAdapters[slot]->init(this, slot);
8579 }
8580
8581 /* create the bandwidth control */
8582 unconst(mBandwidthControl).createObject();
8583 mBandwidthControl->init(this);
8584
8585 return S_OK;
8586}
8587
8588/**
8589 * Helper to uninitialize all associated child objects and to free all data
8590 * structures.
8591 *
8592 * This method must be called as a part of the object's uninitialization
8593 * procedure (usually done in the #uninit() method).
8594 *
8595 * @note Must be called only from #uninit() or from #i_registeredInit().
8596 */
8597void Machine::uninitDataAndChildObjects()
8598{
8599 AutoCaller autoCaller(this);
8600 AssertComRCReturnVoid(autoCaller.rc());
8601 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8602 || getObjectState().getState() == ObjectState::Limited);
8603
8604 /* tell all our other child objects we've been uninitialized */
8605 if (mBandwidthControl)
8606 {
8607 mBandwidthControl->uninit();
8608 unconst(mBandwidthControl).setNull();
8609 }
8610
8611 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8612 {
8613 if (mNetworkAdapters[slot])
8614 {
8615 mNetworkAdapters[slot]->uninit();
8616 unconst(mNetworkAdapters[slot]).setNull();
8617 }
8618 }
8619
8620 if (mUSBDeviceFilters)
8621 {
8622 mUSBDeviceFilters->uninit();
8623 unconst(mUSBDeviceFilters).setNull();
8624 }
8625
8626 if (mAudioAdapter)
8627 {
8628 mAudioAdapter->uninit();
8629 unconst(mAudioAdapter).setNull();
8630 }
8631
8632 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8633 {
8634 if (mParallelPorts[slot])
8635 {
8636 mParallelPorts[slot]->uninit();
8637 unconst(mParallelPorts[slot]).setNull();
8638 }
8639 }
8640
8641 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8642 {
8643 if (mSerialPorts[slot])
8644 {
8645 mSerialPorts[slot]->uninit();
8646 unconst(mSerialPorts[slot]).setNull();
8647 }
8648 }
8649
8650 if (mVRDEServer)
8651 {
8652 mVRDEServer->uninit();
8653 unconst(mVRDEServer).setNull();
8654 }
8655
8656 if (mBIOSSettings)
8657 {
8658 mBIOSSettings->uninit();
8659 unconst(mBIOSSettings).setNull();
8660 }
8661
8662 /* Deassociate media (only when a real Machine or a SnapshotMachine
8663 * instance is uninitialized; SessionMachine instances refer to real
8664 * Machine media). This is necessary for a clean re-initialization of
8665 * the VM after successfully re-checking the accessibility state. Note
8666 * that in case of normal Machine or SnapshotMachine uninitialization (as
8667 * a result of unregistering or deleting the snapshot), outdated media
8668 * attachments will already be uninitialized and deleted, so this
8669 * code will not affect them. */
8670 if ( !mMediumAttachments.isNull()
8671 && !i_isSessionMachine()
8672 )
8673 {
8674 for (MediumAttachmentList::const_iterator
8675 it = mMediumAttachments->begin();
8676 it != mMediumAttachments->end();
8677 ++it)
8678 {
8679 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8680 if (pMedium.isNull())
8681 continue;
8682 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8683 AssertComRC(rc);
8684 }
8685 }
8686
8687 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8688 {
8689 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8690 if (mData->mFirstSnapshot)
8691 {
8692 // snapshots tree is protected by machine write lock; strictly
8693 // this isn't necessary here since we're deleting the entire
8694 // machine, but otherwise we assert in Snapshot::uninit()
8695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8696 mData->mFirstSnapshot->uninit();
8697 mData->mFirstSnapshot.setNull();
8698 }
8699
8700 mData->mCurrentSnapshot.setNull();
8701 }
8702
8703 /* free data structures (the essential mData structure is not freed here
8704 * since it may be still in use) */
8705 mMediumAttachments.free();
8706 mStorageControllers.free();
8707 mUSBControllers.free();
8708 mHWData.free();
8709 mUserData.free();
8710 mSSData.free();
8711}
8712
8713/**
8714 * Returns a pointer to the Machine object for this machine that acts like a
8715 * parent for complex machine data objects such as shared folders, etc.
8716 *
8717 * For primary Machine objects and for SnapshotMachine objects, returns this
8718 * object's pointer itself. For SessionMachine objects, returns the peer
8719 * (primary) machine pointer.
8720 */
8721Machine *Machine::i_getMachine()
8722{
8723 if (i_isSessionMachine())
8724 return (Machine*)mPeer;
8725 return this;
8726}
8727
8728/**
8729 * Makes sure that there are no machine state dependents. If necessary, waits
8730 * for the number of dependents to drop to zero.
8731 *
8732 * Make sure this method is called from under this object's write lock to
8733 * guarantee that no new dependents may be added when this method returns
8734 * control to the caller.
8735 *
8736 * @note Locks this object for writing. The lock will be released while waiting
8737 * (if necessary).
8738 *
8739 * @warning To be used only in methods that change the machine state!
8740 */
8741void Machine::i_ensureNoStateDependencies()
8742{
8743 AssertReturnVoid(isWriteLockOnCurrentThread());
8744
8745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8746
8747 /* Wait for all state dependents if necessary */
8748 if (mData->mMachineStateDeps != 0)
8749 {
8750 /* lazy semaphore creation */
8751 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8752 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8753
8754 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8755 mData->mMachineStateDeps));
8756
8757 ++mData->mMachineStateChangePending;
8758
8759 /* reset the semaphore before waiting, the last dependent will signal
8760 * it */
8761 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8762
8763 alock.release();
8764
8765 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8766
8767 alock.acquire();
8768
8769 -- mData->mMachineStateChangePending;
8770 }
8771}
8772
8773/**
8774 * Changes the machine state and informs callbacks.
8775 *
8776 * This method is not intended to fail so it either returns S_OK or asserts (and
8777 * returns a failure).
8778 *
8779 * @note Locks this object for writing.
8780 */
8781HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8782{
8783 LogFlowThisFuncEnter();
8784 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8785 Assert(aMachineState != MachineState_Null);
8786
8787 AutoCaller autoCaller(this);
8788 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8789
8790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8791
8792 /* wait for state dependents to drop to zero */
8793 i_ensureNoStateDependencies();
8794
8795 MachineState_T const enmOldState = mData->mMachineState;
8796 if (enmOldState != aMachineState)
8797 {
8798 mData->mMachineState = aMachineState;
8799 RTTimeNow(&mData->mLastStateChange);
8800
8801#ifdef VBOX_WITH_DTRACE_R3_MAIN
8802 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8803#endif
8804 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8805 }
8806
8807 LogFlowThisFuncLeave();
8808 return S_OK;
8809}
8810
8811/**
8812 * Searches for a shared folder with the given logical name
8813 * in the collection of shared folders.
8814 *
8815 * @param aName logical name of the shared folder
8816 * @param aSharedFolder where to return the found object
8817 * @param aSetError whether to set the error info if the folder is
8818 * not found
8819 * @return
8820 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8821 *
8822 * @note
8823 * must be called from under the object's lock!
8824 */
8825HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8826 ComObjPtr<SharedFolder> &aSharedFolder,
8827 bool aSetError /* = false */)
8828{
8829 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8830 for (HWData::SharedFolderList::const_iterator
8831 it = mHWData->mSharedFolders.begin();
8832 it != mHWData->mSharedFolders.end();
8833 ++it)
8834 {
8835 SharedFolder *pSF = *it;
8836 AutoCaller autoCaller(pSF);
8837 if (pSF->i_getName() == aName)
8838 {
8839 aSharedFolder = pSF;
8840 rc = S_OK;
8841 break;
8842 }
8843 }
8844
8845 if (aSetError && FAILED(rc))
8846 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8847
8848 return rc;
8849}
8850
8851/**
8852 * Initializes all machine instance data from the given settings structures
8853 * from XML. The exception is the machine UUID which needs special handling
8854 * depending on the caller's use case, so the caller needs to set that herself.
8855 *
8856 * This gets called in several contexts during machine initialization:
8857 *
8858 * -- When machine XML exists on disk already and needs to be loaded into memory,
8859 * for example, from #i_registeredInit() to load all registered machines on
8860 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8861 * attached to the machine should be part of some media registry already.
8862 *
8863 * -- During OVF import, when a machine config has been constructed from an
8864 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8865 * ensure that the media listed as attachments in the config (which have
8866 * been imported from the OVF) receive the correct registry ID.
8867 *
8868 * -- During VM cloning.
8869 *
8870 * @param config Machine settings from XML.
8871 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8872 * for each attached medium in the config.
8873 * @return
8874 */
8875HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8876 const Guid *puuidRegistry)
8877{
8878 // copy name, description, OS type, teleporter, UTC etc.
8879 mUserData->s = config.machineUserData;
8880
8881 // look up the object by Id to check it is valid
8882 ComObjPtr<GuestOSType> pGuestOSType;
8883 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8884 if (!pGuestOSType.isNull())
8885 mUserData->s.strOsType = pGuestOSType->i_id();
8886
8887 // stateFile (optional)
8888 if (config.strStateFile.isEmpty())
8889 mSSData->strStateFilePath.setNull();
8890 else
8891 {
8892 Utf8Str stateFilePathFull(config.strStateFile);
8893 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8894 if (RT_FAILURE(vrc))
8895 return setErrorBoth(E_FAIL, vrc,
8896 tr("Invalid saved state file path '%s' (%Rrc)"),
8897 config.strStateFile.c_str(),
8898 vrc);
8899 mSSData->strStateFilePath = stateFilePathFull;
8900 }
8901
8902 // snapshot folder needs special processing so set it again
8903 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8904 if (FAILED(rc)) return rc;
8905
8906 /* Copy the extra data items (config may or may not be the same as
8907 * mData->pMachineConfigFile) if necessary. When loading the XML files
8908 * from disk they are the same, but not for OVF import. */
8909 if (mData->pMachineConfigFile != &config)
8910 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8911
8912 /* currentStateModified (optional, default is true) */
8913 mData->mCurrentStateModified = config.fCurrentStateModified;
8914
8915 mData->mLastStateChange = config.timeLastStateChange;
8916
8917 /*
8918 * note: all mUserData members must be assigned prior this point because
8919 * we need to commit changes in order to let mUserData be shared by all
8920 * snapshot machine instances.
8921 */
8922 mUserData.commitCopy();
8923
8924 // machine registry, if present (must be loaded before snapshots)
8925 if (config.canHaveOwnMediaRegistry())
8926 {
8927 // determine machine folder
8928 Utf8Str strMachineFolder = i_getSettingsFileFull();
8929 strMachineFolder.stripFilename();
8930 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8931 config.mediaRegistry,
8932 strMachineFolder);
8933 if (FAILED(rc)) return rc;
8934 }
8935
8936 /* Snapshot node (optional) */
8937 size_t cRootSnapshots;
8938 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8939 {
8940 // there must be only one root snapshot
8941 Assert(cRootSnapshots == 1);
8942
8943 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8944
8945 rc = i_loadSnapshot(snap,
8946 config.uuidCurrentSnapshot,
8947 NULL); // no parent == first snapshot
8948 if (FAILED(rc)) return rc;
8949 }
8950
8951 // hardware data
8952 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8953 if (FAILED(rc)) return rc;
8954
8955 /*
8956 * NOTE: the assignment below must be the last thing to do,
8957 * otherwise it will be not possible to change the settings
8958 * somewhere in the code above because all setters will be
8959 * blocked by i_checkStateDependency(MutableStateDep).
8960 */
8961
8962 /* set the machine state to Aborted or Saved when appropriate */
8963 if (config.fAborted)
8964 {
8965 mSSData->strStateFilePath.setNull();
8966
8967 /* no need to use i_setMachineState() during init() */
8968 mData->mMachineState = MachineState_Aborted;
8969 }
8970 else if (!mSSData->strStateFilePath.isEmpty())
8971 {
8972 /* no need to use i_setMachineState() during init() */
8973 mData->mMachineState = MachineState_Saved;
8974 }
8975
8976 // after loading settings, we are no longer different from the XML on disk
8977 mData->flModifications = 0;
8978
8979 return S_OK;
8980}
8981
8982/**
8983 * Recursively loads all snapshots starting from the given.
8984 *
8985 * @param data snapshot settings.
8986 * @param aCurSnapshotId Current snapshot ID from the settings file.
8987 * @param aParentSnapshot Parent snapshot.
8988 */
8989HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8990 const Guid &aCurSnapshotId,
8991 Snapshot *aParentSnapshot)
8992{
8993 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8994 AssertReturn(!i_isSessionMachine(), E_FAIL);
8995
8996 HRESULT rc = S_OK;
8997
8998 Utf8Str strStateFile;
8999 if (!data.strStateFile.isEmpty())
9000 {
9001 /* optional */
9002 strStateFile = data.strStateFile;
9003 int vrc = i_calculateFullPath(strStateFile, strStateFile);
9004 if (RT_FAILURE(vrc))
9005 return setErrorBoth(E_FAIL, vrc,
9006 tr("Invalid saved state file path '%s' (%Rrc)"),
9007 strStateFile.c_str(),
9008 vrc);
9009 }
9010
9011 /* create a snapshot machine object */
9012 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9013 pSnapshotMachine.createObject();
9014 rc = pSnapshotMachine->initFromSettings(this,
9015 data.hardware,
9016 &data.debugging,
9017 &data.autostart,
9018 data.uuid.ref(),
9019 strStateFile);
9020 if (FAILED(rc)) return rc;
9021
9022 /* create a snapshot object */
9023 ComObjPtr<Snapshot> pSnapshot;
9024 pSnapshot.createObject();
9025 /* initialize the snapshot */
9026 rc = pSnapshot->init(mParent, // VirtualBox object
9027 data.uuid,
9028 data.strName,
9029 data.strDescription,
9030 data.timestamp,
9031 pSnapshotMachine,
9032 aParentSnapshot);
9033 if (FAILED(rc)) return rc;
9034
9035 /* memorize the first snapshot if necessary */
9036 if (!mData->mFirstSnapshot)
9037 mData->mFirstSnapshot = pSnapshot;
9038
9039 /* memorize the current snapshot when appropriate */
9040 if ( !mData->mCurrentSnapshot
9041 && pSnapshot->i_getId() == aCurSnapshotId
9042 )
9043 mData->mCurrentSnapshot = pSnapshot;
9044
9045 // now create the children
9046 for (settings::SnapshotsList::const_iterator
9047 it = data.llChildSnapshots.begin();
9048 it != data.llChildSnapshots.end();
9049 ++it)
9050 {
9051 const settings::Snapshot &childData = *it;
9052 // recurse
9053 rc = i_loadSnapshot(childData,
9054 aCurSnapshotId,
9055 pSnapshot); // parent = the one we created above
9056 if (FAILED(rc)) return rc;
9057 }
9058
9059 return rc;
9060}
9061
9062/**
9063 * Loads settings into mHWData.
9064 *
9065 * @param puuidRegistry Registry ID.
9066 * @param puuidSnapshot Snapshot ID
9067 * @param data Reference to the hardware settings.
9068 * @param pDbg Pointer to the debugging settings.
9069 * @param pAutostart Pointer to the autostart settings.
9070 */
9071HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9072 const Guid *puuidSnapshot,
9073 const settings::Hardware &data,
9074 const settings::Debugging *pDbg,
9075 const settings::Autostart *pAutostart)
9076{
9077 AssertReturn(!i_isSessionMachine(), E_FAIL);
9078
9079 HRESULT rc = S_OK;
9080
9081 try
9082 {
9083 ComObjPtr<GuestOSType> pGuestOSType;
9084 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9085
9086 /* The hardware version attribute (optional). */
9087 mHWData->mHWVersion = data.strVersion;
9088 mHWData->mHardwareUUID = data.uuid;
9089
9090 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9091 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9092 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9093 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9094 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9095 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9096 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9097 mHWData->mPAEEnabled = data.fPAE;
9098 mHWData->mLongMode = data.enmLongMode;
9099 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9100 mHWData->mAPIC = data.fAPIC;
9101 mHWData->mX2APIC = data.fX2APIC;
9102 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9103 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9104 mHWData->mSpecCtrl = data.fSpecCtrl;
9105 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9106 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9107 mHWData->mCPUCount = data.cCPUs;
9108 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9109 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9110 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9111 mHWData->mCpuProfile = data.strCpuProfile;
9112
9113 // cpu
9114 if (mHWData->mCPUHotPlugEnabled)
9115 {
9116 for (settings::CpuList::const_iterator
9117 it = data.llCpus.begin();
9118 it != data.llCpus.end();
9119 ++it)
9120 {
9121 const settings::Cpu &cpu = *it;
9122
9123 mHWData->mCPUAttached[cpu.ulId] = true;
9124 }
9125 }
9126
9127 // cpuid leafs
9128 for (settings::CpuIdLeafsList::const_iterator
9129 it = data.llCpuIdLeafs.begin();
9130 it != data.llCpuIdLeafs.end();
9131 ++it)
9132 {
9133 const settings::CpuIdLeaf &rLeaf= *it;
9134 if ( rLeaf.idx < UINT32_C(0x20)
9135 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9136 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9137 mHWData->mCpuIdLeafList.push_back(rLeaf);
9138 /* else: just ignore */
9139 }
9140
9141 mHWData->mMemorySize = data.ulMemorySizeMB;
9142 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9143
9144 // boot order
9145 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9146 {
9147 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9148 if (it == data.mapBootOrder.end())
9149 mHWData->mBootOrder[i] = DeviceType_Null;
9150 else
9151 mHWData->mBootOrder[i] = it->second;
9152 }
9153
9154 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9155 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9156 mHWData->mMonitorCount = data.cMonitors;
9157 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9158 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9159 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9160 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9161 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9162 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9163 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9164 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9165 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9166 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9167 if (!data.strVideoCaptureFile.isEmpty())
9168 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9169 else
9170 mHWData->mVideoCaptureFile.setNull();
9171 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9172 mHWData->mFirmwareType = data.firmwareType;
9173 mHWData->mPointingHIDType = data.pointingHIDType;
9174 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9175 mHWData->mChipsetType = data.chipsetType;
9176 mHWData->mParavirtProvider = data.paravirtProvider;
9177 mHWData->mParavirtDebug = data.strParavirtDebug;
9178 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9179 mHWData->mHPETEnabled = data.fHPETEnabled;
9180
9181 /* VRDEServer */
9182 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9183 if (FAILED(rc)) return rc;
9184
9185 /* BIOS */
9186 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9187 if (FAILED(rc)) return rc;
9188
9189 // Bandwidth control (must come before network adapters)
9190 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9191 if (FAILED(rc)) return rc;
9192
9193 /* Shared folders */
9194 for (settings::USBControllerList::const_iterator
9195 it = data.usbSettings.llUSBControllers.begin();
9196 it != data.usbSettings.llUSBControllers.end();
9197 ++it)
9198 {
9199 const settings::USBController &settingsCtrl = *it;
9200 ComObjPtr<USBController> newCtrl;
9201
9202 newCtrl.createObject();
9203 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9204 mUSBControllers->push_back(newCtrl);
9205 }
9206
9207 /* USB device filters */
9208 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9209 if (FAILED(rc)) return rc;
9210
9211 // network adapters (establish array size first and apply defaults, to
9212 // ensure reading the same settings as we saved, since the list skips
9213 // adapters having defaults)
9214 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9215 size_t oldCount = mNetworkAdapters.size();
9216 if (newCount > oldCount)
9217 {
9218 mNetworkAdapters.resize(newCount);
9219 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9220 {
9221 unconst(mNetworkAdapters[slot]).createObject();
9222 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9223 }
9224 }
9225 else if (newCount < oldCount)
9226 mNetworkAdapters.resize(newCount);
9227 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9228 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9229 for (settings::NetworkAdaptersList::const_iterator
9230 it = data.llNetworkAdapters.begin();
9231 it != data.llNetworkAdapters.end();
9232 ++it)
9233 {
9234 const settings::NetworkAdapter &nic = *it;
9235
9236 /* slot uniqueness is guaranteed by XML Schema */
9237 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9238 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9239 if (FAILED(rc)) return rc;
9240 }
9241
9242 // serial ports (establish defaults first, to ensure reading the same
9243 // settings as we saved, since the list skips ports having defaults)
9244 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9245 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9246 for (settings::SerialPortsList::const_iterator
9247 it = data.llSerialPorts.begin();
9248 it != data.llSerialPorts.end();
9249 ++it)
9250 {
9251 const settings::SerialPort &s = *it;
9252
9253 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9254 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9255 if (FAILED(rc)) return rc;
9256 }
9257
9258 // parallel ports (establish defaults first, to ensure reading the same
9259 // settings as we saved, since the list skips ports having defaults)
9260 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9261 mParallelPorts[i]->i_applyDefaults();
9262 for (settings::ParallelPortsList::const_iterator
9263 it = data.llParallelPorts.begin();
9264 it != data.llParallelPorts.end();
9265 ++it)
9266 {
9267 const settings::ParallelPort &p = *it;
9268
9269 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9270 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9271 if (FAILED(rc)) return rc;
9272 }
9273
9274 /* AudioAdapter */
9275 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9276 if (FAILED(rc)) return rc;
9277
9278 /* storage controllers */
9279 rc = i_loadStorageControllers(data.storage,
9280 puuidRegistry,
9281 puuidSnapshot);
9282 if (FAILED(rc)) return rc;
9283
9284 /* Shared folders */
9285 for (settings::SharedFoldersList::const_iterator
9286 it = data.llSharedFolders.begin();
9287 it != data.llSharedFolders.end();
9288 ++it)
9289 {
9290 const settings::SharedFolder &sf = *it;
9291
9292 ComObjPtr<SharedFolder> sharedFolder;
9293 /* Check for double entries. Not allowed! */
9294 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9295 if (SUCCEEDED(rc))
9296 return setError(VBOX_E_OBJECT_IN_USE,
9297 tr("Shared folder named '%s' already exists"),
9298 sf.strName.c_str());
9299
9300 /* Create the new shared folder. Don't break on error. This will be
9301 * reported when the machine starts. */
9302 sharedFolder.createObject();
9303 rc = sharedFolder->init(i_getMachine(),
9304 sf.strName,
9305 sf.strHostPath,
9306 RT_BOOL(sf.fWritable),
9307 RT_BOOL(sf.fAutoMount),
9308 false /* fFailOnError */);
9309 if (FAILED(rc)) return rc;
9310 mHWData->mSharedFolders.push_back(sharedFolder);
9311 }
9312
9313 // Clipboard
9314 mHWData->mClipboardMode = data.clipboardMode;
9315
9316 // drag'n'drop
9317 mHWData->mDnDMode = data.dndMode;
9318
9319 // guest settings
9320 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9321
9322 // IO settings
9323 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9324 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9325
9326 // Host PCI devices
9327 for (settings::HostPCIDeviceAttachmentList::const_iterator
9328 it = data.pciAttachments.begin();
9329 it != data.pciAttachments.end();
9330 ++it)
9331 {
9332 const settings::HostPCIDeviceAttachment &hpda = *it;
9333 ComObjPtr<PCIDeviceAttachment> pda;
9334
9335 pda.createObject();
9336 pda->i_loadSettings(this, hpda);
9337 mHWData->mPCIDeviceAssignments.push_back(pda);
9338 }
9339
9340 /*
9341 * (The following isn't really real hardware, but it lives in HWData
9342 * for reasons of convenience.)
9343 */
9344
9345#ifdef VBOX_WITH_GUEST_PROPS
9346 /* Guest properties (optional) */
9347
9348 /* Only load transient guest properties for configs which have saved
9349 * state, because there shouldn't be any for powered off VMs. The same
9350 * logic applies for snapshots, as offline snapshots shouldn't have
9351 * any such properties. They confuse the code in various places.
9352 * Note: can't rely on the machine state, as it isn't set yet. */
9353 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9354 /* apologies for the hacky unconst() usage, but this needs hacking
9355 * actually inconsistent settings into consistency, otherwise there
9356 * will be some corner cases where the inconsistency survives
9357 * surprisingly long without getting fixed, especially for snapshots
9358 * as there are no config changes. */
9359 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9360 for (settings::GuestPropertiesList::iterator
9361 it = llGuestProperties.begin();
9362 it != llGuestProperties.end();
9363 /*nothing*/)
9364 {
9365 const settings::GuestProperty &prop = *it;
9366 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9367 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9368 if ( fSkipTransientGuestProperties
9369 && ( fFlags & GUEST_PROP_F_TRANSIENT
9370 || fFlags & GUEST_PROP_F_TRANSRESET))
9371 {
9372 it = llGuestProperties.erase(it);
9373 continue;
9374 }
9375 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9376 mHWData->mGuestProperties[prop.strName] = property;
9377 ++it;
9378 }
9379#endif /* VBOX_WITH_GUEST_PROPS defined */
9380
9381 rc = i_loadDebugging(pDbg);
9382 if (FAILED(rc))
9383 return rc;
9384
9385 mHWData->mAutostart = *pAutostart;
9386
9387 /* default frontend */
9388 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9389 }
9390 catch (std::bad_alloc &)
9391 {
9392 return E_OUTOFMEMORY;
9393 }
9394
9395 AssertComRC(rc);
9396 return rc;
9397}
9398
9399/**
9400 * Called from i_loadHardware() to load the debugging settings of the
9401 * machine.
9402 *
9403 * @param pDbg Pointer to the settings.
9404 */
9405HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9406{
9407 mHWData->mDebugging = *pDbg;
9408 /* no more processing currently required, this will probably change. */
9409 return S_OK;
9410}
9411
9412/**
9413 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9414 *
9415 * @param data storage settings.
9416 * @param puuidRegistry media registry ID to set media to or NULL;
9417 * see Machine::i_loadMachineDataFromSettings()
9418 * @param puuidSnapshot snapshot ID
9419 * @return
9420 */
9421HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9422 const Guid *puuidRegistry,
9423 const Guid *puuidSnapshot)
9424{
9425 AssertReturn(!i_isSessionMachine(), E_FAIL);
9426
9427 HRESULT rc = S_OK;
9428
9429 for (settings::StorageControllersList::const_iterator
9430 it = data.llStorageControllers.begin();
9431 it != data.llStorageControllers.end();
9432 ++it)
9433 {
9434 const settings::StorageController &ctlData = *it;
9435
9436 ComObjPtr<StorageController> pCtl;
9437 /* Try to find one with the name first. */
9438 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9439 if (SUCCEEDED(rc))
9440 return setError(VBOX_E_OBJECT_IN_USE,
9441 tr("Storage controller named '%s' already exists"),
9442 ctlData.strName.c_str());
9443
9444 pCtl.createObject();
9445 rc = pCtl->init(this,
9446 ctlData.strName,
9447 ctlData.storageBus,
9448 ctlData.ulInstance,
9449 ctlData.fBootable);
9450 if (FAILED(rc)) return rc;
9451
9452 mStorageControllers->push_back(pCtl);
9453
9454 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9455 if (FAILED(rc)) return rc;
9456
9457 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9458 if (FAILED(rc)) return rc;
9459
9460 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9461 if (FAILED(rc)) return rc;
9462
9463 /* Load the attached devices now. */
9464 rc = i_loadStorageDevices(pCtl,
9465 ctlData,
9466 puuidRegistry,
9467 puuidSnapshot);
9468 if (FAILED(rc)) return rc;
9469 }
9470
9471 return S_OK;
9472}
9473
9474/**
9475 * Called from i_loadStorageControllers for a controller's devices.
9476 *
9477 * @param aStorageController
9478 * @param data
9479 * @param puuidRegistry media registry ID to set media to or NULL; see
9480 * Machine::i_loadMachineDataFromSettings()
9481 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9482 * @return
9483 */
9484HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9485 const settings::StorageController &data,
9486 const Guid *puuidRegistry,
9487 const Guid *puuidSnapshot)
9488{
9489 HRESULT rc = S_OK;
9490
9491 /* paranoia: detect duplicate attachments */
9492 for (settings::AttachedDevicesList::const_iterator
9493 it = data.llAttachedDevices.begin();
9494 it != data.llAttachedDevices.end();
9495 ++it)
9496 {
9497 const settings::AttachedDevice &ad = *it;
9498
9499 for (settings::AttachedDevicesList::const_iterator it2 = it;
9500 it2 != data.llAttachedDevices.end();
9501 ++it2)
9502 {
9503 if (it == it2)
9504 continue;
9505
9506 const settings::AttachedDevice &ad2 = *it2;
9507
9508 if ( ad.lPort == ad2.lPort
9509 && ad.lDevice == ad2.lDevice)
9510 {
9511 return setError(E_FAIL,
9512 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9513 aStorageController->i_getName().c_str(),
9514 ad.lPort,
9515 ad.lDevice,
9516 mUserData->s.strName.c_str());
9517 }
9518 }
9519 }
9520
9521 for (settings::AttachedDevicesList::const_iterator
9522 it = data.llAttachedDevices.begin();
9523 it != data.llAttachedDevices.end();
9524 ++it)
9525 {
9526 const settings::AttachedDevice &dev = *it;
9527 ComObjPtr<Medium> medium;
9528
9529 switch (dev.deviceType)
9530 {
9531 case DeviceType_Floppy:
9532 case DeviceType_DVD:
9533 if (dev.strHostDriveSrc.isNotEmpty())
9534 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9535 false /* fRefresh */, medium);
9536 else
9537 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9538 dev.uuid,
9539 false /* fRefresh */,
9540 false /* aSetError */,
9541 medium);
9542 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9543 // This is not an error. The host drive or UUID might have vanished, so just go
9544 // ahead without this removeable medium attachment
9545 rc = S_OK;
9546 break;
9547
9548 case DeviceType_HardDisk:
9549 {
9550 /* find a hard disk by UUID */
9551 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9552 if (FAILED(rc))
9553 {
9554 if (i_isSnapshotMachine())
9555 {
9556 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9557 // so the user knows that the bad disk is in a snapshot somewhere
9558 com::ErrorInfo info;
9559 return setError(E_FAIL,
9560 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9561 puuidSnapshot->raw(),
9562 info.getText().raw());
9563 }
9564 else
9565 return rc;
9566 }
9567
9568 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9569
9570 if (medium->i_getType() == MediumType_Immutable)
9571 {
9572 if (i_isSnapshotMachine())
9573 return setError(E_FAIL,
9574 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9575 "of the virtual machine '%s' ('%s')"),
9576 medium->i_getLocationFull().c_str(),
9577 dev.uuid.raw(),
9578 puuidSnapshot->raw(),
9579 mUserData->s.strName.c_str(),
9580 mData->m_strConfigFileFull.c_str());
9581
9582 return setError(E_FAIL,
9583 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9584 medium->i_getLocationFull().c_str(),
9585 dev.uuid.raw(),
9586 mUserData->s.strName.c_str(),
9587 mData->m_strConfigFileFull.c_str());
9588 }
9589
9590 if (medium->i_getType() == MediumType_MultiAttach)
9591 {
9592 if (i_isSnapshotMachine())
9593 return setError(E_FAIL,
9594 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9595 "of the virtual machine '%s' ('%s')"),
9596 medium->i_getLocationFull().c_str(),
9597 dev.uuid.raw(),
9598 puuidSnapshot->raw(),
9599 mUserData->s.strName.c_str(),
9600 mData->m_strConfigFileFull.c_str());
9601
9602 return setError(E_FAIL,
9603 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9604 medium->i_getLocationFull().c_str(),
9605 dev.uuid.raw(),
9606 mUserData->s.strName.c_str(),
9607 mData->m_strConfigFileFull.c_str());
9608 }
9609
9610 if ( !i_isSnapshotMachine()
9611 && medium->i_getChildren().size() != 0
9612 )
9613 return setError(E_FAIL,
9614 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9615 "because it has %d differencing child hard disks"),
9616 medium->i_getLocationFull().c_str(),
9617 dev.uuid.raw(),
9618 mUserData->s.strName.c_str(),
9619 mData->m_strConfigFileFull.c_str(),
9620 medium->i_getChildren().size());
9621
9622 if (i_findAttachment(*mMediumAttachments.data(),
9623 medium))
9624 return setError(E_FAIL,
9625 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9626 medium->i_getLocationFull().c_str(),
9627 dev.uuid.raw(),
9628 mUserData->s.strName.c_str(),
9629 mData->m_strConfigFileFull.c_str());
9630
9631 break;
9632 }
9633
9634 default:
9635 return setError(E_FAIL,
9636 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9637 medium->i_getLocationFull().c_str(),
9638 mUserData->s.strName.c_str(),
9639 mData->m_strConfigFileFull.c_str());
9640 }
9641
9642 if (FAILED(rc))
9643 break;
9644
9645 /* Bandwidth groups are loaded at this point. */
9646 ComObjPtr<BandwidthGroup> pBwGroup;
9647
9648 if (!dev.strBwGroup.isEmpty())
9649 {
9650 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9651 if (FAILED(rc))
9652 return setError(E_FAIL,
9653 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9654 medium->i_getLocationFull().c_str(),
9655 dev.strBwGroup.c_str(),
9656 mUserData->s.strName.c_str(),
9657 mData->m_strConfigFileFull.c_str());
9658 pBwGroup->i_reference();
9659 }
9660
9661 const Utf8Str controllerName = aStorageController->i_getName();
9662 ComObjPtr<MediumAttachment> pAttachment;
9663 pAttachment.createObject();
9664 rc = pAttachment->init(this,
9665 medium,
9666 controllerName,
9667 dev.lPort,
9668 dev.lDevice,
9669 dev.deviceType,
9670 false,
9671 dev.fPassThrough,
9672 dev.fTempEject,
9673 dev.fNonRotational,
9674 dev.fDiscard,
9675 dev.fHotPluggable,
9676 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9677 if (FAILED(rc)) break;
9678
9679 /* associate the medium with this machine and snapshot */
9680 if (!medium.isNull())
9681 {
9682 AutoCaller medCaller(medium);
9683 if (FAILED(medCaller.rc())) return medCaller.rc();
9684 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9685
9686 if (i_isSnapshotMachine())
9687 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9688 else
9689 rc = medium->i_addBackReference(mData->mUuid);
9690 /* If the medium->addBackReference fails it sets an appropriate
9691 * error message, so no need to do any guesswork here. */
9692
9693 if (puuidRegistry)
9694 // caller wants registry ID to be set on all attached media (OVF import case)
9695 medium->i_addRegistry(*puuidRegistry);
9696 }
9697
9698 if (FAILED(rc))
9699 break;
9700
9701 /* back up mMediumAttachments to let registeredInit() properly rollback
9702 * on failure (= limited accessibility) */
9703 i_setModified(IsModified_Storage);
9704 mMediumAttachments.backup();
9705 mMediumAttachments->push_back(pAttachment);
9706 }
9707
9708 return rc;
9709}
9710
9711/**
9712 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9713 *
9714 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9715 * @param aSnapshot where to return the found snapshot
9716 * @param aSetError true to set extended error info on failure
9717 */
9718HRESULT Machine::i_findSnapshotById(const Guid &aId,
9719 ComObjPtr<Snapshot> &aSnapshot,
9720 bool aSetError /* = false */)
9721{
9722 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9723
9724 if (!mData->mFirstSnapshot)
9725 {
9726 if (aSetError)
9727 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9728 return E_FAIL;
9729 }
9730
9731 if (aId.isZero())
9732 aSnapshot = mData->mFirstSnapshot;
9733 else
9734 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9735
9736 if (!aSnapshot)
9737 {
9738 if (aSetError)
9739 return setError(E_FAIL,
9740 tr("Could not find a snapshot with UUID {%s}"),
9741 aId.toString().c_str());
9742 return E_FAIL;
9743 }
9744
9745 return S_OK;
9746}
9747
9748/**
9749 * Returns the snapshot with the given name or fails of no such snapshot.
9750 *
9751 * @param strName snapshot name to find
9752 * @param aSnapshot where to return the found snapshot
9753 * @param aSetError true to set extended error info on failure
9754 */
9755HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9756 ComObjPtr<Snapshot> &aSnapshot,
9757 bool aSetError /* = false */)
9758{
9759 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9760
9761 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9762
9763 if (!mData->mFirstSnapshot)
9764 {
9765 if (aSetError)
9766 return setError(VBOX_E_OBJECT_NOT_FOUND,
9767 tr("This machine does not have any snapshots"));
9768 return VBOX_E_OBJECT_NOT_FOUND;
9769 }
9770
9771 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9772
9773 if (!aSnapshot)
9774 {
9775 if (aSetError)
9776 return setError(VBOX_E_OBJECT_NOT_FOUND,
9777 tr("Could not find a snapshot named '%s'"), strName.c_str());
9778 return VBOX_E_OBJECT_NOT_FOUND;
9779 }
9780
9781 return S_OK;
9782}
9783
9784/**
9785 * Returns a storage controller object with the given name.
9786 *
9787 * @param aName storage controller name to find
9788 * @param aStorageController where to return the found storage controller
9789 * @param aSetError true to set extended error info on failure
9790 */
9791HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9792 ComObjPtr<StorageController> &aStorageController,
9793 bool aSetError /* = false */)
9794{
9795 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9796
9797 for (StorageControllerList::const_iterator
9798 it = mStorageControllers->begin();
9799 it != mStorageControllers->end();
9800 ++it)
9801 {
9802 if ((*it)->i_getName() == aName)
9803 {
9804 aStorageController = (*it);
9805 return S_OK;
9806 }
9807 }
9808
9809 if (aSetError)
9810 return setError(VBOX_E_OBJECT_NOT_FOUND,
9811 tr("Could not find a storage controller named '%s'"),
9812 aName.c_str());
9813 return VBOX_E_OBJECT_NOT_FOUND;
9814}
9815
9816/**
9817 * Returns a USB controller object with the given name.
9818 *
9819 * @param aName USB controller name to find
9820 * @param aUSBController where to return the found USB controller
9821 * @param aSetError true to set extended error info on failure
9822 */
9823HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9824 ComObjPtr<USBController> &aUSBController,
9825 bool aSetError /* = false */)
9826{
9827 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9828
9829 for (USBControllerList::const_iterator
9830 it = mUSBControllers->begin();
9831 it != mUSBControllers->end();
9832 ++it)
9833 {
9834 if ((*it)->i_getName() == aName)
9835 {
9836 aUSBController = (*it);
9837 return S_OK;
9838 }
9839 }
9840
9841 if (aSetError)
9842 return setError(VBOX_E_OBJECT_NOT_FOUND,
9843 tr("Could not find a storage controller named '%s'"),
9844 aName.c_str());
9845 return VBOX_E_OBJECT_NOT_FOUND;
9846}
9847
9848/**
9849 * Returns the number of USB controller instance of the given type.
9850 *
9851 * @param enmType USB controller type.
9852 */
9853ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9854{
9855 ULONG cCtrls = 0;
9856
9857 for (USBControllerList::const_iterator
9858 it = mUSBControllers->begin();
9859 it != mUSBControllers->end();
9860 ++it)
9861 {
9862 if ((*it)->i_getControllerType() == enmType)
9863 cCtrls++;
9864 }
9865
9866 return cCtrls;
9867}
9868
9869HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9870 MediumAttachmentList &atts)
9871{
9872 AutoCaller autoCaller(this);
9873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9874
9875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9876
9877 for (MediumAttachmentList::const_iterator
9878 it = mMediumAttachments->begin();
9879 it != mMediumAttachments->end();
9880 ++it)
9881 {
9882 const ComObjPtr<MediumAttachment> &pAtt = *it;
9883 // should never happen, but deal with NULL pointers in the list.
9884 AssertContinue(!pAtt.isNull());
9885
9886 // getControllerName() needs caller+read lock
9887 AutoCaller autoAttCaller(pAtt);
9888 if (FAILED(autoAttCaller.rc()))
9889 {
9890 atts.clear();
9891 return autoAttCaller.rc();
9892 }
9893 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9894
9895 if (pAtt->i_getControllerName() == aName)
9896 atts.push_back(pAtt);
9897 }
9898
9899 return S_OK;
9900}
9901
9902
9903/**
9904 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9905 * file if the machine name was changed and about creating a new settings file
9906 * if this is a new machine.
9907 *
9908 * @note Must be never called directly but only from #saveSettings().
9909 */
9910HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9911{
9912 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9913
9914 HRESULT rc = S_OK;
9915
9916 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9917
9918 /// @todo need to handle primary group change, too
9919
9920 /* attempt to rename the settings file if machine name is changed */
9921 if ( mUserData->s.fNameSync
9922 && mUserData.isBackedUp()
9923 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9924 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9925 )
9926 {
9927 bool dirRenamed = false;
9928 bool fileRenamed = false;
9929
9930 Utf8Str configFile, newConfigFile;
9931 Utf8Str configFilePrev, newConfigFilePrev;
9932 Utf8Str configDir, newConfigDir;
9933
9934 do
9935 {
9936 int vrc = VINF_SUCCESS;
9937
9938 Utf8Str name = mUserData.backedUpData()->s.strName;
9939 Utf8Str newName = mUserData->s.strName;
9940 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9941 if (group == "/")
9942 group.setNull();
9943 Utf8Str newGroup = mUserData->s.llGroups.front();
9944 if (newGroup == "/")
9945 newGroup.setNull();
9946
9947 configFile = mData->m_strConfigFileFull;
9948
9949 /* first, rename the directory if it matches the group and machine name */
9950 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9951 group.c_str(), RTPATH_DELIMITER, name.c_str());
9952 /** @todo hack, make somehow use of ComposeMachineFilename */
9953 if (mUserData->s.fDirectoryIncludesUUID)
9954 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9955 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9956 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9957 /** @todo hack, make somehow use of ComposeMachineFilename */
9958 if (mUserData->s.fDirectoryIncludesUUID)
9959 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9960 configDir = configFile;
9961 configDir.stripFilename();
9962 newConfigDir = configDir;
9963 if ( configDir.length() >= groupPlusName.length()
9964 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9965 groupPlusName.c_str()))
9966 {
9967 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9968 Utf8Str newConfigBaseDir(newConfigDir);
9969 newConfigDir.append(newGroupPlusName);
9970 /* consistency: use \ if appropriate on the platform */
9971 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9972 /* new dir and old dir cannot be equal here because of 'if'
9973 * above and because name != newName */
9974 Assert(configDir != newConfigDir);
9975 if (!fSettingsFileIsNew)
9976 {
9977 /* perform real rename only if the machine is not new */
9978 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9979 if ( vrc == VERR_FILE_NOT_FOUND
9980 || vrc == VERR_PATH_NOT_FOUND)
9981 {
9982 /* create the parent directory, then retry renaming */
9983 Utf8Str parent(newConfigDir);
9984 parent.stripFilename();
9985 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9986 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9987 }
9988 if (RT_FAILURE(vrc))
9989 {
9990 rc = setErrorBoth(E_FAIL, vrc,
9991 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9992 configDir.c_str(),
9993 newConfigDir.c_str(),
9994 vrc);
9995 break;
9996 }
9997 /* delete subdirectories which are no longer needed */
9998 Utf8Str dir(configDir);
9999 dir.stripFilename();
10000 while (dir != newConfigBaseDir && dir != ".")
10001 {
10002 vrc = RTDirRemove(dir.c_str());
10003 if (RT_FAILURE(vrc))
10004 break;
10005 dir.stripFilename();
10006 }
10007 dirRenamed = true;
10008 }
10009 }
10010
10011 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10012 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10013
10014 /* then try to rename the settings file itself */
10015 if (newConfigFile != configFile)
10016 {
10017 /* get the path to old settings file in renamed directory */
10018 configFile = Utf8StrFmt("%s%c%s",
10019 newConfigDir.c_str(),
10020 RTPATH_DELIMITER,
10021 RTPathFilename(configFile.c_str()));
10022 if (!fSettingsFileIsNew)
10023 {
10024 /* perform real rename only if the machine is not new */
10025 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10026 if (RT_FAILURE(vrc))
10027 {
10028 rc = setErrorBoth(E_FAIL, vrc,
10029 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10030 configFile.c_str(),
10031 newConfigFile.c_str(),
10032 vrc);
10033 break;
10034 }
10035 fileRenamed = true;
10036 configFilePrev = configFile;
10037 configFilePrev += "-prev";
10038 newConfigFilePrev = newConfigFile;
10039 newConfigFilePrev += "-prev";
10040 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10041 }
10042 }
10043
10044 // update m_strConfigFileFull amd mConfigFile
10045 mData->m_strConfigFileFull = newConfigFile;
10046 // compute the relative path too
10047 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10048
10049 // store the old and new so that VirtualBox::i_saveSettings() can update
10050 // the media registry
10051 if ( mData->mRegistered
10052 && (configDir != newConfigDir || configFile != newConfigFile))
10053 {
10054 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10055
10056 if (pfNeedsGlobalSaveSettings)
10057 *pfNeedsGlobalSaveSettings = true;
10058 }
10059
10060 // in the saved state file path, replace the old directory with the new directory
10061 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10062 {
10063 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10064 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10065 }
10066
10067 // and do the same thing for the saved state file paths of all the online snapshots
10068 if (mData->mFirstSnapshot)
10069 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10070 newConfigDir.c_str());
10071 }
10072 while (0);
10073
10074 if (FAILED(rc))
10075 {
10076 /* silently try to rename everything back */
10077 if (fileRenamed)
10078 {
10079 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10080 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10081 }
10082 if (dirRenamed)
10083 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10084 }
10085
10086 if (FAILED(rc)) return rc;
10087 }
10088
10089 if (fSettingsFileIsNew)
10090 {
10091 /* create a virgin config file */
10092 int vrc = VINF_SUCCESS;
10093
10094 /* ensure the settings directory exists */
10095 Utf8Str path(mData->m_strConfigFileFull);
10096 path.stripFilename();
10097 if (!RTDirExists(path.c_str()))
10098 {
10099 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10100 if (RT_FAILURE(vrc))
10101 {
10102 return setErrorBoth(E_FAIL, vrc,
10103 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10104 path.c_str(),
10105 vrc);
10106 }
10107 }
10108
10109 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10110 path = Utf8Str(mData->m_strConfigFileFull);
10111 RTFILE f = NIL_RTFILE;
10112 vrc = RTFileOpen(&f, path.c_str(),
10113 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10114 if (RT_FAILURE(vrc))
10115 return setErrorBoth(E_FAIL, vrc,
10116 tr("Could not create the settings file '%s' (%Rrc)"),
10117 path.c_str(),
10118 vrc);
10119 RTFileClose(f);
10120 }
10121
10122 return rc;
10123}
10124
10125/**
10126 * Saves and commits machine data, user data and hardware data.
10127 *
10128 * Note that on failure, the data remains uncommitted.
10129 *
10130 * @a aFlags may combine the following flags:
10131 *
10132 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10133 * Used when saving settings after an operation that makes them 100%
10134 * correspond to the settings from the current snapshot.
10135 * - SaveS_Force: settings will be saved without doing a deep compare of the
10136 * settings structures. This is used when this is called because snapshots
10137 * have changed to avoid the overhead of the deep compare.
10138 *
10139 * @note Must be called from under this object's write lock. Locks children for
10140 * writing.
10141 *
10142 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10143 * initialized to false and that will be set to true by this function if
10144 * the caller must invoke VirtualBox::i_saveSettings() because the global
10145 * settings have changed. This will happen if a machine rename has been
10146 * saved and the global machine and media registries will therefore need
10147 * updating.
10148 * @param aFlags Flags.
10149 */
10150HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10151 int aFlags /*= 0*/)
10152{
10153 LogFlowThisFuncEnter();
10154
10155 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10156
10157 /* make sure child objects are unable to modify the settings while we are
10158 * saving them */
10159 i_ensureNoStateDependencies();
10160
10161 AssertReturn(!i_isSnapshotMachine(),
10162 E_FAIL);
10163
10164 HRESULT rc = S_OK;
10165 bool fNeedsWrite = false;
10166
10167 /* First, prepare to save settings. It will care about renaming the
10168 * settings directory and file if the machine name was changed and about
10169 * creating a new settings file if this is a new machine. */
10170 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10171 if (FAILED(rc)) return rc;
10172
10173 // keep a pointer to the current settings structures
10174 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10175 settings::MachineConfigFile *pNewConfig = NULL;
10176
10177 try
10178 {
10179 // make a fresh one to have everyone write stuff into
10180 pNewConfig = new settings::MachineConfigFile(NULL);
10181 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10182
10183 // now go and copy all the settings data from COM to the settings structures
10184 // (this calls i_saveSettings() on all the COM objects in the machine)
10185 i_copyMachineDataToSettings(*pNewConfig);
10186
10187 if (aFlags & SaveS_ResetCurStateModified)
10188 {
10189 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10190 mData->mCurrentStateModified = FALSE;
10191 fNeedsWrite = true; // always, no need to compare
10192 }
10193 else if (aFlags & SaveS_Force)
10194 {
10195 fNeedsWrite = true; // always, no need to compare
10196 }
10197 else
10198 {
10199 if (!mData->mCurrentStateModified)
10200 {
10201 // do a deep compare of the settings that we just saved with the settings
10202 // previously stored in the config file; this invokes MachineConfigFile::operator==
10203 // which does a deep compare of all the settings, which is expensive but less expensive
10204 // than writing out XML in vain
10205 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10206
10207 // could still be modified if any settings changed
10208 mData->mCurrentStateModified = fAnySettingsChanged;
10209
10210 fNeedsWrite = fAnySettingsChanged;
10211 }
10212 else
10213 fNeedsWrite = true;
10214 }
10215
10216 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10217
10218 if (fNeedsWrite)
10219 // now spit it all out!
10220 pNewConfig->write(mData->m_strConfigFileFull);
10221
10222 mData->pMachineConfigFile = pNewConfig;
10223 delete pOldConfig;
10224 i_commit();
10225
10226 // after saving settings, we are no longer different from the XML on disk
10227 mData->flModifications = 0;
10228 }
10229 catch (HRESULT err)
10230 {
10231 // we assume that error info is set by the thrower
10232 rc = err;
10233
10234 // restore old config
10235 delete pNewConfig;
10236 mData->pMachineConfigFile = pOldConfig;
10237 }
10238 catch (...)
10239 {
10240 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10241 }
10242
10243 if (fNeedsWrite)
10244 {
10245 /* Fire the data change event, even on failure (since we've already
10246 * committed all data). This is done only for SessionMachines because
10247 * mutable Machine instances are always not registered (i.e. private
10248 * to the client process that creates them) and thus don't need to
10249 * inform callbacks. */
10250 if (i_isSessionMachine())
10251 mParent->i_onMachineDataChange(mData->mUuid);
10252 }
10253
10254 LogFlowThisFunc(("rc=%08X\n", rc));
10255 LogFlowThisFuncLeave();
10256 return rc;
10257}
10258
10259/**
10260 * Implementation for saving the machine settings into the given
10261 * settings::MachineConfigFile instance. This copies machine extradata
10262 * from the previous machine config file in the instance data, if any.
10263 *
10264 * This gets called from two locations:
10265 *
10266 * -- Machine::i_saveSettings(), during the regular XML writing;
10267 *
10268 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10269 * exported to OVF and we write the VirtualBox proprietary XML
10270 * into a <vbox:Machine> tag.
10271 *
10272 * This routine fills all the fields in there, including snapshots, *except*
10273 * for the following:
10274 *
10275 * -- fCurrentStateModified. There is some special logic associated with that.
10276 *
10277 * The caller can then call MachineConfigFile::write() or do something else
10278 * with it.
10279 *
10280 * Caller must hold the machine lock!
10281 *
10282 * This throws XML errors and HRESULT, so the caller must have a catch block!
10283 */
10284void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10285{
10286 // deep copy extradata, being extra careful with self assignment (the STL
10287 // map assignment on Mac OS X clang based Xcode isn't checking)
10288 if (&config != mData->pMachineConfigFile)
10289 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10290
10291 config.uuid = mData->mUuid;
10292
10293 // copy name, description, OS type, teleport, UTC etc.
10294 config.machineUserData = mUserData->s;
10295
10296 if ( mData->mMachineState == MachineState_Saved
10297 || mData->mMachineState == MachineState_Restoring
10298 // when doing certain snapshot operations we may or may not have
10299 // a saved state in the current state, so keep everything as is
10300 || ( ( mData->mMachineState == MachineState_Snapshotting
10301 || mData->mMachineState == MachineState_DeletingSnapshot
10302 || mData->mMachineState == MachineState_RestoringSnapshot)
10303 && (!mSSData->strStateFilePath.isEmpty())
10304 )
10305 )
10306 {
10307 Assert(!mSSData->strStateFilePath.isEmpty());
10308 /* try to make the file name relative to the settings file dir */
10309 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10310 }
10311 else
10312 {
10313 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10314 config.strStateFile.setNull();
10315 }
10316
10317 if (mData->mCurrentSnapshot)
10318 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10319 else
10320 config.uuidCurrentSnapshot.clear();
10321
10322 config.timeLastStateChange = mData->mLastStateChange;
10323 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10324 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10325
10326 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10327 if (FAILED(rc)) throw rc;
10328
10329 // save machine's media registry if this is VirtualBox 4.0 or later
10330 if (config.canHaveOwnMediaRegistry())
10331 {
10332 // determine machine folder
10333 Utf8Str strMachineFolder = i_getSettingsFileFull();
10334 strMachineFolder.stripFilename();
10335 mParent->i_saveMediaRegistry(config.mediaRegistry,
10336 i_getId(), // only media with registry ID == machine UUID
10337 strMachineFolder);
10338 // this throws HRESULT
10339 }
10340
10341 // save snapshots
10342 rc = i_saveAllSnapshots(config);
10343 if (FAILED(rc)) throw rc;
10344}
10345
10346/**
10347 * Saves all snapshots of the machine into the given machine config file. Called
10348 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10349 * @param config
10350 * @return
10351 */
10352HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10353{
10354 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10355
10356 HRESULT rc = S_OK;
10357
10358 try
10359 {
10360 config.llFirstSnapshot.clear();
10361
10362 if (mData->mFirstSnapshot)
10363 {
10364 // the settings use a list for "the first snapshot"
10365 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10366
10367 // get reference to the snapshot on the list and work on that
10368 // element straight in the list to avoid excessive copying later
10369 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10370 if (FAILED(rc)) throw rc;
10371 }
10372
10373// if (mType == IsSessionMachine)
10374// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10375
10376 }
10377 catch (HRESULT err)
10378 {
10379 /* we assume that error info is set by the thrower */
10380 rc = err;
10381 }
10382 catch (...)
10383 {
10384 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10385 }
10386
10387 return rc;
10388}
10389
10390/**
10391 * Saves the VM hardware configuration. It is assumed that the
10392 * given node is empty.
10393 *
10394 * @param data Reference to the settings object for the hardware config.
10395 * @param pDbg Pointer to the settings object for the debugging config
10396 * which happens to live in mHWData.
10397 * @param pAutostart Pointer to the settings object for the autostart config
10398 * which happens to live in mHWData.
10399 */
10400HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10401 settings::Autostart *pAutostart)
10402{
10403 HRESULT rc = S_OK;
10404
10405 try
10406 {
10407 /* The hardware version attribute (optional).
10408 Automatically upgrade from 1 to current default hardware version
10409 when there is no saved state. (ugly!) */
10410 if ( mHWData->mHWVersion == "1"
10411 && mSSData->strStateFilePath.isEmpty()
10412 )
10413 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10414
10415 data.strVersion = mHWData->mHWVersion;
10416 data.uuid = mHWData->mHardwareUUID;
10417
10418 // CPU
10419 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10420 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10421 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10422 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10423 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10424 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10425 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10426 data.fPAE = !!mHWData->mPAEEnabled;
10427 data.enmLongMode = mHWData->mLongMode;
10428 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10429 data.fAPIC = !!mHWData->mAPIC;
10430 data.fX2APIC = !!mHWData->mX2APIC;
10431 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10432 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10433 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10434 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10435 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10436 data.cCPUs = mHWData->mCPUCount;
10437 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10438 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10439 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10440 data.strCpuProfile = mHWData->mCpuProfile;
10441
10442 data.llCpus.clear();
10443 if (data.fCpuHotPlug)
10444 {
10445 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10446 {
10447 if (mHWData->mCPUAttached[idx])
10448 {
10449 settings::Cpu cpu;
10450 cpu.ulId = idx;
10451 data.llCpus.push_back(cpu);
10452 }
10453 }
10454 }
10455
10456 /* Standard and Extended CPUID leafs. */
10457 data.llCpuIdLeafs.clear();
10458 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10459
10460 // memory
10461 data.ulMemorySizeMB = mHWData->mMemorySize;
10462 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10463
10464 // firmware
10465 data.firmwareType = mHWData->mFirmwareType;
10466
10467 // HID
10468 data.pointingHIDType = mHWData->mPointingHIDType;
10469 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10470
10471 // chipset
10472 data.chipsetType = mHWData->mChipsetType;
10473
10474 // paravirt
10475 data.paravirtProvider = mHWData->mParavirtProvider;
10476 data.strParavirtDebug = mHWData->mParavirtDebug;
10477
10478 // emulated USB card reader
10479 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10480
10481 // HPET
10482 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10483
10484 // boot order
10485 data.mapBootOrder.clear();
10486 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10487 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10488
10489 // display
10490 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10491 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10492 data.cMonitors = mHWData->mMonitorCount;
10493 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10494 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10495 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10496 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10497 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10498 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10499 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10500 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10501 {
10502 if (mHWData->maVideoCaptureScreens[i])
10503 ASMBitSet(&data.u64VideoCaptureScreens, i);
10504 else
10505 ASMBitClear(&data.u64VideoCaptureScreens, i);
10506 }
10507 /* store relative video capture file if possible */
10508 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10509 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10510
10511 /* VRDEServer settings (optional) */
10512 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10513 if (FAILED(rc)) throw rc;
10514
10515 /* BIOS (required) */
10516 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10517 if (FAILED(rc)) throw rc;
10518
10519 /* USB Controller (required) */
10520 data.usbSettings.llUSBControllers.clear();
10521 for (USBControllerList::const_iterator
10522 it = mUSBControllers->begin();
10523 it != mUSBControllers->end();
10524 ++it)
10525 {
10526 ComObjPtr<USBController> ctrl = *it;
10527 settings::USBController settingsCtrl;
10528
10529 settingsCtrl.strName = ctrl->i_getName();
10530 settingsCtrl.enmType = ctrl->i_getControllerType();
10531
10532 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10533 }
10534
10535 /* USB device filters (required) */
10536 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10537 if (FAILED(rc)) throw rc;
10538
10539 /* Network adapters (required) */
10540 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10541 data.llNetworkAdapters.clear();
10542 /* Write out only the nominal number of network adapters for this
10543 * chipset type. Since Machine::commit() hasn't been called there
10544 * may be extra NIC settings in the vector. */
10545 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10546 {
10547 settings::NetworkAdapter nic;
10548 nic.ulSlot = (uint32_t)slot;
10549 /* paranoia check... must not be NULL, but must not crash either. */
10550 if (mNetworkAdapters[slot])
10551 {
10552 if (mNetworkAdapters[slot]->i_hasDefaults())
10553 continue;
10554
10555 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10556 if (FAILED(rc)) throw rc;
10557
10558 data.llNetworkAdapters.push_back(nic);
10559 }
10560 }
10561
10562 /* Serial ports */
10563 data.llSerialPorts.clear();
10564 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10565 {
10566 if (mSerialPorts[slot]->i_hasDefaults())
10567 continue;
10568
10569 settings::SerialPort s;
10570 s.ulSlot = slot;
10571 rc = mSerialPorts[slot]->i_saveSettings(s);
10572 if (FAILED(rc)) return rc;
10573
10574 data.llSerialPorts.push_back(s);
10575 }
10576
10577 /* Parallel ports */
10578 data.llParallelPorts.clear();
10579 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10580 {
10581 if (mParallelPorts[slot]->i_hasDefaults())
10582 continue;
10583
10584 settings::ParallelPort p;
10585 p.ulSlot = slot;
10586 rc = mParallelPorts[slot]->i_saveSettings(p);
10587 if (FAILED(rc)) return rc;
10588
10589 data.llParallelPorts.push_back(p);
10590 }
10591
10592 /* Audio adapter */
10593 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10594 if (FAILED(rc)) return rc;
10595
10596 rc = i_saveStorageControllers(data.storage);
10597 if (FAILED(rc)) return rc;
10598
10599 /* Shared folders */
10600 data.llSharedFolders.clear();
10601 for (HWData::SharedFolderList::const_iterator
10602 it = mHWData->mSharedFolders.begin();
10603 it != mHWData->mSharedFolders.end();
10604 ++it)
10605 {
10606 SharedFolder *pSF = *it;
10607 AutoCaller sfCaller(pSF);
10608 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10609 settings::SharedFolder sf;
10610 sf.strName = pSF->i_getName();
10611 sf.strHostPath = pSF->i_getHostPath();
10612 sf.fWritable = !!pSF->i_isWritable();
10613 sf.fAutoMount = !!pSF->i_isAutoMounted();
10614
10615 data.llSharedFolders.push_back(sf);
10616 }
10617
10618 // clipboard
10619 data.clipboardMode = mHWData->mClipboardMode;
10620
10621 // drag'n'drop
10622 data.dndMode = mHWData->mDnDMode;
10623
10624 /* Guest */
10625 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10626
10627 // IO settings
10628 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10629 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10630
10631 /* BandwidthControl (required) */
10632 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10633 if (FAILED(rc)) throw rc;
10634
10635 /* Host PCI devices */
10636 data.pciAttachments.clear();
10637 for (HWData::PCIDeviceAssignmentList::const_iterator
10638 it = mHWData->mPCIDeviceAssignments.begin();
10639 it != mHWData->mPCIDeviceAssignments.end();
10640 ++it)
10641 {
10642 ComObjPtr<PCIDeviceAttachment> pda = *it;
10643 settings::HostPCIDeviceAttachment hpda;
10644
10645 rc = pda->i_saveSettings(hpda);
10646 if (FAILED(rc)) throw rc;
10647
10648 data.pciAttachments.push_back(hpda);
10649 }
10650
10651 // guest properties
10652 data.llGuestProperties.clear();
10653#ifdef VBOX_WITH_GUEST_PROPS
10654 for (HWData::GuestPropertyMap::const_iterator
10655 it = mHWData->mGuestProperties.begin();
10656 it != mHWData->mGuestProperties.end();
10657 ++it)
10658 {
10659 HWData::GuestProperty property = it->second;
10660
10661 /* Remove transient guest properties at shutdown unless we
10662 * are saving state. Note that restoring snapshot intentionally
10663 * keeps them, they will be removed if appropriate once the final
10664 * machine state is set (as crashes etc. need to work). */
10665 if ( ( mData->mMachineState == MachineState_PoweredOff
10666 || mData->mMachineState == MachineState_Aborted
10667 || mData->mMachineState == MachineState_Teleported)
10668 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10669 continue;
10670 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10671 prop.strName = it->first;
10672 prop.strValue = property.strValue;
10673 prop.timestamp = property.mTimestamp;
10674 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10675 GuestPropWriteFlags(property.mFlags, szFlags);
10676 prop.strFlags = szFlags;
10677
10678 data.llGuestProperties.push_back(prop);
10679 }
10680
10681 /* I presume this doesn't require a backup(). */
10682 mData->mGuestPropertiesModified = FALSE;
10683#endif /* VBOX_WITH_GUEST_PROPS defined */
10684
10685 *pDbg = mHWData->mDebugging;
10686 *pAutostart = mHWData->mAutostart;
10687
10688 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10689 }
10690 catch (std::bad_alloc &)
10691 {
10692 return E_OUTOFMEMORY;
10693 }
10694
10695 AssertComRC(rc);
10696 return rc;
10697}
10698
10699/**
10700 * Saves the storage controller configuration.
10701 *
10702 * @param data storage settings.
10703 */
10704HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10705{
10706 data.llStorageControllers.clear();
10707
10708 for (StorageControllerList::const_iterator
10709 it = mStorageControllers->begin();
10710 it != mStorageControllers->end();
10711 ++it)
10712 {
10713 HRESULT rc;
10714 ComObjPtr<StorageController> pCtl = *it;
10715
10716 settings::StorageController ctl;
10717 ctl.strName = pCtl->i_getName();
10718 ctl.controllerType = pCtl->i_getControllerType();
10719 ctl.storageBus = pCtl->i_getStorageBus();
10720 ctl.ulInstance = pCtl->i_getInstance();
10721 ctl.fBootable = pCtl->i_getBootable();
10722
10723 /* Save the port count. */
10724 ULONG portCount;
10725 rc = pCtl->COMGETTER(PortCount)(&portCount);
10726 ComAssertComRCRet(rc, rc);
10727 ctl.ulPortCount = portCount;
10728
10729 /* Save fUseHostIOCache */
10730 BOOL fUseHostIOCache;
10731 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10732 ComAssertComRCRet(rc, rc);
10733 ctl.fUseHostIOCache = !!fUseHostIOCache;
10734
10735 /* save the devices now. */
10736 rc = i_saveStorageDevices(pCtl, ctl);
10737 ComAssertComRCRet(rc, rc);
10738
10739 data.llStorageControllers.push_back(ctl);
10740 }
10741
10742 return S_OK;
10743}
10744
10745/**
10746 * Saves the hard disk configuration.
10747 */
10748HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10749 settings::StorageController &data)
10750{
10751 MediumAttachmentList atts;
10752
10753 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10754 if (FAILED(rc)) return rc;
10755
10756 data.llAttachedDevices.clear();
10757 for (MediumAttachmentList::const_iterator
10758 it = atts.begin();
10759 it != atts.end();
10760 ++it)
10761 {
10762 settings::AttachedDevice dev;
10763 IMediumAttachment *iA = *it;
10764 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10765 Medium *pMedium = pAttach->i_getMedium();
10766
10767 dev.deviceType = pAttach->i_getType();
10768 dev.lPort = pAttach->i_getPort();
10769 dev.lDevice = pAttach->i_getDevice();
10770 dev.fPassThrough = pAttach->i_getPassthrough();
10771 dev.fHotPluggable = pAttach->i_getHotPluggable();
10772 if (pMedium)
10773 {
10774 if (pMedium->i_isHostDrive())
10775 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10776 else
10777 dev.uuid = pMedium->i_getId();
10778 dev.fTempEject = pAttach->i_getTempEject();
10779 dev.fNonRotational = pAttach->i_getNonRotational();
10780 dev.fDiscard = pAttach->i_getDiscard();
10781 }
10782
10783 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10784
10785 data.llAttachedDevices.push_back(dev);
10786 }
10787
10788 return S_OK;
10789}
10790
10791/**
10792 * Saves machine state settings as defined by aFlags
10793 * (SaveSTS_* values).
10794 *
10795 * @param aFlags Combination of SaveSTS_* flags.
10796 *
10797 * @note Locks objects for writing.
10798 */
10799HRESULT Machine::i_saveStateSettings(int aFlags)
10800{
10801 if (aFlags == 0)
10802 return S_OK;
10803
10804 AutoCaller autoCaller(this);
10805 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10806
10807 /* This object's write lock is also necessary to serialize file access
10808 * (prevent concurrent reads and writes) */
10809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10810
10811 HRESULT rc = S_OK;
10812
10813 Assert(mData->pMachineConfigFile);
10814
10815 try
10816 {
10817 if (aFlags & SaveSTS_CurStateModified)
10818 mData->pMachineConfigFile->fCurrentStateModified = true;
10819
10820 if (aFlags & SaveSTS_StateFilePath)
10821 {
10822 if (!mSSData->strStateFilePath.isEmpty())
10823 /* try to make the file name relative to the settings file dir */
10824 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10825 else
10826 mData->pMachineConfigFile->strStateFile.setNull();
10827 }
10828
10829 if (aFlags & SaveSTS_StateTimeStamp)
10830 {
10831 Assert( mData->mMachineState != MachineState_Aborted
10832 || mSSData->strStateFilePath.isEmpty());
10833
10834 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10835
10836 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10837/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10838 }
10839
10840 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10841 }
10842 catch (...)
10843 {
10844 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10845 }
10846
10847 return rc;
10848}
10849
10850/**
10851 * Ensures that the given medium is added to a media registry. If this machine
10852 * was created with 4.0 or later, then the machine registry is used. Otherwise
10853 * the global VirtualBox media registry is used.
10854 *
10855 * Caller must NOT hold machine lock, media tree or any medium locks!
10856 *
10857 * @param pMedium
10858 */
10859void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10860{
10861 /* Paranoia checks: do not hold machine or media tree locks. */
10862 AssertReturnVoid(!isWriteLockOnCurrentThread());
10863 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10864
10865 ComObjPtr<Medium> pBase;
10866 {
10867 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10868 pBase = pMedium->i_getBase();
10869 }
10870
10871 /* Paranoia checks: do not hold medium locks. */
10872 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10873 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10874
10875 // decide which medium registry to use now that the medium is attached:
10876 Guid uuid;
10877 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10878 if (fCanHaveOwnMediaRegistry)
10879 // machine XML is VirtualBox 4.0 or higher:
10880 uuid = i_getId(); // machine UUID
10881 else
10882 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10883
10884 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10885 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10886 if (pMedium->i_addRegistry(uuid))
10887 mParent->i_markRegistryModified(uuid);
10888
10889 /* For more complex hard disk structures it can happen that the base
10890 * medium isn't yet associated with any medium registry. Do that now. */
10891 if (pMedium != pBase)
10892 {
10893 /* Tree lock needed by Medium::addRegistry when recursing. */
10894 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10895 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10896 {
10897 treeLock.release();
10898 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10899 treeLock.acquire();
10900 }
10901 if (pBase->i_addRegistryRecursive(uuid))
10902 {
10903 treeLock.release();
10904 mParent->i_markRegistryModified(uuid);
10905 }
10906 }
10907}
10908
10909/**
10910 * Creates differencing hard disks for all normal hard disks attached to this
10911 * machine and a new set of attachments to refer to created disks.
10912 *
10913 * Used when taking a snapshot or when deleting the current state. Gets called
10914 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10915 *
10916 * This method assumes that mMediumAttachments contains the original hard disk
10917 * attachments it needs to create diffs for. On success, these attachments will
10918 * be replaced with the created diffs.
10919 *
10920 * Attachments with non-normal hard disks are left as is.
10921 *
10922 * If @a aOnline is @c false then the original hard disks that require implicit
10923 * diffs will be locked for reading. Otherwise it is assumed that they are
10924 * already locked for writing (when the VM was started). Note that in the latter
10925 * case it is responsibility of the caller to lock the newly created diffs for
10926 * writing if this method succeeds.
10927 *
10928 * @param aProgress Progress object to run (must contain at least as
10929 * many operations left as the number of hard disks
10930 * attached).
10931 * @param aWeight Weight of this operation.
10932 * @param aOnline Whether the VM was online prior to this operation.
10933 *
10934 * @note The progress object is not marked as completed, neither on success nor
10935 * on failure. This is a responsibility of the caller.
10936 *
10937 * @note Locks this object and the media tree for writing.
10938 */
10939HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10940 ULONG aWeight,
10941 bool aOnline)
10942{
10943 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10944
10945 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10946 AssertReturn(!!pProgressControl, E_INVALIDARG);
10947
10948 AutoCaller autoCaller(this);
10949 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10950
10951 AutoMultiWriteLock2 alock(this->lockHandle(),
10952 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10953
10954 /* must be in a protective state because we release the lock below */
10955 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10956 || mData->mMachineState == MachineState_OnlineSnapshotting
10957 || mData->mMachineState == MachineState_LiveSnapshotting
10958 || mData->mMachineState == MachineState_RestoringSnapshot
10959 || mData->mMachineState == MachineState_DeletingSnapshot
10960 , E_FAIL);
10961
10962 HRESULT rc = S_OK;
10963
10964 // use appropriate locked media map (online or offline)
10965 MediumLockListMap lockedMediaOffline;
10966 MediumLockListMap *lockedMediaMap;
10967 if (aOnline)
10968 lockedMediaMap = &mData->mSession.mLockedMedia;
10969 else
10970 lockedMediaMap = &lockedMediaOffline;
10971
10972 try
10973 {
10974 if (!aOnline)
10975 {
10976 /* lock all attached hard disks early to detect "in use"
10977 * situations before creating actual diffs */
10978 for (MediumAttachmentList::const_iterator
10979 it = mMediumAttachments->begin();
10980 it != mMediumAttachments->end();
10981 ++it)
10982 {
10983 MediumAttachment *pAtt = *it;
10984 if (pAtt->i_getType() == DeviceType_HardDisk)
10985 {
10986 Medium *pMedium = pAtt->i_getMedium();
10987 Assert(pMedium);
10988
10989 MediumLockList *pMediumLockList(new MediumLockList());
10990 alock.release();
10991 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10992 NULL /* pToLockWrite */,
10993 false /* fMediumLockWriteAll */,
10994 NULL,
10995 *pMediumLockList);
10996 alock.acquire();
10997 if (FAILED(rc))
10998 {
10999 delete pMediumLockList;
11000 throw rc;
11001 }
11002 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11003 if (FAILED(rc))
11004 {
11005 throw setError(rc,
11006 tr("Collecting locking information for all attached media failed"));
11007 }
11008 }
11009 }
11010
11011 /* Now lock all media. If this fails, nothing is locked. */
11012 alock.release();
11013 rc = lockedMediaMap->Lock();
11014 alock.acquire();
11015 if (FAILED(rc))
11016 {
11017 throw setError(rc,
11018 tr("Locking of attached media failed"));
11019 }
11020 }
11021
11022 /* remember the current list (note that we don't use backup() since
11023 * mMediumAttachments may be already backed up) */
11024 MediumAttachmentList atts = *mMediumAttachments.data();
11025
11026 /* start from scratch */
11027 mMediumAttachments->clear();
11028
11029 /* go through remembered attachments and create diffs for normal hard
11030 * disks and attach them */
11031 for (MediumAttachmentList::const_iterator
11032 it = atts.begin();
11033 it != atts.end();
11034 ++it)
11035 {
11036 MediumAttachment *pAtt = *it;
11037
11038 DeviceType_T devType = pAtt->i_getType();
11039 Medium *pMedium = pAtt->i_getMedium();
11040
11041 if ( devType != DeviceType_HardDisk
11042 || pMedium == NULL
11043 || pMedium->i_getType() != MediumType_Normal)
11044 {
11045 /* copy the attachment as is */
11046
11047 /** @todo the progress object created in SessionMachine::TakeSnaphot
11048 * only expects operations for hard disks. Later other
11049 * device types need to show up in the progress as well. */
11050 if (devType == DeviceType_HardDisk)
11051 {
11052 if (pMedium == NULL)
11053 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11054 aWeight); // weight
11055 else
11056 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11057 pMedium->i_getBase()->i_getName().c_str()).raw(),
11058 aWeight); // weight
11059 }
11060
11061 mMediumAttachments->push_back(pAtt);
11062 continue;
11063 }
11064
11065 /* need a diff */
11066 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11067 pMedium->i_getBase()->i_getName().c_str()).raw(),
11068 aWeight); // weight
11069
11070 Utf8Str strFullSnapshotFolder;
11071 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11072
11073 ComObjPtr<Medium> diff;
11074 diff.createObject();
11075 // store the diff in the same registry as the parent
11076 // (this cannot fail here because we can't create implicit diffs for
11077 // unregistered images)
11078 Guid uuidRegistryParent;
11079 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11080 Assert(fInRegistry); NOREF(fInRegistry);
11081 rc = diff->init(mParent,
11082 pMedium->i_getPreferredDiffFormat(),
11083 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11084 uuidRegistryParent,
11085 DeviceType_HardDisk);
11086 if (FAILED(rc)) throw rc;
11087
11088 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11089 * the push_back? Looks like we're going to release medium with the
11090 * wrong kind of lock (general issue with if we fail anywhere at all)
11091 * and an orphaned VDI in the snapshots folder. */
11092
11093 /* update the appropriate lock list */
11094 MediumLockList *pMediumLockList;
11095 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11096 AssertComRCThrowRC(rc);
11097 if (aOnline)
11098 {
11099 alock.release();
11100 /* The currently attached medium will be read-only, change
11101 * the lock type to read. */
11102 rc = pMediumLockList->Update(pMedium, false);
11103 alock.acquire();
11104 AssertComRCThrowRC(rc);
11105 }
11106
11107 /* release the locks before the potentially lengthy operation */
11108 alock.release();
11109 rc = pMedium->i_createDiffStorage(diff,
11110 pMedium->i_getPreferredDiffVariant(),
11111 pMediumLockList,
11112 NULL /* aProgress */,
11113 true /* aWait */);
11114 alock.acquire();
11115 if (FAILED(rc)) throw rc;
11116
11117 /* actual lock list update is done in Machine::i_commitMedia */
11118
11119 rc = diff->i_addBackReference(mData->mUuid);
11120 AssertComRCThrowRC(rc);
11121
11122 /* add a new attachment */
11123 ComObjPtr<MediumAttachment> attachment;
11124 attachment.createObject();
11125 rc = attachment->init(this,
11126 diff,
11127 pAtt->i_getControllerName(),
11128 pAtt->i_getPort(),
11129 pAtt->i_getDevice(),
11130 DeviceType_HardDisk,
11131 true /* aImplicit */,
11132 false /* aPassthrough */,
11133 false /* aTempEject */,
11134 pAtt->i_getNonRotational(),
11135 pAtt->i_getDiscard(),
11136 pAtt->i_getHotPluggable(),
11137 pAtt->i_getBandwidthGroup());
11138 if (FAILED(rc)) throw rc;
11139
11140 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11141 AssertComRCThrowRC(rc);
11142 mMediumAttachments->push_back(attachment);
11143 }
11144 }
11145 catch (HRESULT aRC) { rc = aRC; }
11146
11147 /* unlock all hard disks we locked when there is no VM */
11148 if (!aOnline)
11149 {
11150 ErrorInfoKeeper eik;
11151
11152 HRESULT rc1 = lockedMediaMap->Clear();
11153 AssertComRC(rc1);
11154 }
11155
11156 return rc;
11157}
11158
11159/**
11160 * Deletes implicit differencing hard disks created either by
11161 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11162 * mMediumAttachments.
11163 *
11164 * Note that to delete hard disks created by #attachDevice() this method is
11165 * called from #i_rollbackMedia() when the changes are rolled back.
11166 *
11167 * @note Locks this object and the media tree for writing.
11168 */
11169HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11170{
11171 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11172
11173 AutoCaller autoCaller(this);
11174 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11175
11176 AutoMultiWriteLock2 alock(this->lockHandle(),
11177 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11178
11179 /* We absolutely must have backed up state. */
11180 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11181
11182 /* Check if there are any implicitly created diff images. */
11183 bool fImplicitDiffs = false;
11184 for (MediumAttachmentList::const_iterator
11185 it = mMediumAttachments->begin();
11186 it != mMediumAttachments->end();
11187 ++it)
11188 {
11189 const ComObjPtr<MediumAttachment> &pAtt = *it;
11190 if (pAtt->i_isImplicit())
11191 {
11192 fImplicitDiffs = true;
11193 break;
11194 }
11195 }
11196 /* If there is nothing to do, leave early. This saves lots of image locking
11197 * effort. It also avoids a MachineStateChanged event without real reason.
11198 * This is important e.g. when loading a VM config, because there should be
11199 * no events. Otherwise API clients can become thoroughly confused for
11200 * inaccessible VMs (the code for loading VM configs uses this method for
11201 * cleanup if the config makes no sense), as they take such events as an
11202 * indication that the VM is alive, and they would force the VM config to
11203 * be reread, leading to an endless loop. */
11204 if (!fImplicitDiffs)
11205 return S_OK;
11206
11207 HRESULT rc = S_OK;
11208 MachineState_T oldState = mData->mMachineState;
11209
11210 /* will release the lock before the potentially lengthy operation,
11211 * so protect with the special state (unless already protected) */
11212 if ( oldState != MachineState_Snapshotting
11213 && oldState != MachineState_OnlineSnapshotting
11214 && oldState != MachineState_LiveSnapshotting
11215 && oldState != MachineState_RestoringSnapshot
11216 && oldState != MachineState_DeletingSnapshot
11217 && oldState != MachineState_DeletingSnapshotOnline
11218 && oldState != MachineState_DeletingSnapshotPaused
11219 )
11220 i_setMachineState(MachineState_SettingUp);
11221
11222 // use appropriate locked media map (online or offline)
11223 MediumLockListMap lockedMediaOffline;
11224 MediumLockListMap *lockedMediaMap;
11225 if (aOnline)
11226 lockedMediaMap = &mData->mSession.mLockedMedia;
11227 else
11228 lockedMediaMap = &lockedMediaOffline;
11229
11230 try
11231 {
11232 if (!aOnline)
11233 {
11234 /* lock all attached hard disks early to detect "in use"
11235 * situations before deleting actual diffs */
11236 for (MediumAttachmentList::const_iterator
11237 it = mMediumAttachments->begin();
11238 it != mMediumAttachments->end();
11239 ++it)
11240 {
11241 MediumAttachment *pAtt = *it;
11242 if (pAtt->i_getType() == DeviceType_HardDisk)
11243 {
11244 Medium *pMedium = pAtt->i_getMedium();
11245 Assert(pMedium);
11246
11247 MediumLockList *pMediumLockList(new MediumLockList());
11248 alock.release();
11249 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11250 NULL /* pToLockWrite */,
11251 false /* fMediumLockWriteAll */,
11252 NULL,
11253 *pMediumLockList);
11254 alock.acquire();
11255
11256 if (FAILED(rc))
11257 {
11258 delete pMediumLockList;
11259 throw rc;
11260 }
11261
11262 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11263 if (FAILED(rc))
11264 throw rc;
11265 }
11266 }
11267
11268 if (FAILED(rc))
11269 throw rc;
11270 } // end of offline
11271
11272 /* Lock lists are now up to date and include implicitly created media */
11273
11274 /* Go through remembered attachments and delete all implicitly created
11275 * diffs and fix up the attachment information */
11276 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11277 MediumAttachmentList implicitAtts;
11278 for (MediumAttachmentList::const_iterator
11279 it = mMediumAttachments->begin();
11280 it != mMediumAttachments->end();
11281 ++it)
11282 {
11283 ComObjPtr<MediumAttachment> pAtt = *it;
11284 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11285 if (pMedium.isNull())
11286 continue;
11287
11288 // Implicit attachments go on the list for deletion and back references are removed.
11289 if (pAtt->i_isImplicit())
11290 {
11291 /* Deassociate and mark for deletion */
11292 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11293 rc = pMedium->i_removeBackReference(mData->mUuid);
11294 if (FAILED(rc))
11295 throw rc;
11296 implicitAtts.push_back(pAtt);
11297 continue;
11298 }
11299
11300 /* Was this medium attached before? */
11301 if (!i_findAttachment(oldAtts, pMedium))
11302 {
11303 /* no: de-associate */
11304 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11305 rc = pMedium->i_removeBackReference(mData->mUuid);
11306 if (FAILED(rc))
11307 throw rc;
11308 continue;
11309 }
11310 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11311 }
11312
11313 /* If there are implicit attachments to delete, throw away the lock
11314 * map contents (which will unlock all media) since the medium
11315 * attachments will be rolled back. Below we need to completely
11316 * recreate the lock map anyway since it is infinitely complex to
11317 * do this incrementally (would need reconstructing each attachment
11318 * change, which would be extremely hairy). */
11319 if (implicitAtts.size() != 0)
11320 {
11321 ErrorInfoKeeper eik;
11322
11323 HRESULT rc1 = lockedMediaMap->Clear();
11324 AssertComRC(rc1);
11325 }
11326
11327 /* rollback hard disk changes */
11328 mMediumAttachments.rollback();
11329
11330 MultiResult mrc(S_OK);
11331
11332 // Delete unused implicit diffs.
11333 if (implicitAtts.size() != 0)
11334 {
11335 alock.release();
11336
11337 for (MediumAttachmentList::const_iterator
11338 it = implicitAtts.begin();
11339 it != implicitAtts.end();
11340 ++it)
11341 {
11342 // Remove medium associated with this attachment.
11343 ComObjPtr<MediumAttachment> pAtt = *it;
11344 Assert(pAtt);
11345 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11346 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11347 Assert(pMedium);
11348
11349 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11350 // continue on delete failure, just collect error messages
11351 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11352 pMedium->i_getLocationFull().c_str() ));
11353 mrc = rc;
11354 }
11355 // Clear the list of deleted implicit attachments now, while not
11356 // holding the lock, as it will ultimately trigger Medium::uninit()
11357 // calls which assume that the media tree lock isn't held.
11358 implicitAtts.clear();
11359
11360 alock.acquire();
11361
11362 /* if there is a VM recreate media lock map as mentioned above,
11363 * otherwise it is a waste of time and we leave things unlocked */
11364 if (aOnline)
11365 {
11366 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11367 /* must never be NULL, but better safe than sorry */
11368 if (!pMachine.isNull())
11369 {
11370 alock.release();
11371 rc = mData->mSession.mMachine->i_lockMedia();
11372 alock.acquire();
11373 if (FAILED(rc))
11374 throw rc;
11375 }
11376 }
11377 }
11378 }
11379 catch (HRESULT aRC) {rc = aRC;}
11380
11381 if (mData->mMachineState == MachineState_SettingUp)
11382 i_setMachineState(oldState);
11383
11384 /* unlock all hard disks we locked when there is no VM */
11385 if (!aOnline)
11386 {
11387 ErrorInfoKeeper eik;
11388
11389 HRESULT rc1 = lockedMediaMap->Clear();
11390 AssertComRC(rc1);
11391 }
11392
11393 return rc;
11394}
11395
11396
11397/**
11398 * Looks through the given list of media attachments for one with the given parameters
11399 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11400 * can be searched as well if needed.
11401 *
11402 * @param ll
11403 * @param aControllerName
11404 * @param aControllerPort
11405 * @param aDevice
11406 * @return
11407 */
11408MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11409 const Utf8Str &aControllerName,
11410 LONG aControllerPort,
11411 LONG aDevice)
11412{
11413 for (MediumAttachmentList::const_iterator
11414 it = ll.begin();
11415 it != ll.end();
11416 ++it)
11417 {
11418 MediumAttachment *pAttach = *it;
11419 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11420 return pAttach;
11421 }
11422
11423 return NULL;
11424}
11425
11426/**
11427 * Looks through the given list of media attachments for one with the given parameters
11428 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11429 * can be searched as well if needed.
11430 *
11431 * @param ll
11432 * @param pMedium
11433 * @return
11434 */
11435MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11436 ComObjPtr<Medium> pMedium)
11437{
11438 for (MediumAttachmentList::const_iterator
11439 it = ll.begin();
11440 it != ll.end();
11441 ++it)
11442 {
11443 MediumAttachment *pAttach = *it;
11444 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11445 if (pMediumThis == pMedium)
11446 return pAttach;
11447 }
11448
11449 return NULL;
11450}
11451
11452/**
11453 * Looks through the given list of media attachments for one with the given parameters
11454 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11455 * can be searched as well if needed.
11456 *
11457 * @param ll
11458 * @param id
11459 * @return
11460 */
11461MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11462 Guid &id)
11463{
11464 for (MediumAttachmentList::const_iterator
11465 it = ll.begin();
11466 it != ll.end();
11467 ++it)
11468 {
11469 MediumAttachment *pAttach = *it;
11470 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11471 if (pMediumThis->i_getId() == id)
11472 return pAttach;
11473 }
11474
11475 return NULL;
11476}
11477
11478/**
11479 * Main implementation for Machine::DetachDevice. This also gets called
11480 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11481 *
11482 * @param pAttach Medium attachment to detach.
11483 * @param writeLock Machine write lock which the caller must have locked once.
11484 * This may be released temporarily in here.
11485 * @param pSnapshot If NULL, then the detachment is for the current machine.
11486 * Otherwise this is for a SnapshotMachine, and this must be
11487 * its snapshot.
11488 * @return
11489 */
11490HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11491 AutoWriteLock &writeLock,
11492 Snapshot *pSnapshot)
11493{
11494 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11495 DeviceType_T mediumType = pAttach->i_getType();
11496
11497 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11498
11499 if (pAttach->i_isImplicit())
11500 {
11501 /* attempt to implicitly delete the implicitly created diff */
11502
11503 /// @todo move the implicit flag from MediumAttachment to Medium
11504 /// and forbid any hard disk operation when it is implicit. Or maybe
11505 /// a special media state for it to make it even more simple.
11506
11507 Assert(mMediumAttachments.isBackedUp());
11508
11509 /* will release the lock before the potentially lengthy operation, so
11510 * protect with the special state */
11511 MachineState_T oldState = mData->mMachineState;
11512 i_setMachineState(MachineState_SettingUp);
11513
11514 writeLock.release();
11515
11516 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11517 true /*aWait*/);
11518
11519 writeLock.acquire();
11520
11521 i_setMachineState(oldState);
11522
11523 if (FAILED(rc)) return rc;
11524 }
11525
11526 i_setModified(IsModified_Storage);
11527 mMediumAttachments.backup();
11528 mMediumAttachments->remove(pAttach);
11529
11530 if (!oldmedium.isNull())
11531 {
11532 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11533 if (pSnapshot)
11534 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11535 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11536 else if (mediumType != DeviceType_HardDisk)
11537 oldmedium->i_removeBackReference(mData->mUuid);
11538 }
11539
11540 return S_OK;
11541}
11542
11543/**
11544 * Goes thru all media of the given list and
11545 *
11546 * 1) calls i_detachDevice() on each of them for this machine and
11547 * 2) adds all Medium objects found in the process to the given list,
11548 * depending on cleanupMode.
11549 *
11550 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11551 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11552 * media to the list.
11553 *
11554 * This gets called from Machine::Unregister, both for the actual Machine and
11555 * the SnapshotMachine objects that might be found in the snapshots.
11556 *
11557 * Requires caller and locking. The machine lock must be passed in because it
11558 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11559 *
11560 * @param writeLock Machine lock from top-level caller; this gets passed to
11561 * i_detachDevice.
11562 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11563 * object if called for a SnapshotMachine.
11564 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11565 * added to llMedia; if Full, then all media get added;
11566 * otherwise no media get added.
11567 * @param llMedia Caller's list to receive Medium objects which got detached so
11568 * caller can close() them, depending on cleanupMode.
11569 * @return
11570 */
11571HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11572 Snapshot *pSnapshot,
11573 CleanupMode_T cleanupMode,
11574 MediaList &llMedia)
11575{
11576 Assert(isWriteLockOnCurrentThread());
11577
11578 HRESULT rc;
11579
11580 // make a temporary list because i_detachDevice invalidates iterators into
11581 // mMediumAttachments
11582 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11583
11584 for (MediumAttachmentList::iterator
11585 it = llAttachments2.begin();
11586 it != llAttachments2.end();
11587 ++it)
11588 {
11589 ComObjPtr<MediumAttachment> &pAttach = *it;
11590 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11591
11592 if (!pMedium.isNull())
11593 {
11594 AutoCaller mac(pMedium);
11595 if (FAILED(mac.rc())) return mac.rc();
11596 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11597 DeviceType_T devType = pMedium->i_getDeviceType();
11598 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11599 && devType == DeviceType_HardDisk)
11600 || (cleanupMode == CleanupMode_Full)
11601 )
11602 {
11603 llMedia.push_back(pMedium);
11604 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11605 /* Not allowed to keep this lock as below we need the parent
11606 * medium lock, and the lock order is parent to child. */
11607 lock.release();
11608 /*
11609 * Search for medias which are not attached to any machine, but
11610 * in the chain to an attached disk. Mediums are only consided
11611 * if they are:
11612 * - have only one child
11613 * - no references to any machines
11614 * - are of normal medium type
11615 */
11616 while (!pParent.isNull())
11617 {
11618 AutoCaller mac1(pParent);
11619 if (FAILED(mac1.rc())) return mac1.rc();
11620 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11621 if (pParent->i_getChildren().size() == 1)
11622 {
11623 if ( pParent->i_getMachineBackRefCount() == 0
11624 && pParent->i_getType() == MediumType_Normal
11625 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11626 llMedia.push_back(pParent);
11627 }
11628 else
11629 break;
11630 pParent = pParent->i_getParent();
11631 }
11632 }
11633 }
11634
11635 // real machine: then we need to use the proper method
11636 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11637
11638 if (FAILED(rc))
11639 return rc;
11640 }
11641
11642 return S_OK;
11643}
11644
11645/**
11646 * Perform deferred hard disk detachments.
11647 *
11648 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11649 * changed (not backed up).
11650 *
11651 * If @a aOnline is @c true then this method will also unlock the old hard
11652 * disks for which the new implicit diffs were created and will lock these new
11653 * diffs for writing.
11654 *
11655 * @param aOnline Whether the VM was online prior to this operation.
11656 *
11657 * @note Locks this object for writing!
11658 */
11659void Machine::i_commitMedia(bool aOnline /*= false*/)
11660{
11661 AutoCaller autoCaller(this);
11662 AssertComRCReturnVoid(autoCaller.rc());
11663
11664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11665
11666 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11667
11668 HRESULT rc = S_OK;
11669
11670 /* no attach/detach operations -- nothing to do */
11671 if (!mMediumAttachments.isBackedUp())
11672 return;
11673
11674 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11675 bool fMediaNeedsLocking = false;
11676
11677 /* enumerate new attachments */
11678 for (MediumAttachmentList::const_iterator
11679 it = mMediumAttachments->begin();
11680 it != mMediumAttachments->end();
11681 ++it)
11682 {
11683 MediumAttachment *pAttach = *it;
11684
11685 pAttach->i_commit();
11686
11687 Medium *pMedium = pAttach->i_getMedium();
11688 bool fImplicit = pAttach->i_isImplicit();
11689
11690 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11691 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11692 fImplicit));
11693
11694 /** @todo convert all this Machine-based voodoo to MediumAttachment
11695 * based commit logic. */
11696 if (fImplicit)
11697 {
11698 /* convert implicit attachment to normal */
11699 pAttach->i_setImplicit(false);
11700
11701 if ( aOnline
11702 && pMedium
11703 && pAttach->i_getType() == DeviceType_HardDisk
11704 )
11705 {
11706 /* update the appropriate lock list */
11707 MediumLockList *pMediumLockList;
11708 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11709 AssertComRC(rc);
11710 if (pMediumLockList)
11711 {
11712 /* unlock if there's a need to change the locking */
11713 if (!fMediaNeedsLocking)
11714 {
11715 rc = mData->mSession.mLockedMedia.Unlock();
11716 AssertComRC(rc);
11717 fMediaNeedsLocking = true;
11718 }
11719 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11720 AssertComRC(rc);
11721 rc = pMediumLockList->Append(pMedium, true);
11722 AssertComRC(rc);
11723 }
11724 }
11725
11726 continue;
11727 }
11728
11729 if (pMedium)
11730 {
11731 /* was this medium attached before? */
11732 for (MediumAttachmentList::iterator
11733 oldIt = oldAtts.begin();
11734 oldIt != oldAtts.end();
11735 ++oldIt)
11736 {
11737 MediumAttachment *pOldAttach = *oldIt;
11738 if (pOldAttach->i_getMedium() == pMedium)
11739 {
11740 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11741
11742 /* yes: remove from old to avoid de-association */
11743 oldAtts.erase(oldIt);
11744 break;
11745 }
11746 }
11747 }
11748 }
11749
11750 /* enumerate remaining old attachments and de-associate from the
11751 * current machine state */
11752 for (MediumAttachmentList::const_iterator
11753 it = oldAtts.begin();
11754 it != oldAtts.end();
11755 ++it)
11756 {
11757 MediumAttachment *pAttach = *it;
11758 Medium *pMedium = pAttach->i_getMedium();
11759
11760 /* Detach only hard disks, since DVD/floppy media is detached
11761 * instantly in MountMedium. */
11762 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11763 {
11764 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11765
11766 /* now de-associate from the current machine state */
11767 rc = pMedium->i_removeBackReference(mData->mUuid);
11768 AssertComRC(rc);
11769
11770 if (aOnline)
11771 {
11772 /* unlock since medium is not used anymore */
11773 MediumLockList *pMediumLockList;
11774 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11775 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11776 {
11777 /* this happens for online snapshots, there the attachment
11778 * is changing, but only to a diff image created under
11779 * the old one, so there is no separate lock list */
11780 Assert(!pMediumLockList);
11781 }
11782 else
11783 {
11784 AssertComRC(rc);
11785 if (pMediumLockList)
11786 {
11787 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11788 AssertComRC(rc);
11789 }
11790 }
11791 }
11792 }
11793 }
11794
11795 /* take media locks again so that the locking state is consistent */
11796 if (fMediaNeedsLocking)
11797 {
11798 Assert(aOnline);
11799 rc = mData->mSession.mLockedMedia.Lock();
11800 AssertComRC(rc);
11801 }
11802
11803 /* commit the hard disk changes */
11804 mMediumAttachments.commit();
11805
11806 if (i_isSessionMachine())
11807 {
11808 /*
11809 * Update the parent machine to point to the new owner.
11810 * This is necessary because the stored parent will point to the
11811 * session machine otherwise and cause crashes or errors later
11812 * when the session machine gets invalid.
11813 */
11814 /** @todo Change the MediumAttachment class to behave like any other
11815 * class in this regard by creating peer MediumAttachment
11816 * objects for session machines and share the data with the peer
11817 * machine.
11818 */
11819 for (MediumAttachmentList::const_iterator
11820 it = mMediumAttachments->begin();
11821 it != mMediumAttachments->end();
11822 ++it)
11823 (*it)->i_updateParentMachine(mPeer);
11824
11825 /* attach new data to the primary machine and reshare it */
11826 mPeer->mMediumAttachments.attach(mMediumAttachments);
11827 }
11828
11829 return;
11830}
11831
11832/**
11833 * Perform deferred deletion of implicitly created diffs.
11834 *
11835 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11836 * changed (not backed up).
11837 *
11838 * @note Locks this object for writing!
11839 */
11840void Machine::i_rollbackMedia()
11841{
11842 AutoCaller autoCaller(this);
11843 AssertComRCReturnVoid(autoCaller.rc());
11844
11845 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11846 LogFlowThisFunc(("Entering rollbackMedia\n"));
11847
11848 HRESULT rc = S_OK;
11849
11850 /* no attach/detach operations -- nothing to do */
11851 if (!mMediumAttachments.isBackedUp())
11852 return;
11853
11854 /* enumerate new attachments */
11855 for (MediumAttachmentList::const_iterator
11856 it = mMediumAttachments->begin();
11857 it != mMediumAttachments->end();
11858 ++it)
11859 {
11860 MediumAttachment *pAttach = *it;
11861 /* Fix up the backrefs for DVD/floppy media. */
11862 if (pAttach->i_getType() != DeviceType_HardDisk)
11863 {
11864 Medium *pMedium = pAttach->i_getMedium();
11865 if (pMedium)
11866 {
11867 rc = pMedium->i_removeBackReference(mData->mUuid);
11868 AssertComRC(rc);
11869 }
11870 }
11871
11872 (*it)->i_rollback();
11873
11874 pAttach = *it;
11875 /* Fix up the backrefs for DVD/floppy media. */
11876 if (pAttach->i_getType() != DeviceType_HardDisk)
11877 {
11878 Medium *pMedium = pAttach->i_getMedium();
11879 if (pMedium)
11880 {
11881 rc = pMedium->i_addBackReference(mData->mUuid);
11882 AssertComRC(rc);
11883 }
11884 }
11885 }
11886
11887 /** @todo convert all this Machine-based voodoo to MediumAttachment
11888 * based rollback logic. */
11889 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11890
11891 return;
11892}
11893
11894/**
11895 * Returns true if the settings file is located in the directory named exactly
11896 * as the machine; this means, among other things, that the machine directory
11897 * should be auto-renamed.
11898 *
11899 * @param aSettingsDir if not NULL, the full machine settings file directory
11900 * name will be assigned there.
11901 *
11902 * @note Doesn't lock anything.
11903 * @note Not thread safe (must be called from this object's lock).
11904 */
11905bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11906{
11907 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11908 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11909 if (aSettingsDir)
11910 *aSettingsDir = strMachineDirName;
11911 strMachineDirName.stripPath(); // vmname
11912 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11913 strConfigFileOnly.stripPath() // vmname.vbox
11914 .stripSuffix(); // vmname
11915 /** @todo hack, make somehow use of ComposeMachineFilename */
11916 if (mUserData->s.fDirectoryIncludesUUID)
11917 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11918
11919 AssertReturn(!strMachineDirName.isEmpty(), false);
11920 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11921
11922 return strMachineDirName == strConfigFileOnly;
11923}
11924
11925/**
11926 * Discards all changes to machine settings.
11927 *
11928 * @param aNotify Whether to notify the direct session about changes or not.
11929 *
11930 * @note Locks objects for writing!
11931 */
11932void Machine::i_rollback(bool aNotify)
11933{
11934 AutoCaller autoCaller(this);
11935 AssertComRCReturn(autoCaller.rc(), (void)0);
11936
11937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11938
11939 if (!mStorageControllers.isNull())
11940 {
11941 if (mStorageControllers.isBackedUp())
11942 {
11943 /* unitialize all new devices (absent in the backed up list). */
11944 StorageControllerList *backedList = mStorageControllers.backedUpData();
11945 for (StorageControllerList::const_iterator
11946 it = mStorageControllers->begin();
11947 it != mStorageControllers->end();
11948 ++it)
11949 {
11950 if ( std::find(backedList->begin(), backedList->end(), *it)
11951 == backedList->end()
11952 )
11953 {
11954 (*it)->uninit();
11955 }
11956 }
11957
11958 /* restore the list */
11959 mStorageControllers.rollback();
11960 }
11961
11962 /* rollback any changes to devices after restoring the list */
11963 if (mData->flModifications & IsModified_Storage)
11964 {
11965 for (StorageControllerList::const_iterator
11966 it = mStorageControllers->begin();
11967 it != mStorageControllers->end();
11968 ++it)
11969 {
11970 (*it)->i_rollback();
11971 }
11972 }
11973 }
11974
11975 if (!mUSBControllers.isNull())
11976 {
11977 if (mUSBControllers.isBackedUp())
11978 {
11979 /* unitialize all new devices (absent in the backed up list). */
11980 USBControllerList *backedList = mUSBControllers.backedUpData();
11981 for (USBControllerList::const_iterator
11982 it = mUSBControllers->begin();
11983 it != mUSBControllers->end();
11984 ++it)
11985 {
11986 if ( std::find(backedList->begin(), backedList->end(), *it)
11987 == backedList->end()
11988 )
11989 {
11990 (*it)->uninit();
11991 }
11992 }
11993
11994 /* restore the list */
11995 mUSBControllers.rollback();
11996 }
11997
11998 /* rollback any changes to devices after restoring the list */
11999 if (mData->flModifications & IsModified_USB)
12000 {
12001 for (USBControllerList::const_iterator
12002 it = mUSBControllers->begin();
12003 it != mUSBControllers->end();
12004 ++it)
12005 {
12006 (*it)->i_rollback();
12007 }
12008 }
12009 }
12010
12011 mUserData.rollback();
12012
12013 mHWData.rollback();
12014
12015 if (mData->flModifications & IsModified_Storage)
12016 i_rollbackMedia();
12017
12018 if (mBIOSSettings)
12019 mBIOSSettings->i_rollback();
12020
12021 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12022 mVRDEServer->i_rollback();
12023
12024 if (mAudioAdapter)
12025 mAudioAdapter->i_rollback();
12026
12027 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12028 mUSBDeviceFilters->i_rollback();
12029
12030 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12031 mBandwidthControl->i_rollback();
12032
12033 if (!mHWData.isNull())
12034 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12035 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12036 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12037 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12038
12039 if (mData->flModifications & IsModified_NetworkAdapters)
12040 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12041 if ( mNetworkAdapters[slot]
12042 && mNetworkAdapters[slot]->i_isModified())
12043 {
12044 mNetworkAdapters[slot]->i_rollback();
12045 networkAdapters[slot] = mNetworkAdapters[slot];
12046 }
12047
12048 if (mData->flModifications & IsModified_SerialPorts)
12049 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12050 if ( mSerialPorts[slot]
12051 && mSerialPorts[slot]->i_isModified())
12052 {
12053 mSerialPorts[slot]->i_rollback();
12054 serialPorts[slot] = mSerialPorts[slot];
12055 }
12056
12057 if (mData->flModifications & IsModified_ParallelPorts)
12058 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12059 if ( mParallelPorts[slot]
12060 && mParallelPorts[slot]->i_isModified())
12061 {
12062 mParallelPorts[slot]->i_rollback();
12063 parallelPorts[slot] = mParallelPorts[slot];
12064 }
12065
12066 if (aNotify)
12067 {
12068 /* inform the direct session about changes */
12069
12070 ComObjPtr<Machine> that = this;
12071 uint32_t flModifications = mData->flModifications;
12072 alock.release();
12073
12074 if (flModifications & IsModified_SharedFolders)
12075 that->i_onSharedFolderChange();
12076
12077 if (flModifications & IsModified_VRDEServer)
12078 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12079 if (flModifications & IsModified_USB)
12080 that->i_onUSBControllerChange();
12081
12082 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12083 if (networkAdapters[slot])
12084 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12085 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12086 if (serialPorts[slot])
12087 that->i_onSerialPortChange(serialPorts[slot]);
12088 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12089 if (parallelPorts[slot])
12090 that->i_onParallelPortChange(parallelPorts[slot]);
12091
12092 if (flModifications & IsModified_Storage)
12093 that->i_onStorageControllerChange();
12094
12095#if 0
12096 if (flModifications & IsModified_BandwidthControl)
12097 that->onBandwidthControlChange();
12098#endif
12099 }
12100}
12101
12102/**
12103 * Commits all the changes to machine settings.
12104 *
12105 * Note that this operation is supposed to never fail.
12106 *
12107 * @note Locks this object and children for writing.
12108 */
12109void Machine::i_commit()
12110{
12111 AutoCaller autoCaller(this);
12112 AssertComRCReturnVoid(autoCaller.rc());
12113
12114 AutoCaller peerCaller(mPeer);
12115 AssertComRCReturnVoid(peerCaller.rc());
12116
12117 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12118
12119 /*
12120 * use safe commit to ensure Snapshot machines (that share mUserData)
12121 * will still refer to a valid memory location
12122 */
12123 mUserData.commitCopy();
12124
12125 mHWData.commit();
12126
12127 if (mMediumAttachments.isBackedUp())
12128 i_commitMedia(Global::IsOnline(mData->mMachineState));
12129
12130 mBIOSSettings->i_commit();
12131 mVRDEServer->i_commit();
12132 mAudioAdapter->i_commit();
12133 mUSBDeviceFilters->i_commit();
12134 mBandwidthControl->i_commit();
12135
12136 /* Since mNetworkAdapters is a list which might have been changed (resized)
12137 * without using the Backupable<> template we need to handle the copying
12138 * of the list entries manually, including the creation of peers for the
12139 * new objects. */
12140 bool commitNetworkAdapters = false;
12141 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12142 if (mPeer)
12143 {
12144 /* commit everything, even the ones which will go away */
12145 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12146 mNetworkAdapters[slot]->i_commit();
12147 /* copy over the new entries, creating a peer and uninit the original */
12148 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12149 for (size_t slot = 0; slot < newSize; slot++)
12150 {
12151 /* look if this adapter has a peer device */
12152 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12153 if (!peer)
12154 {
12155 /* no peer means the adapter is a newly created one;
12156 * create a peer owning data this data share it with */
12157 peer.createObject();
12158 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12159 }
12160 mPeer->mNetworkAdapters[slot] = peer;
12161 }
12162 /* uninit any no longer needed network adapters */
12163 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12164 mNetworkAdapters[slot]->uninit();
12165 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12166 {
12167 if (mPeer->mNetworkAdapters[slot])
12168 mPeer->mNetworkAdapters[slot]->uninit();
12169 }
12170 /* Keep the original network adapter count until this point, so that
12171 * discarding a chipset type change will not lose settings. */
12172 mNetworkAdapters.resize(newSize);
12173 mPeer->mNetworkAdapters.resize(newSize);
12174 }
12175 else
12176 {
12177 /* we have no peer (our parent is the newly created machine);
12178 * just commit changes to the network adapters */
12179 commitNetworkAdapters = true;
12180 }
12181 if (commitNetworkAdapters)
12182 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12183 mNetworkAdapters[slot]->i_commit();
12184
12185 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12186 mSerialPorts[slot]->i_commit();
12187 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12188 mParallelPorts[slot]->i_commit();
12189
12190 bool commitStorageControllers = false;
12191
12192 if (mStorageControllers.isBackedUp())
12193 {
12194 mStorageControllers.commit();
12195
12196 if (mPeer)
12197 {
12198 /* Commit all changes to new controllers (this will reshare data with
12199 * peers for those who have peers) */
12200 StorageControllerList *newList = new StorageControllerList();
12201 for (StorageControllerList::const_iterator
12202 it = mStorageControllers->begin();
12203 it != mStorageControllers->end();
12204 ++it)
12205 {
12206 (*it)->i_commit();
12207
12208 /* look if this controller has a peer device */
12209 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12210 if (!peer)
12211 {
12212 /* no peer means the device is a newly created one;
12213 * create a peer owning data this device share it with */
12214 peer.createObject();
12215 peer->init(mPeer, *it, true /* aReshare */);
12216 }
12217 else
12218 {
12219 /* remove peer from the old list */
12220 mPeer->mStorageControllers->remove(peer);
12221 }
12222 /* and add it to the new list */
12223 newList->push_back(peer);
12224 }
12225
12226 /* uninit old peer's controllers that are left */
12227 for (StorageControllerList::const_iterator
12228 it = mPeer->mStorageControllers->begin();
12229 it != mPeer->mStorageControllers->end();
12230 ++it)
12231 {
12232 (*it)->uninit();
12233 }
12234
12235 /* attach new list of controllers to our peer */
12236 mPeer->mStorageControllers.attach(newList);
12237 }
12238 else
12239 {
12240 /* we have no peer (our parent is the newly created machine);
12241 * just commit changes to devices */
12242 commitStorageControllers = true;
12243 }
12244 }
12245 else
12246 {
12247 /* the list of controllers itself is not changed,
12248 * just commit changes to controllers themselves */
12249 commitStorageControllers = true;
12250 }
12251
12252 if (commitStorageControllers)
12253 {
12254 for (StorageControllerList::const_iterator
12255 it = mStorageControllers->begin();
12256 it != mStorageControllers->end();
12257 ++it)
12258 {
12259 (*it)->i_commit();
12260 }
12261 }
12262
12263 bool commitUSBControllers = false;
12264
12265 if (mUSBControllers.isBackedUp())
12266 {
12267 mUSBControllers.commit();
12268
12269 if (mPeer)
12270 {
12271 /* Commit all changes to new controllers (this will reshare data with
12272 * peers for those who have peers) */
12273 USBControllerList *newList = new USBControllerList();
12274 for (USBControllerList::const_iterator
12275 it = mUSBControllers->begin();
12276 it != mUSBControllers->end();
12277 ++it)
12278 {
12279 (*it)->i_commit();
12280
12281 /* look if this controller has a peer device */
12282 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12283 if (!peer)
12284 {
12285 /* no peer means the device is a newly created one;
12286 * create a peer owning data this device share it with */
12287 peer.createObject();
12288 peer->init(mPeer, *it, true /* aReshare */);
12289 }
12290 else
12291 {
12292 /* remove peer from the old list */
12293 mPeer->mUSBControllers->remove(peer);
12294 }
12295 /* and add it to the new list */
12296 newList->push_back(peer);
12297 }
12298
12299 /* uninit old peer's controllers that are left */
12300 for (USBControllerList::const_iterator
12301 it = mPeer->mUSBControllers->begin();
12302 it != mPeer->mUSBControllers->end();
12303 ++it)
12304 {
12305 (*it)->uninit();
12306 }
12307
12308 /* attach new list of controllers to our peer */
12309 mPeer->mUSBControllers.attach(newList);
12310 }
12311 else
12312 {
12313 /* we have no peer (our parent is the newly created machine);
12314 * just commit changes to devices */
12315 commitUSBControllers = true;
12316 }
12317 }
12318 else
12319 {
12320 /* the list of controllers itself is not changed,
12321 * just commit changes to controllers themselves */
12322 commitUSBControllers = true;
12323 }
12324
12325 if (commitUSBControllers)
12326 {
12327 for (USBControllerList::const_iterator
12328 it = mUSBControllers->begin();
12329 it != mUSBControllers->end();
12330 ++it)
12331 {
12332 (*it)->i_commit();
12333 }
12334 }
12335
12336 if (i_isSessionMachine())
12337 {
12338 /* attach new data to the primary machine and reshare it */
12339 mPeer->mUserData.attach(mUserData);
12340 mPeer->mHWData.attach(mHWData);
12341 /* mmMediumAttachments is reshared by fixupMedia */
12342 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12343 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12344 }
12345}
12346
12347/**
12348 * Copies all the hardware data from the given machine.
12349 *
12350 * Currently, only called when the VM is being restored from a snapshot. In
12351 * particular, this implies that the VM is not running during this method's
12352 * call.
12353 *
12354 * @note This method must be called from under this object's lock.
12355 *
12356 * @note This method doesn't call #i_commit(), so all data remains backed up and
12357 * unsaved.
12358 */
12359void Machine::i_copyFrom(Machine *aThat)
12360{
12361 AssertReturnVoid(!i_isSnapshotMachine());
12362 AssertReturnVoid(aThat->i_isSnapshotMachine());
12363
12364 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12365
12366 mHWData.assignCopy(aThat->mHWData);
12367
12368 // create copies of all shared folders (mHWData after attaching a copy
12369 // contains just references to original objects)
12370 for (HWData::SharedFolderList::iterator
12371 it = mHWData->mSharedFolders.begin();
12372 it != mHWData->mSharedFolders.end();
12373 ++it)
12374 {
12375 ComObjPtr<SharedFolder> folder;
12376 folder.createObject();
12377 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12378 AssertComRC(rc);
12379 *it = folder;
12380 }
12381
12382 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12383 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12384 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12385 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12386 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12387
12388 /* create private copies of all controllers */
12389 mStorageControllers.backup();
12390 mStorageControllers->clear();
12391 for (StorageControllerList::const_iterator
12392 it = aThat->mStorageControllers->begin();
12393 it != aThat->mStorageControllers->end();
12394 ++it)
12395 {
12396 ComObjPtr<StorageController> ctrl;
12397 ctrl.createObject();
12398 ctrl->initCopy(this, *it);
12399 mStorageControllers->push_back(ctrl);
12400 }
12401
12402 /* create private copies of all USB controllers */
12403 mUSBControllers.backup();
12404 mUSBControllers->clear();
12405 for (USBControllerList::const_iterator
12406 it = aThat->mUSBControllers->begin();
12407 it != aThat->mUSBControllers->end();
12408 ++it)
12409 {
12410 ComObjPtr<USBController> ctrl;
12411 ctrl.createObject();
12412 ctrl->initCopy(this, *it);
12413 mUSBControllers->push_back(ctrl);
12414 }
12415
12416 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12417 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12418 {
12419 if (mNetworkAdapters[slot].isNotNull())
12420 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12421 else
12422 {
12423 unconst(mNetworkAdapters[slot]).createObject();
12424 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12425 }
12426 }
12427 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12428 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12429 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12430 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12431}
12432
12433/**
12434 * Returns whether the given storage controller is hotplug capable.
12435 *
12436 * @returns true if the controller supports hotplugging
12437 * false otherwise.
12438 * @param enmCtrlType The controller type to check for.
12439 */
12440bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12441{
12442 ComPtr<ISystemProperties> systemProperties;
12443 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12444 if (FAILED(rc))
12445 return false;
12446
12447 BOOL aHotplugCapable = FALSE;
12448 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12449
12450 return RT_BOOL(aHotplugCapable);
12451}
12452
12453#ifdef VBOX_WITH_RESOURCE_USAGE_API
12454
12455void Machine::i_getDiskList(MediaList &list)
12456{
12457 for (MediumAttachmentList::const_iterator
12458 it = mMediumAttachments->begin();
12459 it != mMediumAttachments->end();
12460 ++it)
12461 {
12462 MediumAttachment *pAttach = *it;
12463 /* just in case */
12464 AssertContinue(pAttach);
12465
12466 AutoCaller localAutoCallerA(pAttach);
12467 if (FAILED(localAutoCallerA.rc())) continue;
12468
12469 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12470
12471 if (pAttach->i_getType() == DeviceType_HardDisk)
12472 list.push_back(pAttach->i_getMedium());
12473 }
12474}
12475
12476void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12477{
12478 AssertReturnVoid(isWriteLockOnCurrentThread());
12479 AssertPtrReturnVoid(aCollector);
12480
12481 pm::CollectorHAL *hal = aCollector->getHAL();
12482 /* Create sub metrics */
12483 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12484 "Percentage of processor time spent in user mode by the VM process.");
12485 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12486 "Percentage of processor time spent in kernel mode by the VM process.");
12487 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12488 "Size of resident portion of VM process in memory.");
12489 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12490 "Actual size of all VM disks combined.");
12491 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12492 "Network receive rate.");
12493 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12494 "Network transmit rate.");
12495 /* Create and register base metrics */
12496 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12497 cpuLoadUser, cpuLoadKernel);
12498 aCollector->registerBaseMetric(cpuLoad);
12499 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12500 ramUsageUsed);
12501 aCollector->registerBaseMetric(ramUsage);
12502 MediaList disks;
12503 i_getDiskList(disks);
12504 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12505 diskUsageUsed);
12506 aCollector->registerBaseMetric(diskUsage);
12507
12508 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12509 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12510 new pm::AggregateAvg()));
12511 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12512 new pm::AggregateMin()));
12513 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12514 new pm::AggregateMax()));
12515 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12516 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12517 new pm::AggregateAvg()));
12518 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12519 new pm::AggregateMin()));
12520 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12521 new pm::AggregateMax()));
12522
12523 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12524 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12525 new pm::AggregateAvg()));
12526 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12527 new pm::AggregateMin()));
12528 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12529 new pm::AggregateMax()));
12530
12531 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12532 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12533 new pm::AggregateAvg()));
12534 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12535 new pm::AggregateMin()));
12536 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12537 new pm::AggregateMax()));
12538
12539
12540 /* Guest metrics collector */
12541 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12542 aCollector->registerGuest(mCollectorGuest);
12543 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12544
12545 /* Create sub metrics */
12546 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12547 "Percentage of processor time spent in user mode as seen by the guest.");
12548 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12549 "Percentage of processor time spent in kernel mode as seen by the guest.");
12550 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12551 "Percentage of processor time spent idling as seen by the guest.");
12552
12553 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12554 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12555 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12556 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12557 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12558 pm::SubMetric *guestMemCache = new pm::SubMetric(
12559 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12560
12561 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12562 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12563
12564 /* Create and register base metrics */
12565 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12566 machineNetRx, machineNetTx);
12567 aCollector->registerBaseMetric(machineNetRate);
12568
12569 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12570 guestLoadUser, guestLoadKernel, guestLoadIdle);
12571 aCollector->registerBaseMetric(guestCpuLoad);
12572
12573 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12574 guestMemTotal, guestMemFree,
12575 guestMemBalloon, guestMemShared,
12576 guestMemCache, guestPagedTotal);
12577 aCollector->registerBaseMetric(guestCpuMem);
12578
12579 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12580 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12581 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12582 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12583
12584 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12585 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12586 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12587 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12588
12589 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12590 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12591 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12592 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12593
12594 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12595 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12596 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12597 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12598
12599 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12600 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12601 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12602 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12603
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12608
12609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12613
12614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12618
12619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12623
12624 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12625 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12626 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12627 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12628
12629 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12630 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12631 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12632 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12633}
12634
12635void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12636{
12637 AssertReturnVoid(isWriteLockOnCurrentThread());
12638
12639 if (aCollector)
12640 {
12641 aCollector->unregisterMetricsFor(aMachine);
12642 aCollector->unregisterBaseMetricsFor(aMachine);
12643 }
12644}
12645
12646#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12647
12648
12649////////////////////////////////////////////////////////////////////////////////
12650
12651DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12652
12653HRESULT SessionMachine::FinalConstruct()
12654{
12655 LogFlowThisFunc(("\n"));
12656
12657 mClientToken = NULL;
12658
12659 return BaseFinalConstruct();
12660}
12661
12662void SessionMachine::FinalRelease()
12663{
12664 LogFlowThisFunc(("\n"));
12665
12666 Assert(!mClientToken);
12667 /* paranoia, should not hang around any more */
12668 if (mClientToken)
12669 {
12670 delete mClientToken;
12671 mClientToken = NULL;
12672 }
12673
12674 uninit(Uninit::Unexpected);
12675
12676 BaseFinalRelease();
12677}
12678
12679/**
12680 * @note Must be called only by Machine::LockMachine() from its own write lock.
12681 */
12682HRESULT SessionMachine::init(Machine *aMachine)
12683{
12684 LogFlowThisFuncEnter();
12685 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12686
12687 AssertReturn(aMachine, E_INVALIDARG);
12688
12689 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12690
12691 /* Enclose the state transition NotReady->InInit->Ready */
12692 AutoInitSpan autoInitSpan(this);
12693 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12694
12695 HRESULT rc = S_OK;
12696
12697 RT_ZERO(mAuthLibCtx);
12698
12699 /* create the machine client token */
12700 try
12701 {
12702 mClientToken = new ClientToken(aMachine, this);
12703 if (!mClientToken->isReady())
12704 {
12705 delete mClientToken;
12706 mClientToken = NULL;
12707 rc = E_FAIL;
12708 }
12709 }
12710 catch (std::bad_alloc &)
12711 {
12712 rc = E_OUTOFMEMORY;
12713 }
12714 if (FAILED(rc))
12715 return rc;
12716
12717 /* memorize the peer Machine */
12718 unconst(mPeer) = aMachine;
12719 /* share the parent pointer */
12720 unconst(mParent) = aMachine->mParent;
12721
12722 /* take the pointers to data to share */
12723 mData.share(aMachine->mData);
12724 mSSData.share(aMachine->mSSData);
12725
12726 mUserData.share(aMachine->mUserData);
12727 mHWData.share(aMachine->mHWData);
12728 mMediumAttachments.share(aMachine->mMediumAttachments);
12729
12730 mStorageControllers.allocate();
12731 for (StorageControllerList::const_iterator
12732 it = aMachine->mStorageControllers->begin();
12733 it != aMachine->mStorageControllers->end();
12734 ++it)
12735 {
12736 ComObjPtr<StorageController> ctl;
12737 ctl.createObject();
12738 ctl->init(this, *it);
12739 mStorageControllers->push_back(ctl);
12740 }
12741
12742 mUSBControllers.allocate();
12743 for (USBControllerList::const_iterator
12744 it = aMachine->mUSBControllers->begin();
12745 it != aMachine->mUSBControllers->end();
12746 ++it)
12747 {
12748 ComObjPtr<USBController> ctl;
12749 ctl.createObject();
12750 ctl->init(this, *it);
12751 mUSBControllers->push_back(ctl);
12752 }
12753
12754 unconst(mBIOSSettings).createObject();
12755 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12756 /* create another VRDEServer object that will be mutable */
12757 unconst(mVRDEServer).createObject();
12758 mVRDEServer->init(this, aMachine->mVRDEServer);
12759 /* create another audio adapter object that will be mutable */
12760 unconst(mAudioAdapter).createObject();
12761 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12762 /* create a list of serial ports that will be mutable */
12763 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12764 {
12765 unconst(mSerialPorts[slot]).createObject();
12766 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12767 }
12768 /* create a list of parallel ports that will be mutable */
12769 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12770 {
12771 unconst(mParallelPorts[slot]).createObject();
12772 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12773 }
12774
12775 /* create another USB device filters object that will be mutable */
12776 unconst(mUSBDeviceFilters).createObject();
12777 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12778
12779 /* create a list of network adapters that will be mutable */
12780 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12781 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12782 {
12783 unconst(mNetworkAdapters[slot]).createObject();
12784 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12785 }
12786
12787 /* create another bandwidth control object that will be mutable */
12788 unconst(mBandwidthControl).createObject();
12789 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12790
12791 /* default is to delete saved state on Saved -> PoweredOff transition */
12792 mRemoveSavedState = true;
12793
12794 /* Confirm a successful initialization when it's the case */
12795 autoInitSpan.setSucceeded();
12796
12797 miNATNetworksStarted = 0;
12798
12799 LogFlowThisFuncLeave();
12800 return rc;
12801}
12802
12803/**
12804 * Uninitializes this session object. If the reason is other than
12805 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12806 * or the client watcher code.
12807 *
12808 * @param aReason uninitialization reason
12809 *
12810 * @note Locks mParent + this object for writing.
12811 */
12812void SessionMachine::uninit(Uninit::Reason aReason)
12813{
12814 LogFlowThisFuncEnter();
12815 LogFlowThisFunc(("reason=%d\n", aReason));
12816
12817 /*
12818 * Strongly reference ourselves to prevent this object deletion after
12819 * mData->mSession.mMachine.setNull() below (which can release the last
12820 * reference and call the destructor). Important: this must be done before
12821 * accessing any members (and before AutoUninitSpan that does it as well).
12822 * This self reference will be released as the very last step on return.
12823 */
12824 ComObjPtr<SessionMachine> selfRef;
12825 if (aReason != Uninit::Unexpected)
12826 selfRef = this;
12827
12828 /* Enclose the state transition Ready->InUninit->NotReady */
12829 AutoUninitSpan autoUninitSpan(this);
12830 if (autoUninitSpan.uninitDone())
12831 {
12832 LogFlowThisFunc(("Already uninitialized\n"));
12833 LogFlowThisFuncLeave();
12834 return;
12835 }
12836
12837 if (autoUninitSpan.initFailed())
12838 {
12839 /* We've been called by init() because it's failed. It's not really
12840 * necessary (nor it's safe) to perform the regular uninit sequence
12841 * below, the following is enough.
12842 */
12843 LogFlowThisFunc(("Initialization failed.\n"));
12844 /* destroy the machine client token */
12845 if (mClientToken)
12846 {
12847 delete mClientToken;
12848 mClientToken = NULL;
12849 }
12850 uninitDataAndChildObjects();
12851 mData.free();
12852 unconst(mParent) = NULL;
12853 unconst(mPeer) = NULL;
12854 LogFlowThisFuncLeave();
12855 return;
12856 }
12857
12858 MachineState_T lastState;
12859 {
12860 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12861 lastState = mData->mMachineState;
12862 }
12863 NOREF(lastState);
12864
12865#ifdef VBOX_WITH_USB
12866 // release all captured USB devices, but do this before requesting the locks below
12867 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12868 {
12869 /* Console::captureUSBDevices() is called in the VM process only after
12870 * setting the machine state to Starting or Restoring.
12871 * Console::detachAllUSBDevices() will be called upon successful
12872 * termination. So, we need to release USB devices only if there was
12873 * an abnormal termination of a running VM.
12874 *
12875 * This is identical to SessionMachine::DetachAllUSBDevices except
12876 * for the aAbnormal argument. */
12877 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12878 AssertComRC(rc);
12879 NOREF(rc);
12880
12881 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12882 if (service)
12883 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12884 }
12885#endif /* VBOX_WITH_USB */
12886
12887 // we need to lock this object in uninit() because the lock is shared
12888 // with mPeer (as well as data we modify below). mParent lock is needed
12889 // by several calls to it.
12890 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12891
12892#ifdef VBOX_WITH_RESOURCE_USAGE_API
12893 /*
12894 * It is safe to call Machine::i_unregisterMetrics() here because
12895 * PerformanceCollector::samplerCallback no longer accesses guest methods
12896 * holding the lock.
12897 */
12898 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12899 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12900 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12901 if (mCollectorGuest)
12902 {
12903 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12904 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12905 mCollectorGuest = NULL;
12906 }
12907#endif
12908
12909 if (aReason == Uninit::Abnormal)
12910 {
12911 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12912
12913 /* reset the state to Aborted */
12914 if (mData->mMachineState != MachineState_Aborted)
12915 i_setMachineState(MachineState_Aborted);
12916 }
12917
12918 // any machine settings modified?
12919 if (mData->flModifications)
12920 {
12921 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12922 i_rollback(false /* aNotify */);
12923 }
12924
12925 mData->mSession.mPID = NIL_RTPROCESS;
12926
12927 if (aReason == Uninit::Unexpected)
12928 {
12929 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12930 * client watcher thread to update the set of machines that have open
12931 * sessions. */
12932 mParent->i_updateClientWatcher();
12933 }
12934
12935 /* uninitialize all remote controls */
12936 if (mData->mSession.mRemoteControls.size())
12937 {
12938 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12939 mData->mSession.mRemoteControls.size()));
12940
12941 /* Always restart a the beginning, since the iterator is invalidated
12942 * by using erase(). */
12943 for (Data::Session::RemoteControlList::iterator
12944 it = mData->mSession.mRemoteControls.begin();
12945 it != mData->mSession.mRemoteControls.end();
12946 it = mData->mSession.mRemoteControls.begin())
12947 {
12948 ComPtr<IInternalSessionControl> pControl = *it;
12949 mData->mSession.mRemoteControls.erase(it);
12950 multilock.release();
12951 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12952 HRESULT rc = pControl->Uninitialize();
12953 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12954 if (FAILED(rc))
12955 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12956 multilock.acquire();
12957 }
12958 mData->mSession.mRemoteControls.clear();
12959 }
12960
12961 /* Remove all references to the NAT network service. The service will stop
12962 * if all references (also from other VMs) are removed. */
12963 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12964 {
12965 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12966 {
12967 BOOL enabled;
12968 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12969 if ( FAILED(hrc)
12970 || !enabled)
12971 continue;
12972
12973 NetworkAttachmentType_T type;
12974 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12975 if ( SUCCEEDED(hrc)
12976 && type == NetworkAttachmentType_NATNetwork)
12977 {
12978 Bstr name;
12979 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12980 if (SUCCEEDED(hrc))
12981 {
12982 multilock.release();
12983 Utf8Str strName(name);
12984 LogRel(("VM '%s' stops using NAT network '%s'\n",
12985 mUserData->s.strName.c_str(), strName.c_str()));
12986 mParent->i_natNetworkRefDec(strName);
12987 multilock.acquire();
12988 }
12989 }
12990 }
12991 }
12992
12993 /*
12994 * An expected uninitialization can come only from #i_checkForDeath().
12995 * Otherwise it means that something's gone really wrong (for example,
12996 * the Session implementation has released the VirtualBox reference
12997 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12998 * etc). However, it's also possible, that the client releases the IPC
12999 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13000 * but the VirtualBox release event comes first to the server process.
13001 * This case is practically possible, so we should not assert on an
13002 * unexpected uninit, just log a warning.
13003 */
13004
13005 if (aReason == Uninit::Unexpected)
13006 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13007
13008 if (aReason != Uninit::Normal)
13009 {
13010 mData->mSession.mDirectControl.setNull();
13011 }
13012 else
13013 {
13014 /* this must be null here (see #OnSessionEnd()) */
13015 Assert(mData->mSession.mDirectControl.isNull());
13016 Assert(mData->mSession.mState == SessionState_Unlocking);
13017 Assert(!mData->mSession.mProgress.isNull());
13018 }
13019 if (mData->mSession.mProgress)
13020 {
13021 if (aReason == Uninit::Normal)
13022 mData->mSession.mProgress->i_notifyComplete(S_OK);
13023 else
13024 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13025 COM_IIDOF(ISession),
13026 getComponentName(),
13027 tr("The VM session was aborted"));
13028 mData->mSession.mProgress.setNull();
13029 }
13030
13031 if (mConsoleTaskData.mProgress)
13032 {
13033 Assert(aReason == Uninit::Abnormal);
13034 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13035 COM_IIDOF(ISession),
13036 getComponentName(),
13037 tr("The VM session was aborted"));
13038 mConsoleTaskData.mProgress.setNull();
13039 }
13040
13041 /* remove the association between the peer machine and this session machine */
13042 Assert( (SessionMachine*)mData->mSession.mMachine == this
13043 || aReason == Uninit::Unexpected);
13044
13045 /* reset the rest of session data */
13046 mData->mSession.mLockType = LockType_Null;
13047 mData->mSession.mMachine.setNull();
13048 mData->mSession.mState = SessionState_Unlocked;
13049 mData->mSession.mName.setNull();
13050
13051 /* destroy the machine client token before leaving the exclusive lock */
13052 if (mClientToken)
13053 {
13054 delete mClientToken;
13055 mClientToken = NULL;
13056 }
13057
13058 /* fire an event */
13059 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13060
13061 uninitDataAndChildObjects();
13062
13063 /* free the essential data structure last */
13064 mData.free();
13065
13066 /* release the exclusive lock before setting the below two to NULL */
13067 multilock.release();
13068
13069 unconst(mParent) = NULL;
13070 unconst(mPeer) = NULL;
13071
13072 AuthLibUnload(&mAuthLibCtx);
13073
13074 LogFlowThisFuncLeave();
13075}
13076
13077// util::Lockable interface
13078////////////////////////////////////////////////////////////////////////////////
13079
13080/**
13081 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13082 * with the primary Machine instance (mPeer).
13083 */
13084RWLockHandle *SessionMachine::lockHandle() const
13085{
13086 AssertReturn(mPeer != NULL, NULL);
13087 return mPeer->lockHandle();
13088}
13089
13090// IInternalMachineControl methods
13091////////////////////////////////////////////////////////////////////////////////
13092
13093/**
13094 * Passes collected guest statistics to performance collector object
13095 */
13096HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13097 ULONG aCpuKernel, ULONG aCpuIdle,
13098 ULONG aMemTotal, ULONG aMemFree,
13099 ULONG aMemBalloon, ULONG aMemShared,
13100 ULONG aMemCache, ULONG aPageTotal,
13101 ULONG aAllocVMM, ULONG aFreeVMM,
13102 ULONG aBalloonedVMM, ULONG aSharedVMM,
13103 ULONG aVmNetRx, ULONG aVmNetTx)
13104{
13105#ifdef VBOX_WITH_RESOURCE_USAGE_API
13106 if (mCollectorGuest)
13107 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13108 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13109 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13110 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13111
13112 return S_OK;
13113#else
13114 NOREF(aValidStats);
13115 NOREF(aCpuUser);
13116 NOREF(aCpuKernel);
13117 NOREF(aCpuIdle);
13118 NOREF(aMemTotal);
13119 NOREF(aMemFree);
13120 NOREF(aMemBalloon);
13121 NOREF(aMemShared);
13122 NOREF(aMemCache);
13123 NOREF(aPageTotal);
13124 NOREF(aAllocVMM);
13125 NOREF(aFreeVMM);
13126 NOREF(aBalloonedVMM);
13127 NOREF(aSharedVMM);
13128 NOREF(aVmNetRx);
13129 NOREF(aVmNetTx);
13130 return E_NOTIMPL;
13131#endif
13132}
13133
13134////////////////////////////////////////////////////////////////////////////////
13135//
13136// SessionMachine task records
13137//
13138////////////////////////////////////////////////////////////////////////////////
13139
13140/**
13141 * Task record for saving the machine state.
13142 */
13143class SessionMachine::SaveStateTask
13144 : public Machine::Task
13145{
13146public:
13147 SaveStateTask(SessionMachine *m,
13148 Progress *p,
13149 const Utf8Str &t,
13150 Reason_T enmReason,
13151 const Utf8Str &strStateFilePath)
13152 : Task(m, p, t),
13153 m_enmReason(enmReason),
13154 m_strStateFilePath(strStateFilePath)
13155 {}
13156
13157private:
13158 void handler()
13159 {
13160 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13161 }
13162
13163 Reason_T m_enmReason;
13164 Utf8Str m_strStateFilePath;
13165
13166 friend class SessionMachine;
13167};
13168
13169/**
13170 * Task thread implementation for SessionMachine::SaveState(), called from
13171 * SessionMachine::taskHandler().
13172 *
13173 * @note Locks this object for writing.
13174 *
13175 * @param task
13176 * @return
13177 */
13178void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13179{
13180 LogFlowThisFuncEnter();
13181
13182 AutoCaller autoCaller(this);
13183 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13184 if (FAILED(autoCaller.rc()))
13185 {
13186 /* we might have been uninitialized because the session was accidentally
13187 * closed by the client, so don't assert */
13188 HRESULT rc = setError(E_FAIL,
13189 tr("The session has been accidentally closed"));
13190 task.m_pProgress->i_notifyComplete(rc);
13191 LogFlowThisFuncLeave();
13192 return;
13193 }
13194
13195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13196
13197 HRESULT rc = S_OK;
13198
13199 try
13200 {
13201 ComPtr<IInternalSessionControl> directControl;
13202 if (mData->mSession.mLockType == LockType_VM)
13203 directControl = mData->mSession.mDirectControl;
13204 if (directControl.isNull())
13205 throw setError(VBOX_E_INVALID_VM_STATE,
13206 tr("Trying to save state without a running VM"));
13207 alock.release();
13208 BOOL fSuspendedBySave;
13209 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13210 Assert(!fSuspendedBySave);
13211 alock.acquire();
13212
13213 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13214 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13215 throw E_FAIL);
13216
13217 if (SUCCEEDED(rc))
13218 {
13219 mSSData->strStateFilePath = task.m_strStateFilePath;
13220
13221 /* save all VM settings */
13222 rc = i_saveSettings(NULL);
13223 // no need to check whether VirtualBox.xml needs saving also since
13224 // we can't have a name change pending at this point
13225 }
13226 else
13227 {
13228 // On failure, set the state to the state we had at the beginning.
13229 i_setMachineState(task.m_machineStateBackup);
13230 i_updateMachineStateOnClient();
13231
13232 // Delete the saved state file (might have been already created).
13233 // No need to check whether this is shared with a snapshot here
13234 // because we certainly created a fresh saved state file here.
13235 RTFileDelete(task.m_strStateFilePath.c_str());
13236 }
13237 }
13238 catch (HRESULT aRC) { rc = aRC; }
13239
13240 task.m_pProgress->i_notifyComplete(rc);
13241
13242 LogFlowThisFuncLeave();
13243}
13244
13245/**
13246 * @note Locks this object for writing.
13247 */
13248HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13249{
13250 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13251}
13252
13253HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13254{
13255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13256
13257 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13258 if (FAILED(rc)) return rc;
13259
13260 if ( mData->mMachineState != MachineState_Running
13261 && mData->mMachineState != MachineState_Paused
13262 )
13263 return setError(VBOX_E_INVALID_VM_STATE,
13264 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13265 Global::stringifyMachineState(mData->mMachineState));
13266
13267 ComObjPtr<Progress> pProgress;
13268 pProgress.createObject();
13269 rc = pProgress->init(i_getVirtualBox(),
13270 static_cast<IMachine *>(this) /* aInitiator */,
13271 tr("Saving the execution state of the virtual machine"),
13272 FALSE /* aCancelable */);
13273 if (FAILED(rc))
13274 return rc;
13275
13276 Utf8Str strStateFilePath;
13277 i_composeSavedStateFilename(strStateFilePath);
13278
13279 /* create and start the task on a separate thread (note that it will not
13280 * start working until we release alock) */
13281 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13282 rc = pTask->createThread();
13283 if (FAILED(rc))
13284 return rc;
13285
13286 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13287 i_setMachineState(MachineState_Saving);
13288 i_updateMachineStateOnClient();
13289
13290 pProgress.queryInterfaceTo(aProgress.asOutParam());
13291
13292 return S_OK;
13293}
13294
13295/**
13296 * @note Locks this object for writing.
13297 */
13298HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13299{
13300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13301
13302 HRESULT rc = i_checkStateDependency(MutableStateDep);
13303 if (FAILED(rc)) return rc;
13304
13305 if ( mData->mMachineState != MachineState_PoweredOff
13306 && mData->mMachineState != MachineState_Teleported
13307 && mData->mMachineState != MachineState_Aborted
13308 )
13309 return setError(VBOX_E_INVALID_VM_STATE,
13310 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13311 Global::stringifyMachineState(mData->mMachineState));
13312
13313 com::Utf8Str stateFilePathFull;
13314 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13315 if (RT_FAILURE(vrc))
13316 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13317 tr("Invalid saved state file path '%s' (%Rrc)"),
13318 aSavedStateFile.c_str(),
13319 vrc);
13320
13321 mSSData->strStateFilePath = stateFilePathFull;
13322
13323 /* The below i_setMachineState() will detect the state transition and will
13324 * update the settings file */
13325
13326 return i_setMachineState(MachineState_Saved);
13327}
13328
13329/**
13330 * @note Locks this object for writing.
13331 */
13332HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13333{
13334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13335
13336 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13337 if (FAILED(rc)) return rc;
13338
13339 if (mData->mMachineState != MachineState_Saved)
13340 return setError(VBOX_E_INVALID_VM_STATE,
13341 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13342 Global::stringifyMachineState(mData->mMachineState));
13343
13344 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13345
13346 /*
13347 * Saved -> PoweredOff transition will be detected in the SessionMachine
13348 * and properly handled.
13349 */
13350 rc = i_setMachineState(MachineState_PoweredOff);
13351 return rc;
13352}
13353
13354
13355/**
13356 * @note Locks the same as #i_setMachineState() does.
13357 */
13358HRESULT SessionMachine::updateState(MachineState_T aState)
13359{
13360 return i_setMachineState(aState);
13361}
13362
13363/**
13364 * @note Locks this object for writing.
13365 */
13366HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13367{
13368 IProgress *pProgress(aProgress);
13369
13370 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13371
13372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13373
13374 if (mData->mSession.mState != SessionState_Locked)
13375 return VBOX_E_INVALID_OBJECT_STATE;
13376
13377 if (!mData->mSession.mProgress.isNull())
13378 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13379
13380 /* If we didn't reference the NAT network service yet, add a reference to
13381 * force a start */
13382 if (miNATNetworksStarted < 1)
13383 {
13384 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13385 {
13386 BOOL enabled;
13387 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13388 if ( FAILED(hrc)
13389 || !enabled)
13390 continue;
13391
13392 NetworkAttachmentType_T type;
13393 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13394 if ( SUCCEEDED(hrc)
13395 && type == NetworkAttachmentType_NATNetwork)
13396 {
13397 Bstr name;
13398 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13399 if (SUCCEEDED(hrc))
13400 {
13401 Utf8Str strName(name);
13402 LogRel(("VM '%s' starts using NAT network '%s'\n",
13403 mUserData->s.strName.c_str(), strName.c_str()));
13404 mPeer->lockHandle()->unlockWrite();
13405 mParent->i_natNetworkRefInc(strName);
13406#ifdef RT_LOCK_STRICT
13407 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13408#else
13409 mPeer->lockHandle()->lockWrite();
13410#endif
13411 }
13412 }
13413 }
13414 miNATNetworksStarted++;
13415 }
13416
13417 LogFlowThisFunc(("returns S_OK.\n"));
13418 return S_OK;
13419}
13420
13421/**
13422 * @note Locks this object for writing.
13423 */
13424HRESULT SessionMachine::endPowerUp(LONG aResult)
13425{
13426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13427
13428 if (mData->mSession.mState != SessionState_Locked)
13429 return VBOX_E_INVALID_OBJECT_STATE;
13430
13431 /* Finalize the LaunchVMProcess progress object. */
13432 if (mData->mSession.mProgress)
13433 {
13434 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13435 mData->mSession.mProgress.setNull();
13436 }
13437
13438 if (SUCCEEDED((HRESULT)aResult))
13439 {
13440#ifdef VBOX_WITH_RESOURCE_USAGE_API
13441 /* The VM has been powered up successfully, so it makes sense
13442 * now to offer the performance metrics for a running machine
13443 * object. Doing it earlier wouldn't be safe. */
13444 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13445 mData->mSession.mPID);
13446#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13447 }
13448
13449 return S_OK;
13450}
13451
13452/**
13453 * @note Locks this object for writing.
13454 */
13455HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13456{
13457 LogFlowThisFuncEnter();
13458
13459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13460
13461 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13462 E_FAIL);
13463
13464 /* create a progress object to track operation completion */
13465 ComObjPtr<Progress> pProgress;
13466 pProgress.createObject();
13467 pProgress->init(i_getVirtualBox(),
13468 static_cast<IMachine *>(this) /* aInitiator */,
13469 tr("Stopping the virtual machine"),
13470 FALSE /* aCancelable */);
13471
13472 /* fill in the console task data */
13473 mConsoleTaskData.mLastState = mData->mMachineState;
13474 mConsoleTaskData.mProgress = pProgress;
13475
13476 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13477 i_setMachineState(MachineState_Stopping);
13478
13479 pProgress.queryInterfaceTo(aProgress.asOutParam());
13480
13481 return S_OK;
13482}
13483
13484/**
13485 * @note Locks this object for writing.
13486 */
13487HRESULT SessionMachine::endPoweringDown(LONG aResult,
13488 const com::Utf8Str &aErrMsg)
13489{
13490 LogFlowThisFuncEnter();
13491
13492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13493
13494 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13495 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13496 && mConsoleTaskData.mLastState != MachineState_Null,
13497 E_FAIL);
13498
13499 /*
13500 * On failure, set the state to the state we had when BeginPoweringDown()
13501 * was called (this is expected by Console::PowerDown() and the associated
13502 * task). On success the VM process already changed the state to
13503 * MachineState_PoweredOff, so no need to do anything.
13504 */
13505 if (FAILED(aResult))
13506 i_setMachineState(mConsoleTaskData.mLastState);
13507
13508 /* notify the progress object about operation completion */
13509 Assert(mConsoleTaskData.mProgress);
13510 if (SUCCEEDED(aResult))
13511 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13512 else
13513 {
13514 if (aErrMsg.length())
13515 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13516 COM_IIDOF(ISession),
13517 getComponentName(),
13518 aErrMsg.c_str());
13519 else
13520 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13521 }
13522
13523 /* clear out the temporary saved state data */
13524 mConsoleTaskData.mLastState = MachineState_Null;
13525 mConsoleTaskData.mProgress.setNull();
13526
13527 LogFlowThisFuncLeave();
13528 return S_OK;
13529}
13530
13531
13532/**
13533 * Goes through the USB filters of the given machine to see if the given
13534 * device matches any filter or not.
13535 *
13536 * @note Locks the same as USBController::hasMatchingFilter() does.
13537 */
13538HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13539 BOOL *aMatched,
13540 ULONG *aMaskedInterfaces)
13541{
13542 LogFlowThisFunc(("\n"));
13543
13544#ifdef VBOX_WITH_USB
13545 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13546#else
13547 NOREF(aDevice);
13548 NOREF(aMaskedInterfaces);
13549 *aMatched = FALSE;
13550#endif
13551
13552 return S_OK;
13553}
13554
13555/**
13556 * @note Locks the same as Host::captureUSBDevice() does.
13557 */
13558HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13559{
13560 LogFlowThisFunc(("\n"));
13561
13562#ifdef VBOX_WITH_USB
13563 /* if captureDeviceForVM() fails, it must have set extended error info */
13564 clearError();
13565 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13566 if (FAILED(rc)) return rc;
13567
13568 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13569 AssertReturn(service, E_FAIL);
13570 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13571#else
13572 NOREF(aId);
13573 return E_NOTIMPL;
13574#endif
13575}
13576
13577/**
13578 * @note Locks the same as Host::detachUSBDevice() does.
13579 */
13580HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13581 BOOL aDone)
13582{
13583 LogFlowThisFunc(("\n"));
13584
13585#ifdef VBOX_WITH_USB
13586 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13587 AssertReturn(service, E_FAIL);
13588 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13589#else
13590 NOREF(aId);
13591 NOREF(aDone);
13592 return E_NOTIMPL;
13593#endif
13594}
13595
13596/**
13597 * Inserts all machine filters to the USB proxy service and then calls
13598 * Host::autoCaptureUSBDevices().
13599 *
13600 * Called by Console from the VM process upon VM startup.
13601 *
13602 * @note Locks what called methods lock.
13603 */
13604HRESULT SessionMachine::autoCaptureUSBDevices()
13605{
13606 LogFlowThisFunc(("\n"));
13607
13608#ifdef VBOX_WITH_USB
13609 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13610 AssertComRC(rc);
13611 NOREF(rc);
13612
13613 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13614 AssertReturn(service, E_FAIL);
13615 return service->autoCaptureDevicesForVM(this);
13616#else
13617 return S_OK;
13618#endif
13619}
13620
13621/**
13622 * Removes all machine filters from the USB proxy service and then calls
13623 * Host::detachAllUSBDevices().
13624 *
13625 * Called by Console from the VM process upon normal VM termination or by
13626 * SessionMachine::uninit() upon abnormal VM termination (from under the
13627 * Machine/SessionMachine lock).
13628 *
13629 * @note Locks what called methods lock.
13630 */
13631HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13632{
13633 LogFlowThisFunc(("\n"));
13634
13635#ifdef VBOX_WITH_USB
13636 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13637 AssertComRC(rc);
13638 NOREF(rc);
13639
13640 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13641 AssertReturn(service, E_FAIL);
13642 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13643#else
13644 NOREF(aDone);
13645 return S_OK;
13646#endif
13647}
13648
13649/**
13650 * @note Locks this object for writing.
13651 */
13652HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13653 ComPtr<IProgress> &aProgress)
13654{
13655 LogFlowThisFuncEnter();
13656
13657 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13658 /*
13659 * We don't assert below because it might happen that a non-direct session
13660 * informs us it is closed right after we've been uninitialized -- it's ok.
13661 */
13662
13663 /* get IInternalSessionControl interface */
13664 ComPtr<IInternalSessionControl> control(aSession);
13665
13666 ComAssertRet(!control.isNull(), E_INVALIDARG);
13667
13668 /* Creating a Progress object requires the VirtualBox lock, and
13669 * thus locking it here is required by the lock order rules. */
13670 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13671
13672 if (control == mData->mSession.mDirectControl)
13673 {
13674 /* The direct session is being normally closed by the client process
13675 * ----------------------------------------------------------------- */
13676
13677 /* go to the closing state (essential for all open*Session() calls and
13678 * for #i_checkForDeath()) */
13679 Assert(mData->mSession.mState == SessionState_Locked);
13680 mData->mSession.mState = SessionState_Unlocking;
13681
13682 /* set direct control to NULL to release the remote instance */
13683 mData->mSession.mDirectControl.setNull();
13684 LogFlowThisFunc(("Direct control is set to NULL\n"));
13685
13686 if (mData->mSession.mProgress)
13687 {
13688 /* finalize the progress, someone might wait if a frontend
13689 * closes the session before powering on the VM. */
13690 mData->mSession.mProgress->notifyComplete(E_FAIL,
13691 COM_IIDOF(ISession),
13692 getComponentName(),
13693 tr("The VM session was closed before any attempt to power it on"));
13694 mData->mSession.mProgress.setNull();
13695 }
13696
13697 /* Create the progress object the client will use to wait until
13698 * #i_checkForDeath() is called to uninitialize this session object after
13699 * it releases the IPC semaphore.
13700 * Note! Because we're "reusing" mProgress here, this must be a proxy
13701 * object just like for LaunchVMProcess. */
13702 Assert(mData->mSession.mProgress.isNull());
13703 ComObjPtr<ProgressProxy> progress;
13704 progress.createObject();
13705 ComPtr<IUnknown> pPeer(mPeer);
13706 progress->init(mParent, pPeer,
13707 Bstr(tr("Closing session")).raw(),
13708 FALSE /* aCancelable */);
13709 progress.queryInterfaceTo(aProgress.asOutParam());
13710 mData->mSession.mProgress = progress;
13711 }
13712 else
13713 {
13714 /* the remote session is being normally closed */
13715 bool found = false;
13716 for (Data::Session::RemoteControlList::iterator
13717 it = mData->mSession.mRemoteControls.begin();
13718 it != mData->mSession.mRemoteControls.end();
13719 ++it)
13720 {
13721 if (control == *it)
13722 {
13723 found = true;
13724 // This MUST be erase(it), not remove(*it) as the latter
13725 // triggers a very nasty use after free due to the place where
13726 // the value "lives".
13727 mData->mSession.mRemoteControls.erase(it);
13728 break;
13729 }
13730 }
13731 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13732 E_INVALIDARG);
13733 }
13734
13735 /* signal the client watcher thread, because the client is going away */
13736 mParent->i_updateClientWatcher();
13737
13738 LogFlowThisFuncLeave();
13739 return S_OK;
13740}
13741
13742HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13743 std::vector<com::Utf8Str> &aValues,
13744 std::vector<LONG64> &aTimestamps,
13745 std::vector<com::Utf8Str> &aFlags)
13746{
13747 LogFlowThisFunc(("\n"));
13748
13749#ifdef VBOX_WITH_GUEST_PROPS
13750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13751
13752 size_t cEntries = mHWData->mGuestProperties.size();
13753 aNames.resize(cEntries);
13754 aValues.resize(cEntries);
13755 aTimestamps.resize(cEntries);
13756 aFlags.resize(cEntries);
13757
13758 size_t i = 0;
13759 for (HWData::GuestPropertyMap::const_iterator
13760 it = mHWData->mGuestProperties.begin();
13761 it != mHWData->mGuestProperties.end();
13762 ++it, ++i)
13763 {
13764 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13765 aNames[i] = it->first;
13766 aValues[i] = it->second.strValue;
13767 aTimestamps[i] = it->second.mTimestamp;
13768
13769 /* If it is NULL, keep it NULL. */
13770 if (it->second.mFlags)
13771 {
13772 GuestPropWriteFlags(it->second.mFlags, szFlags);
13773 aFlags[i] = szFlags;
13774 }
13775 else
13776 aFlags[i] = "";
13777 }
13778 return S_OK;
13779#else
13780 ReturnComNotImplemented();
13781#endif
13782}
13783
13784HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13785 const com::Utf8Str &aValue,
13786 LONG64 aTimestamp,
13787 const com::Utf8Str &aFlags)
13788{
13789 LogFlowThisFunc(("\n"));
13790
13791#ifdef VBOX_WITH_GUEST_PROPS
13792 try
13793 {
13794 /*
13795 * Convert input up front.
13796 */
13797 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13798 if (aFlags.length())
13799 {
13800 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13801 AssertRCReturn(vrc, E_INVALIDARG);
13802 }
13803
13804 /*
13805 * Now grab the object lock, validate the state and do the update.
13806 */
13807
13808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13809
13810 if (!Global::IsOnline(mData->mMachineState))
13811 {
13812 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13813 VBOX_E_INVALID_VM_STATE);
13814 }
13815
13816 i_setModified(IsModified_MachineData);
13817 mHWData.backup();
13818
13819 bool fDelete = !aValue.length();
13820 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13821 if (it != mHWData->mGuestProperties.end())
13822 {
13823 if (!fDelete)
13824 {
13825 it->second.strValue = aValue;
13826 it->second.mTimestamp = aTimestamp;
13827 it->second.mFlags = fFlags;
13828 }
13829 else
13830 mHWData->mGuestProperties.erase(it);
13831
13832 mData->mGuestPropertiesModified = TRUE;
13833 }
13834 else if (!fDelete)
13835 {
13836 HWData::GuestProperty prop;
13837 prop.strValue = aValue;
13838 prop.mTimestamp = aTimestamp;
13839 prop.mFlags = fFlags;
13840
13841 mHWData->mGuestProperties[aName] = prop;
13842 mData->mGuestPropertiesModified = TRUE;
13843 }
13844
13845 alock.release();
13846
13847 mParent->i_onGuestPropertyChange(mData->mUuid,
13848 Bstr(aName).raw(),
13849 Bstr(aValue).raw(),
13850 Bstr(aFlags).raw());
13851 }
13852 catch (...)
13853 {
13854 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13855 }
13856 return S_OK;
13857#else
13858 ReturnComNotImplemented();
13859#endif
13860}
13861
13862
13863HRESULT SessionMachine::lockMedia()
13864{
13865 AutoMultiWriteLock2 alock(this->lockHandle(),
13866 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13867
13868 AssertReturn( mData->mMachineState == MachineState_Starting
13869 || mData->mMachineState == MachineState_Restoring
13870 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13871
13872 clearError();
13873 alock.release();
13874 return i_lockMedia();
13875}
13876
13877HRESULT SessionMachine::unlockMedia()
13878{
13879 HRESULT hrc = i_unlockMedia();
13880 return hrc;
13881}
13882
13883HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13884 ComPtr<IMediumAttachment> &aNewAttachment)
13885{
13886 // request the host lock first, since might be calling Host methods for getting host drives;
13887 // next, protect the media tree all the while we're in here, as well as our member variables
13888 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13889 this->lockHandle(),
13890 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13891
13892 IMediumAttachment *iAttach = aAttachment;
13893 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13894
13895 Utf8Str ctrlName;
13896 LONG lPort;
13897 LONG lDevice;
13898 bool fTempEject;
13899 {
13900 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13901
13902 /* Need to query the details first, as the IMediumAttachment reference
13903 * might be to the original settings, which we are going to change. */
13904 ctrlName = pAttach->i_getControllerName();
13905 lPort = pAttach->i_getPort();
13906 lDevice = pAttach->i_getDevice();
13907 fTempEject = pAttach->i_getTempEject();
13908 }
13909
13910 if (!fTempEject)
13911 {
13912 /* Remember previously mounted medium. The medium before taking the
13913 * backup is not necessarily the same thing. */
13914 ComObjPtr<Medium> oldmedium;
13915 oldmedium = pAttach->i_getMedium();
13916
13917 i_setModified(IsModified_Storage);
13918 mMediumAttachments.backup();
13919
13920 // The backup operation makes the pAttach reference point to the
13921 // old settings. Re-get the correct reference.
13922 pAttach = i_findAttachment(*mMediumAttachments.data(),
13923 ctrlName,
13924 lPort,
13925 lDevice);
13926
13927 {
13928 AutoCaller autoAttachCaller(this);
13929 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13930
13931 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13932 if (!oldmedium.isNull())
13933 oldmedium->i_removeBackReference(mData->mUuid);
13934
13935 pAttach->i_updateMedium(NULL);
13936 pAttach->i_updateEjected();
13937 }
13938
13939 i_setModified(IsModified_Storage);
13940 }
13941 else
13942 {
13943 {
13944 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13945 pAttach->i_updateEjected();
13946 }
13947 }
13948
13949 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13950
13951 return S_OK;
13952}
13953
13954HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13955 com::Utf8Str &aResult)
13956{
13957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13958
13959 HRESULT hr = S_OK;
13960
13961 if (!mAuthLibCtx.hAuthLibrary)
13962 {
13963 /* Load the external authentication library. */
13964 Bstr authLibrary;
13965 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13966
13967 Utf8Str filename = authLibrary;
13968
13969 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13970 if (RT_FAILURE(vrc))
13971 hr = setErrorBoth(E_FAIL, vrc,
13972 tr("Could not load the external authentication library '%s' (%Rrc)"),
13973 filename.c_str(), vrc);
13974 }
13975
13976 /* The auth library might need the machine lock. */
13977 alock.release();
13978
13979 if (FAILED(hr))
13980 return hr;
13981
13982 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13983 {
13984 enum VRDEAuthParams
13985 {
13986 parmUuid = 1,
13987 parmGuestJudgement,
13988 parmUser,
13989 parmPassword,
13990 parmDomain,
13991 parmClientId
13992 };
13993
13994 AuthResult result = AuthResultAccessDenied;
13995
13996 Guid uuid(aAuthParams[parmUuid]);
13997 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13998 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13999
14000 result = AuthLibAuthenticate(&mAuthLibCtx,
14001 uuid.raw(), guestJudgement,
14002 aAuthParams[parmUser].c_str(),
14003 aAuthParams[parmPassword].c_str(),
14004 aAuthParams[parmDomain].c_str(),
14005 u32ClientId);
14006
14007 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
14008 size_t cbPassword = aAuthParams[parmPassword].length();
14009 if (cbPassword)
14010 {
14011 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14012 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14013 }
14014
14015 if (result == AuthResultAccessGranted)
14016 aResult = "granted";
14017 else
14018 aResult = "denied";
14019
14020 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14021 aAuthParams[parmUser].c_str(), aResult.c_str()));
14022 }
14023 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14024 {
14025 enum VRDEAuthDisconnectParams
14026 {
14027 parmUuid = 1,
14028 parmClientId
14029 };
14030
14031 Guid uuid(aAuthParams[parmUuid]);
14032 uint32_t u32ClientId = 0;
14033 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14034 }
14035 else
14036 {
14037 hr = E_INVALIDARG;
14038 }
14039
14040 return hr;
14041}
14042
14043// public methods only for internal purposes
14044/////////////////////////////////////////////////////////////////////////////
14045
14046#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14047/**
14048 * Called from the client watcher thread to check for expected or unexpected
14049 * death of the client process that has a direct session to this machine.
14050 *
14051 * On Win32 and on OS/2, this method is called only when we've got the
14052 * mutex (i.e. the client has either died or terminated normally) so it always
14053 * returns @c true (the client is terminated, the session machine is
14054 * uninitialized).
14055 *
14056 * On other platforms, the method returns @c true if the client process has
14057 * terminated normally or abnormally and the session machine was uninitialized,
14058 * and @c false if the client process is still alive.
14059 *
14060 * @note Locks this object for writing.
14061 */
14062bool SessionMachine::i_checkForDeath()
14063{
14064 Uninit::Reason reason;
14065 bool terminated = false;
14066
14067 /* Enclose autoCaller with a block because calling uninit() from under it
14068 * will deadlock. */
14069 {
14070 AutoCaller autoCaller(this);
14071 if (!autoCaller.isOk())
14072 {
14073 /* return true if not ready, to cause the client watcher to exclude
14074 * the corresponding session from watching */
14075 LogFlowThisFunc(("Already uninitialized!\n"));
14076 return true;
14077 }
14078
14079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14080
14081 /* Determine the reason of death: if the session state is Closing here,
14082 * everything is fine. Otherwise it means that the client did not call
14083 * OnSessionEnd() before it released the IPC semaphore. This may happen
14084 * either because the client process has abnormally terminated, or
14085 * because it simply forgot to call ISession::Close() before exiting. We
14086 * threat the latter also as an abnormal termination (see
14087 * Session::uninit() for details). */
14088 reason = mData->mSession.mState == SessionState_Unlocking ?
14089 Uninit::Normal :
14090 Uninit::Abnormal;
14091
14092 if (mClientToken)
14093 terminated = mClientToken->release();
14094 } /* AutoCaller block */
14095
14096 if (terminated)
14097 uninit(reason);
14098
14099 return terminated;
14100}
14101
14102void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14103{
14104 LogFlowThisFunc(("\n"));
14105
14106 strTokenId.setNull();
14107
14108 AutoCaller autoCaller(this);
14109 AssertComRCReturnVoid(autoCaller.rc());
14110
14111 Assert(mClientToken);
14112 if (mClientToken)
14113 mClientToken->getId(strTokenId);
14114}
14115#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14116IToken *SessionMachine::i_getToken()
14117{
14118 LogFlowThisFunc(("\n"));
14119
14120 AutoCaller autoCaller(this);
14121 AssertComRCReturn(autoCaller.rc(), NULL);
14122
14123 Assert(mClientToken);
14124 if (mClientToken)
14125 return mClientToken->getToken();
14126 else
14127 return NULL;
14128}
14129#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14130
14131Machine::ClientToken *SessionMachine::i_getClientToken()
14132{
14133 LogFlowThisFunc(("\n"));
14134
14135 AutoCaller autoCaller(this);
14136 AssertComRCReturn(autoCaller.rc(), NULL);
14137
14138 return mClientToken;
14139}
14140
14141
14142/**
14143 * @note Locks this object for reading.
14144 */
14145HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14146{
14147 LogFlowThisFunc(("\n"));
14148
14149 AutoCaller autoCaller(this);
14150 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14151
14152 ComPtr<IInternalSessionControl> directControl;
14153 {
14154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14155 if (mData->mSession.mLockType == LockType_VM)
14156 directControl = mData->mSession.mDirectControl;
14157 }
14158
14159 /* ignore notifications sent after #OnSessionEnd() is called */
14160 if (!directControl)
14161 return S_OK;
14162
14163 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14164}
14165
14166/**
14167 * @note Locks this object for reading.
14168 */
14169HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14170 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14171 IN_BSTR aGuestIp, LONG aGuestPort)
14172{
14173 LogFlowThisFunc(("\n"));
14174
14175 AutoCaller autoCaller(this);
14176 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14177
14178 ComPtr<IInternalSessionControl> directControl;
14179 {
14180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14181 if (mData->mSession.mLockType == LockType_VM)
14182 directControl = mData->mSession.mDirectControl;
14183 }
14184
14185 /* ignore notifications sent after #OnSessionEnd() is called */
14186 if (!directControl)
14187 return S_OK;
14188 /*
14189 * instead acting like callback we ask IVirtualBox deliver corresponding event
14190 */
14191
14192 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14193 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14194 return S_OK;
14195}
14196
14197/**
14198 * @note Locks this object for reading.
14199 */
14200HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14201{
14202 LogFlowThisFunc(("\n"));
14203
14204 AutoCaller autoCaller(this);
14205 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14206
14207 ComPtr<IInternalSessionControl> directControl;
14208 {
14209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14210 if (mData->mSession.mLockType == LockType_VM)
14211 directControl = mData->mSession.mDirectControl;
14212 }
14213
14214 /* ignore notifications sent after #OnSessionEnd() is called */
14215 if (!directControl)
14216 return S_OK;
14217
14218 return directControl->OnAudioAdapterChange(audioAdapter);
14219}
14220
14221/**
14222 * @note Locks this object for reading.
14223 */
14224HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14225{
14226 LogFlowThisFunc(("\n"));
14227
14228 AutoCaller autoCaller(this);
14229 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14230
14231 ComPtr<IInternalSessionControl> directControl;
14232 {
14233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14234 if (mData->mSession.mLockType == LockType_VM)
14235 directControl = mData->mSession.mDirectControl;
14236 }
14237
14238 /* ignore notifications sent after #OnSessionEnd() is called */
14239 if (!directControl)
14240 return S_OK;
14241
14242 return directControl->OnSerialPortChange(serialPort);
14243}
14244
14245/**
14246 * @note Locks this object for reading.
14247 */
14248HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14249{
14250 LogFlowThisFunc(("\n"));
14251
14252 AutoCaller autoCaller(this);
14253 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14254
14255 ComPtr<IInternalSessionControl> directControl;
14256 {
14257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14258 if (mData->mSession.mLockType == LockType_VM)
14259 directControl = mData->mSession.mDirectControl;
14260 }
14261
14262 /* ignore notifications sent after #OnSessionEnd() is called */
14263 if (!directControl)
14264 return S_OK;
14265
14266 return directControl->OnParallelPortChange(parallelPort);
14267}
14268
14269/**
14270 * @note Locks this object for reading.
14271 */
14272HRESULT SessionMachine::i_onStorageControllerChange()
14273{
14274 LogFlowThisFunc(("\n"));
14275
14276 AutoCaller autoCaller(this);
14277 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14278
14279 ComPtr<IInternalSessionControl> directControl;
14280 {
14281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14282 if (mData->mSession.mLockType == LockType_VM)
14283 directControl = mData->mSession.mDirectControl;
14284 }
14285
14286 /* ignore notifications sent after #OnSessionEnd() is called */
14287 if (!directControl)
14288 return S_OK;
14289
14290 return directControl->OnStorageControllerChange();
14291}
14292
14293/**
14294 * @note Locks this object for reading.
14295 */
14296HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14297{
14298 LogFlowThisFunc(("\n"));
14299
14300 AutoCaller autoCaller(this);
14301 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14302
14303 ComPtr<IInternalSessionControl> directControl;
14304 {
14305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14306 if (mData->mSession.mLockType == LockType_VM)
14307 directControl = mData->mSession.mDirectControl;
14308 }
14309
14310 /* ignore notifications sent after #OnSessionEnd() is called */
14311 if (!directControl)
14312 return S_OK;
14313
14314 return directControl->OnMediumChange(aAttachment, aForce);
14315}
14316
14317/**
14318 * @note Locks this object for reading.
14319 */
14320HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14321{
14322 LogFlowThisFunc(("\n"));
14323
14324 AutoCaller autoCaller(this);
14325 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14326
14327 ComPtr<IInternalSessionControl> directControl;
14328 {
14329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14330 if (mData->mSession.mLockType == LockType_VM)
14331 directControl = mData->mSession.mDirectControl;
14332 }
14333
14334 /* ignore notifications sent after #OnSessionEnd() is called */
14335 if (!directControl)
14336 return S_OK;
14337
14338 return directControl->OnCPUChange(aCPU, aRemove);
14339}
14340
14341HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14342{
14343 LogFlowThisFunc(("\n"));
14344
14345 AutoCaller autoCaller(this);
14346 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14347
14348 ComPtr<IInternalSessionControl> directControl;
14349 {
14350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14351 if (mData->mSession.mLockType == LockType_VM)
14352 directControl = mData->mSession.mDirectControl;
14353 }
14354
14355 /* ignore notifications sent after #OnSessionEnd() is called */
14356 if (!directControl)
14357 return S_OK;
14358
14359 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14360}
14361
14362/**
14363 * @note Locks this object for reading.
14364 */
14365HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14366{
14367 LogFlowThisFunc(("\n"));
14368
14369 AutoCaller autoCaller(this);
14370 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14371
14372 ComPtr<IInternalSessionControl> directControl;
14373 {
14374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14375 if (mData->mSession.mLockType == LockType_VM)
14376 directControl = mData->mSession.mDirectControl;
14377 }
14378
14379 /* ignore notifications sent after #OnSessionEnd() is called */
14380 if (!directControl)
14381 return S_OK;
14382
14383 return directControl->OnVRDEServerChange(aRestart);
14384}
14385
14386/**
14387 * @note Locks this object for reading.
14388 */
14389HRESULT SessionMachine::i_onVideoCaptureChange()
14390{
14391 LogFlowThisFunc(("\n"));
14392
14393 AutoCaller autoCaller(this);
14394 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14395
14396 ComPtr<IInternalSessionControl> directControl;
14397 {
14398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14399 if (mData->mSession.mLockType == LockType_VM)
14400 directControl = mData->mSession.mDirectControl;
14401 }
14402
14403 /* ignore notifications sent after #OnSessionEnd() is called */
14404 if (!directControl)
14405 return S_OK;
14406
14407 return directControl->OnVideoCaptureChange();
14408}
14409
14410/**
14411 * @note Locks this object for reading.
14412 */
14413HRESULT SessionMachine::i_onUSBControllerChange()
14414{
14415 LogFlowThisFunc(("\n"));
14416
14417 AutoCaller autoCaller(this);
14418 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14419
14420 ComPtr<IInternalSessionControl> directControl;
14421 {
14422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14423 if (mData->mSession.mLockType == LockType_VM)
14424 directControl = mData->mSession.mDirectControl;
14425 }
14426
14427 /* ignore notifications sent after #OnSessionEnd() is called */
14428 if (!directControl)
14429 return S_OK;
14430
14431 return directControl->OnUSBControllerChange();
14432}
14433
14434/**
14435 * @note Locks this object for reading.
14436 */
14437HRESULT SessionMachine::i_onSharedFolderChange()
14438{
14439 LogFlowThisFunc(("\n"));
14440
14441 AutoCaller autoCaller(this);
14442 AssertComRCReturnRC(autoCaller.rc());
14443
14444 ComPtr<IInternalSessionControl> directControl;
14445 {
14446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14447 if (mData->mSession.mLockType == LockType_VM)
14448 directControl = mData->mSession.mDirectControl;
14449 }
14450
14451 /* ignore notifications sent after #OnSessionEnd() is called */
14452 if (!directControl)
14453 return S_OK;
14454
14455 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14456}
14457
14458/**
14459 * @note Locks this object for reading.
14460 */
14461HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14462{
14463 LogFlowThisFunc(("\n"));
14464
14465 AutoCaller autoCaller(this);
14466 AssertComRCReturnRC(autoCaller.rc());
14467
14468 ComPtr<IInternalSessionControl> directControl;
14469 {
14470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14471 if (mData->mSession.mLockType == LockType_VM)
14472 directControl = mData->mSession.mDirectControl;
14473 }
14474
14475 /* ignore notifications sent after #OnSessionEnd() is called */
14476 if (!directControl)
14477 return S_OK;
14478
14479 return directControl->OnClipboardModeChange(aClipboardMode);
14480}
14481
14482/**
14483 * @note Locks this object for reading.
14484 */
14485HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14486{
14487 LogFlowThisFunc(("\n"));
14488
14489 AutoCaller autoCaller(this);
14490 AssertComRCReturnRC(autoCaller.rc());
14491
14492 ComPtr<IInternalSessionControl> directControl;
14493 {
14494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14495 if (mData->mSession.mLockType == LockType_VM)
14496 directControl = mData->mSession.mDirectControl;
14497 }
14498
14499 /* ignore notifications sent after #OnSessionEnd() is called */
14500 if (!directControl)
14501 return S_OK;
14502
14503 return directControl->OnDnDModeChange(aDnDMode);
14504}
14505
14506/**
14507 * @note Locks this object for reading.
14508 */
14509HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14510{
14511 LogFlowThisFunc(("\n"));
14512
14513 AutoCaller autoCaller(this);
14514 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14515
14516 ComPtr<IInternalSessionControl> directControl;
14517 {
14518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14519 if (mData->mSession.mLockType == LockType_VM)
14520 directControl = mData->mSession.mDirectControl;
14521 }
14522
14523 /* ignore notifications sent after #OnSessionEnd() is called */
14524 if (!directControl)
14525 return S_OK;
14526
14527 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14528}
14529
14530/**
14531 * @note Locks this object for reading.
14532 */
14533HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14534{
14535 LogFlowThisFunc(("\n"));
14536
14537 AutoCaller autoCaller(this);
14538 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14539
14540 ComPtr<IInternalSessionControl> directControl;
14541 {
14542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14543 if (mData->mSession.mLockType == LockType_VM)
14544 directControl = mData->mSession.mDirectControl;
14545 }
14546
14547 /* ignore notifications sent after #OnSessionEnd() is called */
14548 if (!directControl)
14549 return S_OK;
14550
14551 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14552}
14553
14554/**
14555 * Returns @c true if this machine's USB controller reports it has a matching
14556 * filter for the given USB device and @c false otherwise.
14557 *
14558 * @note locks this object for reading.
14559 */
14560bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14561{
14562 AutoCaller autoCaller(this);
14563 /* silently return if not ready -- this method may be called after the
14564 * direct machine session has been called */
14565 if (!autoCaller.isOk())
14566 return false;
14567
14568#ifdef VBOX_WITH_USB
14569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14570
14571 switch (mData->mMachineState)
14572 {
14573 case MachineState_Starting:
14574 case MachineState_Restoring:
14575 case MachineState_TeleportingIn:
14576 case MachineState_Paused:
14577 case MachineState_Running:
14578 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14579 * elsewhere... */
14580 alock.release();
14581 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14582 default: break;
14583 }
14584#else
14585 NOREF(aDevice);
14586 NOREF(aMaskedIfs);
14587#endif
14588 return false;
14589}
14590
14591/**
14592 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14593 */
14594HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14595 IVirtualBoxErrorInfo *aError,
14596 ULONG aMaskedIfs,
14597 const com::Utf8Str &aCaptureFilename)
14598{
14599 LogFlowThisFunc(("\n"));
14600
14601 AutoCaller autoCaller(this);
14602
14603 /* This notification may happen after the machine object has been
14604 * uninitialized (the session was closed), so don't assert. */
14605 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14606
14607 ComPtr<IInternalSessionControl> directControl;
14608 {
14609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14610 if (mData->mSession.mLockType == LockType_VM)
14611 directControl = mData->mSession.mDirectControl;
14612 }
14613
14614 /* fail on notifications sent after #OnSessionEnd() is called, it is
14615 * expected by the caller */
14616 if (!directControl)
14617 return E_FAIL;
14618
14619 /* No locks should be held at this point. */
14620 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14621 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14622
14623 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14624}
14625
14626/**
14627 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14628 */
14629HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14630 IVirtualBoxErrorInfo *aError)
14631{
14632 LogFlowThisFunc(("\n"));
14633
14634 AutoCaller autoCaller(this);
14635
14636 /* This notification may happen after the machine object has been
14637 * uninitialized (the session was closed), so don't assert. */
14638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14639
14640 ComPtr<IInternalSessionControl> directControl;
14641 {
14642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14643 if (mData->mSession.mLockType == LockType_VM)
14644 directControl = mData->mSession.mDirectControl;
14645 }
14646
14647 /* fail on notifications sent after #OnSessionEnd() is called, it is
14648 * expected by the caller */
14649 if (!directControl)
14650 return E_FAIL;
14651
14652 /* No locks should be held at this point. */
14653 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14654 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14655
14656 return directControl->OnUSBDeviceDetach(aId, aError);
14657}
14658
14659// protected methods
14660/////////////////////////////////////////////////////////////////////////////
14661
14662/**
14663 * Deletes the given file if it is no longer in use by either the current machine state
14664 * (if the machine is "saved") or any of the machine's snapshots.
14665 *
14666 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14667 * but is different for each SnapshotMachine. When calling this, the order of calling this
14668 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14669 * is therefore critical. I know, it's all rather messy.
14670 *
14671 * @param strStateFile
14672 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14673 * the test for whether the saved state file is in use.
14674 */
14675void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14676 Snapshot *pSnapshotToIgnore)
14677{
14678 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14679 if ( (strStateFile.isNotEmpty())
14680 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14681 )
14682 // ... and it must also not be shared with other snapshots
14683 if ( !mData->mFirstSnapshot
14684 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14685 // this checks the SnapshotMachine's state file paths
14686 )
14687 RTFileDelete(strStateFile.c_str());
14688}
14689
14690/**
14691 * Locks the attached media.
14692 *
14693 * All attached hard disks are locked for writing and DVD/floppy are locked for
14694 * reading. Parents of attached hard disks (if any) are locked for reading.
14695 *
14696 * This method also performs accessibility check of all media it locks: if some
14697 * media is inaccessible, the method will return a failure and a bunch of
14698 * extended error info objects per each inaccessible medium.
14699 *
14700 * Note that this method is atomic: if it returns a success, all media are
14701 * locked as described above; on failure no media is locked at all (all
14702 * succeeded individual locks will be undone).
14703 *
14704 * The caller is responsible for doing the necessary state sanity checks.
14705 *
14706 * The locks made by this method must be undone by calling #unlockMedia() when
14707 * no more needed.
14708 */
14709HRESULT SessionMachine::i_lockMedia()
14710{
14711 AutoCaller autoCaller(this);
14712 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14713
14714 AutoMultiWriteLock2 alock(this->lockHandle(),
14715 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14716
14717 /* bail out if trying to lock things with already set up locking */
14718 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14719
14720 MultiResult mrc(S_OK);
14721
14722 /* Collect locking information for all medium objects attached to the VM. */
14723 for (MediumAttachmentList::const_iterator
14724 it = mMediumAttachments->begin();
14725 it != mMediumAttachments->end();
14726 ++it)
14727 {
14728 MediumAttachment *pAtt = *it;
14729 DeviceType_T devType = pAtt->i_getType();
14730 Medium *pMedium = pAtt->i_getMedium();
14731
14732 MediumLockList *pMediumLockList(new MediumLockList());
14733 // There can be attachments without a medium (floppy/dvd), and thus
14734 // it's impossible to create a medium lock list. It still makes sense
14735 // to have the empty medium lock list in the map in case a medium is
14736 // attached later.
14737 if (pMedium != NULL)
14738 {
14739 MediumType_T mediumType = pMedium->i_getType();
14740 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14741 || mediumType == MediumType_Shareable;
14742 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14743
14744 alock.release();
14745 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14746 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14747 false /* fMediumLockWriteAll */,
14748 NULL,
14749 *pMediumLockList);
14750 alock.acquire();
14751 if (FAILED(mrc))
14752 {
14753 delete pMediumLockList;
14754 mData->mSession.mLockedMedia.Clear();
14755 break;
14756 }
14757 }
14758
14759 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14760 if (FAILED(rc))
14761 {
14762 mData->mSession.mLockedMedia.Clear();
14763 mrc = setError(rc,
14764 tr("Collecting locking information for all attached media failed"));
14765 break;
14766 }
14767 }
14768
14769 if (SUCCEEDED(mrc))
14770 {
14771 /* Now lock all media. If this fails, nothing is locked. */
14772 alock.release();
14773 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14774 alock.acquire();
14775 if (FAILED(rc))
14776 {
14777 mrc = setError(rc,
14778 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14779 }
14780 }
14781
14782 return mrc;
14783}
14784
14785/**
14786 * Undoes the locks made by by #lockMedia().
14787 */
14788HRESULT SessionMachine::i_unlockMedia()
14789{
14790 AutoCaller autoCaller(this);
14791 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14792
14793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14794
14795 /* we may be holding important error info on the current thread;
14796 * preserve it */
14797 ErrorInfoKeeper eik;
14798
14799 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14800 AssertComRC(rc);
14801 return rc;
14802}
14803
14804/**
14805 * Helper to change the machine state (reimplementation).
14806 *
14807 * @note Locks this object for writing.
14808 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14809 * it can cause crashes in random places due to unexpectedly committing
14810 * the current settings. The caller is responsible for that. The call
14811 * to saveStateSettings is fine, because this method does not commit.
14812 */
14813HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14814{
14815 LogFlowThisFuncEnter();
14816 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14817
14818 AutoCaller autoCaller(this);
14819 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14820
14821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14822
14823 MachineState_T oldMachineState = mData->mMachineState;
14824
14825 AssertMsgReturn(oldMachineState != aMachineState,
14826 ("oldMachineState=%s, aMachineState=%s\n",
14827 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14828 E_FAIL);
14829
14830 HRESULT rc = S_OK;
14831
14832 int stsFlags = 0;
14833 bool deleteSavedState = false;
14834
14835 /* detect some state transitions */
14836
14837 if ( ( oldMachineState == MachineState_Saved
14838 && aMachineState == MachineState_Restoring)
14839 || ( ( oldMachineState == MachineState_PoweredOff
14840 || oldMachineState == MachineState_Teleported
14841 || oldMachineState == MachineState_Aborted
14842 )
14843 && ( aMachineState == MachineState_TeleportingIn
14844 || aMachineState == MachineState_Starting
14845 )
14846 )
14847 )
14848 {
14849 /* The EMT thread is about to start */
14850
14851 /* Nothing to do here for now... */
14852
14853 /// @todo NEWMEDIA don't let mDVDDrive and other children
14854 /// change anything when in the Starting/Restoring state
14855 }
14856 else if ( ( oldMachineState == MachineState_Running
14857 || oldMachineState == MachineState_Paused
14858 || oldMachineState == MachineState_Teleporting
14859 || oldMachineState == MachineState_OnlineSnapshotting
14860 || oldMachineState == MachineState_LiveSnapshotting
14861 || oldMachineState == MachineState_Stuck
14862 || oldMachineState == MachineState_Starting
14863 || oldMachineState == MachineState_Stopping
14864 || oldMachineState == MachineState_Saving
14865 || oldMachineState == MachineState_Restoring
14866 || oldMachineState == MachineState_TeleportingPausedVM
14867 || oldMachineState == MachineState_TeleportingIn
14868 )
14869 && ( aMachineState == MachineState_PoweredOff
14870 || aMachineState == MachineState_Saved
14871 || aMachineState == MachineState_Teleported
14872 || aMachineState == MachineState_Aborted
14873 )
14874 )
14875 {
14876 /* The EMT thread has just stopped, unlock attached media. Note that as
14877 * opposed to locking that is done from Console, we do unlocking here
14878 * because the VM process may have aborted before having a chance to
14879 * properly unlock all media it locked. */
14880
14881 unlockMedia();
14882 }
14883
14884 if (oldMachineState == MachineState_Restoring)
14885 {
14886 if (aMachineState != MachineState_Saved)
14887 {
14888 /*
14889 * delete the saved state file once the machine has finished
14890 * restoring from it (note that Console sets the state from
14891 * Restoring to Saved if the VM couldn't restore successfully,
14892 * to give the user an ability to fix an error and retry --
14893 * we keep the saved state file in this case)
14894 */
14895 deleteSavedState = true;
14896 }
14897 }
14898 else if ( oldMachineState == MachineState_Saved
14899 && ( aMachineState == MachineState_PoweredOff
14900 || aMachineState == MachineState_Aborted
14901 || aMachineState == MachineState_Teleported
14902 )
14903 )
14904 {
14905 /*
14906 * delete the saved state after SessionMachine::ForgetSavedState() is called
14907 * or if the VM process (owning a direct VM session) crashed while the
14908 * VM was Saved
14909 */
14910
14911 /// @todo (dmik)
14912 // Not sure that deleting the saved state file just because of the
14913 // client death before it attempted to restore the VM is a good
14914 // thing. But when it crashes we need to go to the Aborted state
14915 // which cannot have the saved state file associated... The only
14916 // way to fix this is to make the Aborted condition not a VM state
14917 // but a bool flag: i.e., when a crash occurs, set it to true and
14918 // change the state to PoweredOff or Saved depending on the
14919 // saved state presence.
14920
14921 deleteSavedState = true;
14922 mData->mCurrentStateModified = TRUE;
14923 stsFlags |= SaveSTS_CurStateModified;
14924 }
14925
14926 if ( aMachineState == MachineState_Starting
14927 || aMachineState == MachineState_Restoring
14928 || aMachineState == MachineState_TeleportingIn
14929 )
14930 {
14931 /* set the current state modified flag to indicate that the current
14932 * state is no more identical to the state in the
14933 * current snapshot */
14934 if (!mData->mCurrentSnapshot.isNull())
14935 {
14936 mData->mCurrentStateModified = TRUE;
14937 stsFlags |= SaveSTS_CurStateModified;
14938 }
14939 }
14940
14941 if (deleteSavedState)
14942 {
14943 if (mRemoveSavedState)
14944 {
14945 Assert(!mSSData->strStateFilePath.isEmpty());
14946
14947 // it is safe to delete the saved state file if ...
14948 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14949 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14950 // ... none of the snapshots share the saved state file
14951 )
14952 RTFileDelete(mSSData->strStateFilePath.c_str());
14953 }
14954
14955 mSSData->strStateFilePath.setNull();
14956 stsFlags |= SaveSTS_StateFilePath;
14957 }
14958
14959 /* redirect to the underlying peer machine */
14960 mPeer->i_setMachineState(aMachineState);
14961
14962 if ( oldMachineState != MachineState_RestoringSnapshot
14963 && ( aMachineState == MachineState_PoweredOff
14964 || aMachineState == MachineState_Teleported
14965 || aMachineState == MachineState_Aborted
14966 || aMachineState == MachineState_Saved))
14967 {
14968 /* the machine has stopped execution
14969 * (or the saved state file was adopted) */
14970 stsFlags |= SaveSTS_StateTimeStamp;
14971 }
14972
14973 if ( ( oldMachineState == MachineState_PoweredOff
14974 || oldMachineState == MachineState_Aborted
14975 || oldMachineState == MachineState_Teleported
14976 )
14977 && aMachineState == MachineState_Saved)
14978 {
14979 /* the saved state file was adopted */
14980 Assert(!mSSData->strStateFilePath.isEmpty());
14981 stsFlags |= SaveSTS_StateFilePath;
14982 }
14983
14984#ifdef VBOX_WITH_GUEST_PROPS
14985 if ( aMachineState == MachineState_PoweredOff
14986 || aMachineState == MachineState_Aborted
14987 || aMachineState == MachineState_Teleported)
14988 {
14989 /* Make sure any transient guest properties get removed from the
14990 * property store on shutdown. */
14991 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14992
14993 /* remove it from the settings representation */
14994 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14995 for (settings::GuestPropertiesList::iterator
14996 it = llGuestProperties.begin();
14997 it != llGuestProperties.end();
14998 /*nothing*/)
14999 {
15000 const settings::GuestProperty &prop = *it;
15001 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
15002 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
15003 {
15004 it = llGuestProperties.erase(it);
15005 fNeedsSaving = true;
15006 }
15007 else
15008 {
15009 ++it;
15010 }
15011 }
15012
15013 /* Additionally remove it from the HWData representation. Required to
15014 * keep everything in sync, as this is what the API keeps using. */
15015 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15016 for (HWData::GuestPropertyMap::iterator
15017 it = llHWGuestProperties.begin();
15018 it != llHWGuestProperties.end();
15019 /*nothing*/)
15020 {
15021 uint32_t fFlags = it->second.mFlags;
15022 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15023 {
15024 /* iterator where we need to continue after the erase call
15025 * (C++03 is a fact still, and it doesn't return the iterator
15026 * which would allow continuing) */
15027 HWData::GuestPropertyMap::iterator it2 = it;
15028 ++it2;
15029 llHWGuestProperties.erase(it);
15030 it = it2;
15031 fNeedsSaving = true;
15032 }
15033 else
15034 {
15035 ++it;
15036 }
15037 }
15038
15039 if (fNeedsSaving)
15040 {
15041 mData->mCurrentStateModified = TRUE;
15042 stsFlags |= SaveSTS_CurStateModified;
15043 }
15044 }
15045#endif /* VBOX_WITH_GUEST_PROPS */
15046
15047 rc = i_saveStateSettings(stsFlags);
15048
15049 if ( ( oldMachineState != MachineState_PoweredOff
15050 && oldMachineState != MachineState_Aborted
15051 && oldMachineState != MachineState_Teleported
15052 )
15053 && ( aMachineState == MachineState_PoweredOff
15054 || aMachineState == MachineState_Aborted
15055 || aMachineState == MachineState_Teleported
15056 )
15057 )
15058 {
15059 /* we've been shut down for any reason */
15060 /* no special action so far */
15061 }
15062
15063 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15064 LogFlowThisFuncLeave();
15065 return rc;
15066}
15067
15068/**
15069 * Sends the current machine state value to the VM process.
15070 *
15071 * @note Locks this object for reading, then calls a client process.
15072 */
15073HRESULT SessionMachine::i_updateMachineStateOnClient()
15074{
15075 AutoCaller autoCaller(this);
15076 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15077
15078 ComPtr<IInternalSessionControl> directControl;
15079 {
15080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15081 AssertReturn(!!mData, E_FAIL);
15082 if (mData->mSession.mLockType == LockType_VM)
15083 directControl = mData->mSession.mDirectControl;
15084
15085 /* directControl may be already set to NULL here in #OnSessionEnd()
15086 * called too early by the direct session process while there is still
15087 * some operation (like deleting the snapshot) in progress. The client
15088 * process in this case is waiting inside Session::close() for the
15089 * "end session" process object to complete, while #uninit() called by
15090 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15091 * operation to complete. For now, we accept this inconsistent behavior
15092 * and simply do nothing here. */
15093
15094 if (mData->mSession.mState == SessionState_Unlocking)
15095 return S_OK;
15096 }
15097
15098 /* ignore notifications sent after #OnSessionEnd() is called */
15099 if (!directControl)
15100 return S_OK;
15101
15102 return directControl->UpdateMachineState(mData->mMachineState);
15103}
15104
15105
15106/*static*/
15107HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15108{
15109 va_list args;
15110 va_start(args, pcszMsg);
15111 HRESULT rc = setErrorInternal(aResultCode,
15112 getStaticClassIID(),
15113 getStaticComponentName(),
15114 Utf8Str(pcszMsg, args),
15115 false /* aWarning */,
15116 true /* aLogIt */);
15117 va_end(args);
15118 return rc;
15119}
15120
15121
15122HRESULT Machine::updateState(MachineState_T aState)
15123{
15124 NOREF(aState);
15125 ReturnComNotImplemented();
15126}
15127
15128HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15129{
15130 NOREF(aProgress);
15131 ReturnComNotImplemented();
15132}
15133
15134HRESULT Machine::endPowerUp(LONG aResult)
15135{
15136 NOREF(aResult);
15137 ReturnComNotImplemented();
15138}
15139
15140HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15141{
15142 NOREF(aProgress);
15143 ReturnComNotImplemented();
15144}
15145
15146HRESULT Machine::endPoweringDown(LONG aResult,
15147 const com::Utf8Str &aErrMsg)
15148{
15149 NOREF(aResult);
15150 NOREF(aErrMsg);
15151 ReturnComNotImplemented();
15152}
15153
15154HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15155 BOOL *aMatched,
15156 ULONG *aMaskedInterfaces)
15157{
15158 NOREF(aDevice);
15159 NOREF(aMatched);
15160 NOREF(aMaskedInterfaces);
15161 ReturnComNotImplemented();
15162
15163}
15164
15165HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15166{
15167 NOREF(aId); NOREF(aCaptureFilename);
15168 ReturnComNotImplemented();
15169}
15170
15171HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15172 BOOL aDone)
15173{
15174 NOREF(aId);
15175 NOREF(aDone);
15176 ReturnComNotImplemented();
15177}
15178
15179HRESULT Machine::autoCaptureUSBDevices()
15180{
15181 ReturnComNotImplemented();
15182}
15183
15184HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15185{
15186 NOREF(aDone);
15187 ReturnComNotImplemented();
15188}
15189
15190HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15191 ComPtr<IProgress> &aProgress)
15192{
15193 NOREF(aSession);
15194 NOREF(aProgress);
15195 ReturnComNotImplemented();
15196}
15197
15198HRESULT Machine::finishOnlineMergeMedium()
15199{
15200 ReturnComNotImplemented();
15201}
15202
15203HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15204 std::vector<com::Utf8Str> &aValues,
15205 std::vector<LONG64> &aTimestamps,
15206 std::vector<com::Utf8Str> &aFlags)
15207{
15208 NOREF(aNames);
15209 NOREF(aValues);
15210 NOREF(aTimestamps);
15211 NOREF(aFlags);
15212 ReturnComNotImplemented();
15213}
15214
15215HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15216 const com::Utf8Str &aValue,
15217 LONG64 aTimestamp,
15218 const com::Utf8Str &aFlags)
15219{
15220 NOREF(aName);
15221 NOREF(aValue);
15222 NOREF(aTimestamp);
15223 NOREF(aFlags);
15224 ReturnComNotImplemented();
15225}
15226
15227HRESULT Machine::lockMedia()
15228{
15229 ReturnComNotImplemented();
15230}
15231
15232HRESULT Machine::unlockMedia()
15233{
15234 ReturnComNotImplemented();
15235}
15236
15237HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15238 ComPtr<IMediumAttachment> &aNewAttachment)
15239{
15240 NOREF(aAttachment);
15241 NOREF(aNewAttachment);
15242 ReturnComNotImplemented();
15243}
15244
15245HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15246 ULONG aCpuUser,
15247 ULONG aCpuKernel,
15248 ULONG aCpuIdle,
15249 ULONG aMemTotal,
15250 ULONG aMemFree,
15251 ULONG aMemBalloon,
15252 ULONG aMemShared,
15253 ULONG aMemCache,
15254 ULONG aPagedTotal,
15255 ULONG aMemAllocTotal,
15256 ULONG aMemFreeTotal,
15257 ULONG aMemBalloonTotal,
15258 ULONG aMemSharedTotal,
15259 ULONG aVmNetRx,
15260 ULONG aVmNetTx)
15261{
15262 NOREF(aValidStats);
15263 NOREF(aCpuUser);
15264 NOREF(aCpuKernel);
15265 NOREF(aCpuIdle);
15266 NOREF(aMemTotal);
15267 NOREF(aMemFree);
15268 NOREF(aMemBalloon);
15269 NOREF(aMemShared);
15270 NOREF(aMemCache);
15271 NOREF(aPagedTotal);
15272 NOREF(aMemAllocTotal);
15273 NOREF(aMemFreeTotal);
15274 NOREF(aMemBalloonTotal);
15275 NOREF(aMemSharedTotal);
15276 NOREF(aVmNetRx);
15277 NOREF(aVmNetTx);
15278 ReturnComNotImplemented();
15279}
15280
15281HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15282 com::Utf8Str &aResult)
15283{
15284 NOREF(aAuthParams);
15285 NOREF(aResult);
15286 ReturnComNotImplemented();
15287}
15288
15289HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15290{
15291 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15292
15293 AutoCaller autoCaller(this);
15294 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15295
15296 HRESULT rc = S_OK;
15297
15298 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15299 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15300 rc = getUSBDeviceFilters(usbDeviceFilters);
15301 if (FAILED(rc)) return rc;
15302
15303 NOREF(aFlags);
15304 com::Utf8Str osTypeId;
15305 ComObjPtr<GuestOSType> osType = NULL;
15306
15307 /* Get the guest os type as a string from the VB. */
15308 rc = getOSTypeId(osTypeId);
15309 if (FAILED(rc)) return rc;
15310
15311 /* Get the os type obj that coresponds, can be used to get
15312 * the defaults for this guest OS. */
15313 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15314 if (FAILED(rc)) return rc;
15315
15316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15317
15318 /* Let the OS type select 64-bit ness. */
15319 mHWData->mLongMode = osType->i_is64Bit()
15320 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15321
15322 /* Apply network adapters defaults */
15323 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15324 mNetworkAdapters[slot]->i_applyDefaults(osType);
15325
15326 /* Apply serial port defaults */
15327 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15328 mSerialPorts[slot]->i_applyDefaults(osType);
15329
15330 /* Apply parallel port defaults - not OS dependent*/
15331 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15332 mParallelPorts[slot]->i_applyDefaults();
15333
15334
15335 /* Let the OS type enable the X2APIC */
15336 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15337
15338 /* This one covers IOAPICEnabled. */
15339 mBIOSSettings->i_applyDefaults(osType);
15340
15341 /* Initialize default BIOS settings here */
15342 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15343 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15344
15345 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15346 if (FAILED(rc)) return rc;
15347
15348 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15349 if (FAILED(rc)) return rc;
15350
15351 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15352 if (FAILED(rc)) return rc;
15353
15354 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15355 if (FAILED(rc)) return rc;
15356
15357 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15358 if (FAILED(rc)) return rc;
15359
15360 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15361 if (FAILED(rc)) return rc;
15362
15363 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15364 if (FAILED(rc)) return rc;
15365
15366 BOOL mRTCUseUTC;
15367 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15368 if (FAILED(rc)) return rc;
15369
15370 setRTCUseUTC(mRTCUseUTC);
15371 if (FAILED(rc)) return rc;
15372
15373 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15374 if (FAILED(rc)) return rc;
15375
15376 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15377 if (FAILED(rc)) return rc;
15378
15379 /* Audio stuff. */
15380 AudioCodecType_T audioCodec;
15381 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15382 if (FAILED(rc)) return rc;
15383
15384 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15385 if (FAILED(rc)) return rc;
15386
15387 AudioControllerType_T audioController;
15388 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15389 if (FAILED(rc)) return rc;
15390
15391 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15392 if (FAILED(rc)) return rc;
15393
15394 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15395 if (FAILED(rc)) return rc;
15396
15397 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15398 if (FAILED(rc)) return rc;
15399
15400 /* Storage Controllers */
15401 StorageControllerType_T hdStorageControllerType;
15402 StorageBus_T hdStorageBusType;
15403 StorageControllerType_T dvdStorageControllerType;
15404 StorageBus_T dvdStorageBusType;
15405 BOOL recommendedFloppy;
15406 ComPtr<IStorageController> floppyController;
15407 ComPtr<IStorageController> hdController;
15408 ComPtr<IStorageController> dvdController;
15409 Utf8Str strFloppyName, strDVDName, strHDName;
15410
15411 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15412 strFloppyName = Bstr("Floppy 1").raw();
15413 strDVDName = Bstr("DVD 1").raw();
15414 strHDName = Bstr("HDD 1").raw();
15415
15416 /* Floppy recommended? add one. */
15417 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15418 if (FAILED(rc)) return rc;
15419 if (recommendedFloppy)
15420 {
15421 rc = addStorageController(strFloppyName,
15422 StorageBus_Floppy,
15423 floppyController);
15424 if (FAILED(rc)) return rc;
15425 }
15426
15427 /* Setup one DVD storage controller. */
15428 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15429 if (FAILED(rc)) return rc;
15430
15431 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15432 if (FAILED(rc)) return rc;
15433
15434 rc = addStorageController(strDVDName,
15435 dvdStorageBusType,
15436 dvdController);
15437 if (FAILED(rc)) return rc;
15438
15439 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15440 if (FAILED(rc)) return rc;
15441
15442 /* Setup one HDD storage controller. */
15443 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15444 if (FAILED(rc)) return rc;
15445
15446 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15447 if (FAILED(rc)) return rc;
15448
15449 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15450 {
15451 rc = addStorageController(strHDName,
15452 hdStorageBusType,
15453 hdController);
15454 if (FAILED(rc)) return rc;
15455
15456 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15457 if (FAILED(rc)) return rc;
15458 }
15459 else
15460 {
15461 /* The HD controller is the same as DVD: */
15462 hdController = dvdController;
15463 strHDName = Bstr("DVD 1").raw();
15464 }
15465
15466 /* Limit the AHCI port count if it's used because windows has trouble with
15467 * too many ports and other guest (OS X in particular) may take extra long
15468 * boot: */
15469
15470 // pParent = static_cast<Medium*>(aP)
15471 IStorageController *temp = hdController;
15472 ComObjPtr<StorageController> storageController;
15473 storageController = static_cast<StorageController *>(temp);
15474
15475 // tempHDController = aHDController;
15476 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15477 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15478 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15479 storageController->COMSETTER(PortCount)(1);
15480
15481 /* USB stuff */
15482
15483 bool ohciEnabled = false;
15484
15485 ComPtr<IUSBController> usbController;
15486 BOOL recommendedUSB3;
15487 BOOL recommendedUSB;
15488 BOOL usbProxyAvailable;
15489
15490 getUSBProxyAvailable(&usbProxyAvailable);
15491 if (FAILED(rc)) return rc;
15492
15493 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15494 if (FAILED(rc)) return rc;
15495 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15496 if (FAILED(rc)) return rc;
15497
15498 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15499 {
15500#ifdef VBOX_WITH_EXTPACK
15501 /* USB 3.0 is only available if the proper ExtPack is installed. */
15502 ExtPackManager *aManager = mParent->i_getExtPackManager();
15503 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15504 {
15505 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15506 if (FAILED(rc)) return rc;
15507
15508 /* xHci includes OHCI */
15509 ohciEnabled = true;
15510 }
15511#endif
15512 }
15513 if ( !ohciEnabled
15514 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15515 {
15516 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15517 if (FAILED(rc)) return rc;
15518 ohciEnabled = true;
15519
15520#ifdef VBOX_WITH_EXTPACK
15521 /* USB 2.0 is only available if the proper ExtPack is installed.
15522 * Note. Configuring EHCI here and providing messages about
15523 * the missing extpack isn't exactly clean, but it is a
15524 * necessary evil to patch over legacy compatability issues
15525 * introduced by the new distribution model. */
15526 ExtPackManager *manager = mParent->i_getExtPackManager();
15527 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15528 {
15529 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15530 if (FAILED(rc)) return rc;
15531 }
15532#endif
15533 }
15534
15535 /* Set recommended human interface device types: */
15536 BOOL recommendedUSBHID;
15537 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15538 if (FAILED(rc)) return rc;
15539
15540 if (recommendedUSBHID)
15541 {
15542 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15543 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15544 if (!ohciEnabled && !usbDeviceFilters.isNull())
15545 {
15546 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15547 if (FAILED(rc)) return rc;
15548 }
15549 }
15550
15551 BOOL recommendedUSBTablet;
15552 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15553 if (FAILED(rc)) return rc;
15554
15555 if (recommendedUSBTablet)
15556 {
15557 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15558 if (!ohciEnabled && !usbDeviceFilters.isNull())
15559 {
15560 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15561 if (FAILED(rc)) return rc;
15562 }
15563 }
15564 return S_OK;
15565}
15566
15567/* This isn't handled entirely by the wrapper generator yet. */
15568#ifdef VBOX_WITH_XPCOM
15569NS_DECL_CLASSINFO(SessionMachine)
15570NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15571
15572NS_DECL_CLASSINFO(SnapshotMachine)
15573NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15574#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