VirtualBox

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

Last change on this file since 76598 was 76592, checked in by vboxsync, 6 years ago

Main: Don't use Logging.h.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 530.2 KB
Line 
1/* $Id: MachineImpl.cpp 76592 2019-01-01 20:13:07Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51
52// generated header
53#include "VBoxEvents.h"
54
55#ifdef VBOX_WITH_USB
56# include "USBProxyService.h"
57#endif
58
59#include "AutoCaller.h"
60#include "HashedPw.h"
61#include "Performance.h"
62
63#include <iprt/asm.h>
64#include <iprt/path.h>
65#include <iprt/dir.h>
66#include <iprt/env.h>
67#include <iprt/lockvalidator.h>
68#include <iprt/process.h>
69#include <iprt/cpp/utils.h>
70#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
71#include <iprt/sha.h>
72#include <iprt/string.h>
73#include <iprt/ctype.h>
74
75#include <VBox/com/array.h>
76#include <VBox/com/list.h>
77
78#include <VBox/err.h>
79#include <VBox/param.h>
80#include <VBox/settings.h>
81#include <VBox/VMMDev.h>
82#include <VBox/vmm/ssm.h>
83
84#ifdef VBOX_WITH_GUEST_PROPS
85# include <VBox/HostServices/GuestPropertySvc.h>
86# include <VBox/com/array.h>
87#endif
88
89#include "VBox/com/MultiResult.h"
90
91#include <algorithm>
92
93#ifdef VBOX_WITH_DTRACE_R3_MAIN
94# include "dtrace/VBoxAPI.h"
95#endif
96
97#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
98# define HOSTSUFF_EXE ".exe"
99#else /* !RT_OS_WINDOWS */
100# define HOSTSUFF_EXE ""
101#endif /* !RT_OS_WINDOWS */
102
103// defines / prototypes
104/////////////////////////////////////////////////////////////////////////////
105
106/////////////////////////////////////////////////////////////////////////////
107// Machine::Data structure
108/////////////////////////////////////////////////////////////////////////////
109
110Machine::Data::Data()
111{
112 mRegistered = FALSE;
113 pMachineConfigFile = NULL;
114 /* Contains hints on what has changed when the user is using the VM (config
115 * changes, running the VM, ...). This is used to decide if a config needs
116 * to be written to disk. */
117 flModifications = 0;
118 /* VM modification usually also trigger setting the current state to
119 * "Modified". Although this is not always the case. An e.g. is the VM
120 * initialization phase or when snapshot related data is changed. The
121 * actually behavior is controlled by the following flag. */
122 m_fAllowStateModification = false;
123 mAccessible = FALSE;
124 /* mUuid is initialized in Machine::init() */
125
126 mMachineState = MachineState_PoweredOff;
127 RTTimeNow(&mLastStateChange);
128
129 mMachineStateDeps = 0;
130 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
131 mMachineStateChangePending = 0;
132
133 mCurrentStateModified = TRUE;
134 mGuestPropertiesModified = FALSE;
135
136 mSession.mPID = NIL_RTPROCESS;
137 mSession.mLockType = LockType_Null;
138 mSession.mState = SessionState_Unlocked;
139}
140
141Machine::Data::~Data()
142{
143 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
144 {
145 RTSemEventMultiDestroy(mMachineStateDepsSem);
146 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
147 }
148 if (pMachineConfigFile)
149 {
150 delete pMachineConfigFile;
151 pMachineConfigFile = NULL;
152 }
153}
154
155/////////////////////////////////////////////////////////////////////////////
156// Machine::HWData structure
157/////////////////////////////////////////////////////////////////////////////
158
159Machine::HWData::HWData()
160{
161 /* default values for a newly created machine */
162 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
163 mMemorySize = 128;
164 mCPUCount = 1;
165 mCPUHotPlugEnabled = false;
166 mMemoryBalloonSize = 0;
167 mPageFusionEnabled = false;
168 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
169 mVRAMSize = 8;
170 mAccelerate3DEnabled = false;
171 mAccelerate2DVideoEnabled = false;
172 mMonitorCount = 1;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mNestedHWVirt = false;
199 mHPETEnabled = false;
200 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
201 mCpuIdPortabilityLevel = 0;
202 mCpuProfile = "host";
203
204 /* default boot order: floppy - DVD - HDD */
205 mBootOrder[0] = DeviceType_Floppy;
206 mBootOrder[1] = DeviceType_DVD;
207 mBootOrder[2] = DeviceType_HardDisk;
208 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
209 mBootOrder[i] = DeviceType_Null;
210
211 mClipboardMode = ClipboardMode_Disabled;
212 mDnDMode = DnDMode_Disabled;
213
214 mFirmwareType = FirmwareType_BIOS;
215 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
216 mPointingHIDType = PointingHIDType_PS2Mouse;
217 mChipsetType = ChipsetType_PIIX3;
218 mParavirtProvider = ParavirtProvider_Default;
219 mEmulatedUSBCardReaderEnabled = FALSE;
220
221 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
222 mCPUAttached[i] = false;
223
224 mIOCacheEnabled = true;
225 mIOCacheSize = 5; /* 5MB */
226}
227
228Machine::HWData::~HWData()
229{
230}
231
232/////////////////////////////////////////////////////////////////////////////
233// Machine class
234/////////////////////////////////////////////////////////////////////////////
235
236// constructor / destructor
237/////////////////////////////////////////////////////////////////////////////
238
239Machine::Machine() :
240#ifdef VBOX_WITH_RESOURCE_USAGE_API
241 mCollectorGuest(NULL),
242#endif
243 mPeer(NULL),
244 mParent(NULL),
245 mSerialPorts(),
246 mParallelPorts(),
247 uRegistryNeedsSaving(0)
248{}
249
250Machine::~Machine()
251{}
252
253HRESULT Machine::FinalConstruct()
254{
255 LogFlowThisFunc(("\n"));
256 return BaseFinalConstruct();
257}
258
259void Machine::FinalRelease()
260{
261 LogFlowThisFunc(("\n"));
262 uninit();
263 BaseFinalRelease();
264}
265
266/**
267 * Initializes a new machine instance; this init() variant creates a new, empty machine.
268 * This gets called from VirtualBox::CreateMachine().
269 *
270 * @param aParent Associated parent object
271 * @param strConfigFile Local file system path to the VM settings file (can
272 * be relative to the VirtualBox config directory).
273 * @param strName name for the machine
274 * @param llGroups list of groups for the machine
275 * @param strOsType OS Type string (stored as is if aOsType is NULL).
276 * @param aOsType OS Type of this machine or NULL.
277 * @param aId UUID for the new machine.
278 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
279 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
280 * scheme (includes the UUID).
281 *
282 * @return Success indicator. if not S_OK, the machine object is invalid
283 */
284HRESULT Machine::init(VirtualBox *aParent,
285 const Utf8Str &strConfigFile,
286 const Utf8Str &strName,
287 const StringsList &llGroups,
288 const Utf8Str &strOsType,
289 GuestOSType *aOsType,
290 const Guid &aId,
291 bool fForceOverwrite,
292 bool fDirectoryIncludesUUID)
293{
294 LogFlowThisFuncEnter();
295 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
296
297 /* Enclose the state transition NotReady->InInit->Ready */
298 AutoInitSpan autoInitSpan(this);
299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
300
301 HRESULT rc = initImpl(aParent, strConfigFile);
302 if (FAILED(rc)) return rc;
303
304 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
305 if (FAILED(rc)) return rc;
306
307 if (SUCCEEDED(rc))
308 {
309 // create an empty machine config
310 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
311
312 rc = initDataAndChildObjects();
313 }
314
315 if (SUCCEEDED(rc))
316 {
317 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
318 mData->mAccessible = TRUE;
319
320 unconst(mData->mUuid) = aId;
321
322 mUserData->s.strName = strName;
323
324 if (llGroups.size())
325 mUserData->s.llGroups = llGroups;
326
327 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
328 // the "name sync" flag determines whether the machine directory gets renamed along
329 // with the machine file; say so if the settings file name is the same as the
330 // settings file parent directory (machine directory)
331 mUserData->s.fNameSync = i_isInOwnDir();
332
333 // initialize the default snapshots folder
334 rc = COMSETTER(SnapshotFolder)(NULL);
335 AssertComRC(rc);
336
337 if (aOsType)
338 {
339 /* Store OS type */
340 mUserData->s.strOsType = aOsType->i_id();
341
342 /* Let the OS type select 64-bit ness. */
343 mHWData->mLongMode = aOsType->i_is64Bit()
344 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
345
346 /* Let the OS type enable the X2APIC */
347 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
348 }
349 else if (!strOsType.isEmpty())
350 {
351 /* Store OS type */
352 mUserData->s.strOsType = strOsType;
353
354 /* No guest OS type object. Pick some plausible defaults which the
355 * host can handle. There's no way to know or validate anything. */
356 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
357 mHWData->mX2APIC = false;
358 }
359
360 /* Apply BIOS defaults. */
361 mBIOSSettings->i_applyDefaults(aOsType);
362
363 /* Apply record defaults. */
364 mRecordingSettings->i_applyDefaults();
365
366 /* Apply network adapters defaults */
367 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
368 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
369
370 /* Apply serial port defaults */
371 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
372 mSerialPorts[slot]->i_applyDefaults(aOsType);
373
374 /* Apply parallel port defaults */
375 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
376 mParallelPorts[slot]->i_applyDefaults();
377
378 /* At this point the changing of the current state modification
379 * flag is allowed. */
380 i_allowStateModification();
381
382 /* commit all changes made during the initialization */
383 i_commit();
384 }
385
386 /* Confirm a successful initialization when it's the case */
387 if (SUCCEEDED(rc))
388 {
389 if (mData->mAccessible)
390 autoInitSpan.setSucceeded();
391 else
392 autoInitSpan.setLimited();
393 }
394
395 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
396 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
397 mData->mRegistered,
398 mData->mAccessible,
399 rc));
400
401 LogFlowThisFuncLeave();
402
403 return rc;
404}
405
406/**
407 * Initializes a new instance with data from machine XML (formerly Init_Registered).
408 * Gets called in two modes:
409 *
410 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
411 * UUID is specified and we mark the machine as "registered";
412 *
413 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
414 * and the machine remains unregistered until RegisterMachine() is called.
415 *
416 * @param aParent Associated parent object
417 * @param strConfigFile Local file system path to the VM settings file (can
418 * be relative to the VirtualBox config directory).
419 * @param aId UUID of the machine or NULL (see above).
420 *
421 * @return Success indicator. if not S_OK, the machine object is invalid
422 */
423HRESULT Machine::initFromSettings(VirtualBox *aParent,
424 const Utf8Str &strConfigFile,
425 const Guid *aId)
426{
427 LogFlowThisFuncEnter();
428 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
429
430 /* Enclose the state transition NotReady->InInit->Ready */
431 AutoInitSpan autoInitSpan(this);
432 AssertReturn(autoInitSpan.isOk(), E_FAIL);
433
434 HRESULT rc = initImpl(aParent, strConfigFile);
435 if (FAILED(rc)) return rc;
436
437 if (aId)
438 {
439 // loading a registered VM:
440 unconst(mData->mUuid) = *aId;
441 mData->mRegistered = TRUE;
442 // now load the settings from XML:
443 rc = i_registeredInit();
444 // this calls initDataAndChildObjects() and loadSettings()
445 }
446 else
447 {
448 // opening an unregistered VM (VirtualBox::OpenMachine()):
449 rc = initDataAndChildObjects();
450
451 if (SUCCEEDED(rc))
452 {
453 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
454 mData->mAccessible = TRUE;
455
456 try
457 {
458 // load and parse machine XML; this will throw on XML or logic errors
459 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
460
461 // reject VM UUID duplicates, they can happen if someone
462 // tries to register an already known VM config again
463 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
464 true /* fPermitInaccessible */,
465 false /* aDoSetError */,
466 NULL) != VBOX_E_OBJECT_NOT_FOUND)
467 {
468 throw setError(E_FAIL,
469 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
470 mData->m_strConfigFile.c_str());
471 }
472
473 // use UUID from machine config
474 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
475
476 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
477 NULL /* puuidRegistry */);
478 if (FAILED(rc)) throw rc;
479
480 /* At this point the changing of the current state modification
481 * flag is allowed. */
482 i_allowStateModification();
483
484 i_commit();
485 }
486 catch (HRESULT err)
487 {
488 /* we assume that error info is set by the thrower */
489 rc = err;
490 }
491 catch (...)
492 {
493 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
494 }
495 }
496 }
497
498 /* Confirm a successful initialization when it's the case */
499 if (SUCCEEDED(rc))
500 {
501 if (mData->mAccessible)
502 autoInitSpan.setSucceeded();
503 else
504 {
505 autoInitSpan.setLimited();
506
507 // uninit media from this machine's media registry, or else
508 // reloading the settings will fail
509 mParent->i_unregisterMachineMedia(i_getId());
510 }
511 }
512
513 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
514 "rc=%08X\n",
515 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
516 mData->mRegistered, mData->mAccessible, rc));
517
518 LogFlowThisFuncLeave();
519
520 return rc;
521}
522
523/**
524 * Initializes a new instance from a machine config that is already in memory
525 * (import OVF case). Since we are importing, the UUID in the machine
526 * config is ignored and we always generate a fresh one.
527 *
528 * @param aParent Associated parent object.
529 * @param strName Name for the new machine; this overrides what is specified in config.
530 * @param strSettingsFilename File name of .vbox file.
531 * @param config Machine configuration loaded and parsed from XML.
532 *
533 * @return Success indicator. if not S_OK, the machine object is invalid
534 */
535HRESULT Machine::init(VirtualBox *aParent,
536 const Utf8Str &strName,
537 const Utf8Str &strSettingsFilename,
538 const settings::MachineConfigFile &config)
539{
540 LogFlowThisFuncEnter();
541
542 /* Enclose the state transition NotReady->InInit->Ready */
543 AutoInitSpan autoInitSpan(this);
544 AssertReturn(autoInitSpan.isOk(), E_FAIL);
545
546 HRESULT rc = initImpl(aParent, strSettingsFilename);
547 if (FAILED(rc)) return rc;
548
549 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
550 if (FAILED(rc)) return rc;
551
552 rc = initDataAndChildObjects();
553
554 if (SUCCEEDED(rc))
555 {
556 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
557 mData->mAccessible = TRUE;
558
559 // create empty machine config for instance data
560 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
561
562 // generate fresh UUID, ignore machine config
563 unconst(mData->mUuid).create();
564
565 rc = i_loadMachineDataFromSettings(config,
566 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
567
568 // override VM name as well, it may be different
569 mUserData->s.strName = strName;
570
571 if (SUCCEEDED(rc))
572 {
573 /* At this point the changing of the current state modification
574 * flag is allowed. */
575 i_allowStateModification();
576
577 /* commit all changes made during the initialization */
578 i_commit();
579 }
580 }
581
582 /* Confirm a successful initialization when it's the case */
583 if (SUCCEEDED(rc))
584 {
585 if (mData->mAccessible)
586 autoInitSpan.setSucceeded();
587 else
588 {
589 /* Ignore all errors from unregistering, they would destroy
590- * the more interesting error information we already have,
591- * pinpointing the issue with the VM config. */
592 ErrorInfoKeeper eik;
593
594 autoInitSpan.setLimited();
595
596 // uninit media from this machine's media registry, or else
597 // reloading the settings will fail
598 mParent->i_unregisterMachineMedia(i_getId());
599 }
600 }
601
602 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
603 "rc=%08X\n",
604 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
605 mData->mRegistered, mData->mAccessible, rc));
606
607 LogFlowThisFuncLeave();
608
609 return rc;
610}
611
612/**
613 * Shared code between the various init() implementations.
614 * @param aParent The VirtualBox object.
615 * @param strConfigFile Settings file.
616 * @return
617 */
618HRESULT Machine::initImpl(VirtualBox *aParent,
619 const Utf8Str &strConfigFile)
620{
621 LogFlowThisFuncEnter();
622
623 AssertReturn(aParent, E_INVALIDARG);
624 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
625
626 HRESULT rc = S_OK;
627
628 /* share the parent weakly */
629 unconst(mParent) = aParent;
630
631 /* allocate the essential machine data structure (the rest will be
632 * allocated later by initDataAndChildObjects() */
633 mData.allocate();
634
635 /* memorize the config file name (as provided) */
636 mData->m_strConfigFile = strConfigFile;
637
638 /* get the full file name */
639 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
640 if (RT_FAILURE(vrc1))
641 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
642 tr("Invalid machine settings file name '%s' (%Rrc)"),
643 strConfigFile.c_str(),
644 vrc1);
645
646 LogFlowThisFuncLeave();
647
648 return rc;
649}
650
651/**
652 * Tries to create a machine settings file in the path stored in the machine
653 * instance data. Used when a new machine is created to fail gracefully if
654 * the settings file could not be written (e.g. because machine dir is read-only).
655 * @return
656 */
657HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
658{
659 HRESULT rc = S_OK;
660
661 // when we create a new machine, we must be able to create the settings file
662 RTFILE f = NIL_RTFILE;
663 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
664 if ( RT_SUCCESS(vrc)
665 || vrc == VERR_SHARING_VIOLATION
666 )
667 {
668 if (RT_SUCCESS(vrc))
669 RTFileClose(f);
670 if (!fForceOverwrite)
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Machine settings file '%s' already exists"),
673 mData->m_strConfigFileFull.c_str());
674 else
675 {
676 /* try to delete the config file, as otherwise the creation
677 * of a new settings file will fail. */
678 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
679 if (RT_FAILURE(vrc2))
680 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
681 tr("Could not delete the existing settings file '%s' (%Rrc)"),
682 mData->m_strConfigFileFull.c_str(), vrc2);
683 }
684 }
685 else if ( vrc != VERR_FILE_NOT_FOUND
686 && vrc != VERR_PATH_NOT_FOUND
687 )
688 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
689 tr("Invalid machine settings file name '%s' (%Rrc)"),
690 mData->m_strConfigFileFull.c_str(),
691 vrc);
692 return rc;
693}
694
695/**
696 * Initializes the registered machine by loading the settings file.
697 * This method is separated from #init() in order to make it possible to
698 * retry the operation after VirtualBox startup instead of refusing to
699 * startup the whole VirtualBox server in case if the settings file of some
700 * registered VM is invalid or inaccessible.
701 *
702 * @note Must be always called from this object's write lock
703 * (unless called from #init() that doesn't need any locking).
704 * @note Locks the mUSBController method for writing.
705 * @note Subclasses must not call this method.
706 */
707HRESULT Machine::i_registeredInit()
708{
709 AssertReturn(!i_isSessionMachine(), E_FAIL);
710 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
711 AssertReturn(mData->mUuid.isValid(), E_FAIL);
712 AssertReturn(!mData->mAccessible, E_FAIL);
713
714 HRESULT rc = initDataAndChildObjects();
715
716 if (SUCCEEDED(rc))
717 {
718 /* Temporarily reset the registered flag in order to let setters
719 * potentially called from loadSettings() succeed (isMutable() used in
720 * all setters will return FALSE for a Machine instance if mRegistered
721 * is TRUE). */
722 mData->mRegistered = FALSE;
723
724 try
725 {
726 // load and parse machine XML; this will throw on XML or logic errors
727 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
728
729 if (mData->mUuid != mData->pMachineConfigFile->uuid)
730 throw setError(E_FAIL,
731 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
732 mData->pMachineConfigFile->uuid.raw(),
733 mData->m_strConfigFileFull.c_str(),
734 mData->mUuid.toString().c_str(),
735 mParent->i_settingsFilePath().c_str());
736
737 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
738 NULL /* const Guid *puuidRegistry */);
739 if (FAILED(rc)) throw rc;
740 }
741 catch (HRESULT err)
742 {
743 /* we assume that error info is set by the thrower */
744 rc = err;
745 }
746 catch (...)
747 {
748 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
749 }
750
751 /* Restore the registered flag (even on failure) */
752 mData->mRegistered = TRUE;
753 }
754
755 if (SUCCEEDED(rc))
756 {
757 /* Set mAccessible to TRUE only if we successfully locked and loaded
758 * the settings file */
759 mData->mAccessible = TRUE;
760
761 /* commit all changes made during loading the settings file */
762 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
763 /// @todo r=klaus for some reason the settings loading logic backs up
764 // the settings, and therefore a commit is needed. Should probably be changed.
765 }
766 else
767 {
768 /* If the machine is registered, then, instead of returning a
769 * failure, we mark it as inaccessible and set the result to
770 * success to give it a try later */
771
772 /* fetch the current error info */
773 mData->mAccessError = com::ErrorInfo();
774 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
775
776 /* rollback all changes */
777 i_rollback(false /* aNotify */);
778
779 // uninit media from this machine's media registry, or else
780 // reloading the settings will fail
781 mParent->i_unregisterMachineMedia(i_getId());
782
783 /* uninitialize the common part to make sure all data is reset to
784 * default (null) values */
785 uninitDataAndChildObjects();
786
787 rc = S_OK;
788 }
789
790 return rc;
791}
792
793/**
794 * Uninitializes the instance.
795 * Called either from FinalRelease() or by the parent when it gets destroyed.
796 *
797 * @note The caller of this method must make sure that this object
798 * a) doesn't have active callers on the current thread and b) is not locked
799 * by the current thread; otherwise uninit() will hang either a) due to
800 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
801 * a dead-lock caused by this thread waiting for all callers on the other
802 * threads are done but preventing them from doing so by holding a lock.
803 */
804void Machine::uninit()
805{
806 LogFlowThisFuncEnter();
807
808 Assert(!isWriteLockOnCurrentThread());
809
810 Assert(!uRegistryNeedsSaving);
811 if (uRegistryNeedsSaving)
812 {
813 AutoCaller autoCaller(this);
814 if (SUCCEEDED(autoCaller.rc()))
815 {
816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
817 i_saveSettings(NULL, Machine::SaveS_Force);
818 }
819 }
820
821 /* Enclose the state transition Ready->InUninit->NotReady */
822 AutoUninitSpan autoUninitSpan(this);
823 if (autoUninitSpan.uninitDone())
824 return;
825
826 Assert(!i_isSnapshotMachine());
827 Assert(!i_isSessionMachine());
828 Assert(!!mData);
829
830 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
831 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
832
833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
834
835 if (!mData->mSession.mMachine.isNull())
836 {
837 /* Theoretically, this can only happen if the VirtualBox server has been
838 * terminated while there were clients running that owned open direct
839 * sessions. Since in this case we are definitely called by
840 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
841 * won't happen on the client watcher thread (because it has a
842 * VirtualBox caller for the duration of the
843 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
844 * cannot happen until the VirtualBox caller is released). This is
845 * important, because SessionMachine::uninit() cannot correctly operate
846 * after we return from this method (it expects the Machine instance is
847 * still valid). We'll call it ourselves below.
848 */
849 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
850 (SessionMachine*)mData->mSession.mMachine));
851
852 if (Global::IsOnlineOrTransient(mData->mMachineState))
853 {
854 Log1WarningThisFunc(("Setting state to Aborted!\n"));
855 /* set machine state using SessionMachine reimplementation */
856 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
857 }
858
859 /*
860 * Uninitialize SessionMachine using public uninit() to indicate
861 * an unexpected uninitialization.
862 */
863 mData->mSession.mMachine->uninit();
864 /* SessionMachine::uninit() must set mSession.mMachine to null */
865 Assert(mData->mSession.mMachine.isNull());
866 }
867
868 // uninit media from this machine's media registry, if they're still there
869 Guid uuidMachine(i_getId());
870
871 /* the lock is no more necessary (SessionMachine is uninitialized) */
872 alock.release();
873
874 /* XXX This will fail with
875 * "cannot be closed because it is still attached to 1 virtual machines"
876 * because at this point we did not call uninitDataAndChildObjects() yet
877 * and therefore also removeBackReference() for all these mediums was not called! */
878
879 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
880 mParent->i_unregisterMachineMedia(uuidMachine);
881
882 // has machine been modified?
883 if (mData->flModifications)
884 {
885 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
886 i_rollback(false /* aNotify */);
887 }
888
889 if (mData->mAccessible)
890 uninitDataAndChildObjects();
891
892 /* free the essential data structure last */
893 mData.free();
894
895 LogFlowThisFuncLeave();
896}
897
898// Wrapped IMachine properties
899/////////////////////////////////////////////////////////////////////////////
900HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
901{
902 /* mParent is constant during life time, no need to lock */
903 ComObjPtr<VirtualBox> pVirtualBox(mParent);
904 aParent = pVirtualBox;
905
906 return S_OK;
907}
908
909
910HRESULT Machine::getAccessible(BOOL *aAccessible)
911{
912 /* In some cases (medium registry related), it is necessary to be able to
913 * go through the list of all machines. Happens when an inaccessible VM
914 * has a sensible medium registry. */
915 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
917
918 HRESULT rc = S_OK;
919
920 if (!mData->mAccessible)
921 {
922 /* try to initialize the VM once more if not accessible */
923
924 AutoReinitSpan autoReinitSpan(this);
925 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
926
927#ifdef DEBUG
928 LogFlowThisFunc(("Dumping media backreferences\n"));
929 mParent->i_dumpAllBackRefs();
930#endif
931
932 if (mData->pMachineConfigFile)
933 {
934 // reset the XML file to force loadSettings() (called from i_registeredInit())
935 // to parse it again; the file might have changed
936 delete mData->pMachineConfigFile;
937 mData->pMachineConfigFile = NULL;
938 }
939
940 rc = i_registeredInit();
941
942 if (SUCCEEDED(rc) && mData->mAccessible)
943 {
944 autoReinitSpan.setSucceeded();
945
946 /* make sure interesting parties will notice the accessibility
947 * state change */
948 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
949 mParent->i_onMachineDataChange(mData->mUuid);
950 }
951 }
952
953 if (SUCCEEDED(rc))
954 *aAccessible = mData->mAccessible;
955
956 LogFlowThisFuncLeave();
957
958 return rc;
959}
960
961HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
962{
963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
964
965 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
966 {
967 /* return shortly */
968 aAccessError = NULL;
969 return S_OK;
970 }
971
972 HRESULT rc = S_OK;
973
974 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
975 rc = errorInfo.createObject();
976 if (SUCCEEDED(rc))
977 {
978 errorInfo->init(mData->mAccessError.getResultCode(),
979 mData->mAccessError.getInterfaceID().ref(),
980 Utf8Str(mData->mAccessError.getComponent()).c_str(),
981 Utf8Str(mData->mAccessError.getText()));
982 aAccessError = errorInfo;
983 }
984
985 return rc;
986}
987
988HRESULT Machine::getName(com::Utf8Str &aName)
989{
990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
991
992 aName = mUserData->s.strName;
993
994 return S_OK;
995}
996
997HRESULT Machine::setName(const com::Utf8Str &aName)
998{
999 // prohibit setting a UUID only as the machine name, or else it can
1000 // never be found by findMachine()
1001 Guid test(aName);
1002
1003 if (test.isValid())
1004 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1005
1006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1007
1008 HRESULT rc = i_checkStateDependency(MutableStateDep);
1009 if (FAILED(rc)) return rc;
1010
1011 i_setModified(IsModified_MachineData);
1012 mUserData.backup();
1013 mUserData->s.strName = aName;
1014
1015 return S_OK;
1016}
1017
1018HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1019{
1020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 aDescription = mUserData->s.strDescription;
1023
1024 return S_OK;
1025}
1026
1027HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1028{
1029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 // this can be done in principle in any state as it doesn't affect the VM
1032 // significantly, but play safe by not messing around while complex
1033 // activities are going on
1034 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1035 if (FAILED(rc)) return rc;
1036
1037 i_setModified(IsModified_MachineData);
1038 mUserData.backup();
1039 mUserData->s.strDescription = aDescription;
1040
1041 return S_OK;
1042}
1043
1044HRESULT Machine::getId(com::Guid &aId)
1045{
1046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1047
1048 aId = mData->mUuid;
1049
1050 return S_OK;
1051}
1052
1053HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1054{
1055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1056 aGroups.resize(mUserData->s.llGroups.size());
1057 size_t i = 0;
1058 for (StringsList::const_iterator
1059 it = mUserData->s.llGroups.begin();
1060 it != mUserData->s.llGroups.end();
1061 ++it, ++i)
1062 aGroups[i] = (*it);
1063
1064 return S_OK;
1065}
1066
1067HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1068{
1069 StringsList llGroups;
1070 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1071 if (FAILED(rc))
1072 return rc;
1073
1074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1075
1076 rc = i_checkStateDependency(MutableOrSavedStateDep);
1077 if (FAILED(rc)) return rc;
1078
1079 i_setModified(IsModified_MachineData);
1080 mUserData.backup();
1081 mUserData->s.llGroups = llGroups;
1082
1083 return S_OK;
1084}
1085
1086HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1087{
1088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1089
1090 aOSTypeId = mUserData->s.strOsType;
1091
1092 return S_OK;
1093}
1094
1095HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1096{
1097 /* look up the object by Id to check it is valid */
1098 ComObjPtr<GuestOSType> pGuestOSType;
1099 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1100
1101 /* when setting, always use the "etalon" value for consistency -- lookup
1102 * by ID is case-insensitive and the input value may have different case */
1103 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1104
1105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1106
1107 HRESULT rc = i_checkStateDependency(MutableStateDep);
1108 if (FAILED(rc)) return rc;
1109
1110 i_setModified(IsModified_MachineData);
1111 mUserData.backup();
1112 mUserData->s.strOsType = osTypeId;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1118{
1119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 *aFirmwareType = mHWData->mFirmwareType;
1122
1123 return S_OK;
1124}
1125
1126HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1127{
1128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1129
1130 HRESULT rc = i_checkStateDependency(MutableStateDep);
1131 if (FAILED(rc)) return rc;
1132
1133 i_setModified(IsModified_MachineData);
1134 mHWData.backup();
1135 mHWData->mFirmwareType = aFirmwareType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1141{
1142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1145
1146 return S_OK;
1147}
1148
1149HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1150{
1151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1152
1153 HRESULT rc = i_checkStateDependency(MutableStateDep);
1154 if (FAILED(rc)) return rc;
1155
1156 i_setModified(IsModified_MachineData);
1157 mHWData.backup();
1158 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1164{
1165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 *aPointingHIDType = mHWData->mPointingHIDType;
1168
1169 return S_OK;
1170}
1171
1172HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1173{
1174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1175
1176 HRESULT rc = i_checkStateDependency(MutableStateDep);
1177 if (FAILED(rc)) return rc;
1178
1179 i_setModified(IsModified_MachineData);
1180 mHWData.backup();
1181 mHWData->mPointingHIDType = aPointingHIDType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1187{
1188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 *aChipsetType = mHWData->mChipsetType;
1191
1192 return S_OK;
1193}
1194
1195HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1196{
1197 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1198
1199 HRESULT rc = i_checkStateDependency(MutableStateDep);
1200 if (FAILED(rc)) return rc;
1201
1202 if (aChipsetType != mHWData->mChipsetType)
1203 {
1204 i_setModified(IsModified_MachineData);
1205 mHWData.backup();
1206 mHWData->mChipsetType = aChipsetType;
1207
1208 // Resize network adapter array, to be finalized on commit/rollback.
1209 // We must not throw away entries yet, otherwise settings are lost
1210 // without a way to roll back.
1211 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1212 size_t oldCount = mNetworkAdapters.size();
1213 if (newCount > oldCount)
1214 {
1215 mNetworkAdapters.resize(newCount);
1216 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1217 {
1218 unconst(mNetworkAdapters[slot]).createObject();
1219 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1220 }
1221 }
1222 }
1223
1224 return S_OK;
1225}
1226
1227HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1228{
1229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 aParavirtDebug = mHWData->mParavirtDebug;
1232 return S_OK;
1233}
1234
1235HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1236{
1237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1238
1239 HRESULT rc = i_checkStateDependency(MutableStateDep);
1240 if (FAILED(rc)) return rc;
1241
1242 /** @todo Parse/validate options? */
1243 if (aParavirtDebug != mHWData->mParavirtDebug)
1244 {
1245 i_setModified(IsModified_MachineData);
1246 mHWData.backup();
1247 mHWData->mParavirtDebug = aParavirtDebug;
1248 }
1249
1250 return S_OK;
1251}
1252
1253HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1254{
1255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1256
1257 *aParavirtProvider = mHWData->mParavirtProvider;
1258
1259 return S_OK;
1260}
1261
1262HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1263{
1264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1265
1266 HRESULT rc = i_checkStateDependency(MutableStateDep);
1267 if (FAILED(rc)) return rc;
1268
1269 if (aParavirtProvider != mHWData->mParavirtProvider)
1270 {
1271 i_setModified(IsModified_MachineData);
1272 mHWData.backup();
1273 mHWData->mParavirtProvider = aParavirtProvider;
1274 }
1275
1276 return S_OK;
1277}
1278
1279HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1280{
1281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1282
1283 *aParavirtProvider = mHWData->mParavirtProvider;
1284 switch (mHWData->mParavirtProvider)
1285 {
1286 case ParavirtProvider_None:
1287 case ParavirtProvider_HyperV:
1288 case ParavirtProvider_KVM:
1289 case ParavirtProvider_Minimal:
1290 break;
1291
1292 /* Resolve dynamic provider types to the effective types. */
1293 default:
1294 {
1295 ComObjPtr<GuestOSType> pGuestOSType;
1296 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1297 pGuestOSType);
1298 if (FAILED(hrc2) || pGuestOSType.isNull())
1299 {
1300 *aParavirtProvider = ParavirtProvider_None;
1301 break;
1302 }
1303
1304 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1305 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1306
1307 switch (mHWData->mParavirtProvider)
1308 {
1309 case ParavirtProvider_Legacy:
1310 {
1311 if (fOsXGuest)
1312 *aParavirtProvider = ParavirtProvider_Minimal;
1313 else
1314 *aParavirtProvider = ParavirtProvider_None;
1315 break;
1316 }
1317
1318 case ParavirtProvider_Default:
1319 {
1320 if (fOsXGuest)
1321 *aParavirtProvider = ParavirtProvider_Minimal;
1322 else if ( mUserData->s.strOsType == "Windows10"
1323 || mUserData->s.strOsType == "Windows10_64"
1324 || mUserData->s.strOsType == "Windows81"
1325 || mUserData->s.strOsType == "Windows81_64"
1326 || mUserData->s.strOsType == "Windows8"
1327 || mUserData->s.strOsType == "Windows8_64"
1328 || mUserData->s.strOsType == "Windows7"
1329 || mUserData->s.strOsType == "Windows7_64"
1330 || mUserData->s.strOsType == "WindowsVista"
1331 || mUserData->s.strOsType == "WindowsVista_64"
1332 || mUserData->s.strOsType == "Windows2012"
1333 || mUserData->s.strOsType == "Windows2012_64"
1334 || mUserData->s.strOsType == "Windows2008"
1335 || mUserData->s.strOsType == "Windows2008_64")
1336 {
1337 *aParavirtProvider = ParavirtProvider_HyperV;
1338 }
1339 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1340 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1341 || mUserData->s.strOsType == "Linux"
1342 || mUserData->s.strOsType == "Linux_64"
1343 || mUserData->s.strOsType == "ArchLinux"
1344 || mUserData->s.strOsType == "ArchLinux_64"
1345 || mUserData->s.strOsType == "Debian"
1346 || mUserData->s.strOsType == "Debian_64"
1347 || mUserData->s.strOsType == "Fedora"
1348 || mUserData->s.strOsType == "Fedora_64"
1349 || mUserData->s.strOsType == "Gentoo"
1350 || mUserData->s.strOsType == "Gentoo_64"
1351 || mUserData->s.strOsType == "Mandriva"
1352 || mUserData->s.strOsType == "Mandriva_64"
1353 || mUserData->s.strOsType == "OpenSUSE"
1354 || mUserData->s.strOsType == "OpenSUSE_64"
1355 || mUserData->s.strOsType == "Oracle"
1356 || mUserData->s.strOsType == "Oracle_64"
1357 || mUserData->s.strOsType == "RedHat"
1358 || mUserData->s.strOsType == "RedHat_64"
1359 || mUserData->s.strOsType == "Turbolinux"
1360 || mUserData->s.strOsType == "Turbolinux_64"
1361 || mUserData->s.strOsType == "Ubuntu"
1362 || mUserData->s.strOsType == "Ubuntu_64"
1363 || mUserData->s.strOsType == "Xandros"
1364 || mUserData->s.strOsType == "Xandros_64")
1365 {
1366 *aParavirtProvider = ParavirtProvider_KVM;
1367 }
1368 else
1369 *aParavirtProvider = ParavirtProvider_None;
1370 break;
1371 }
1372
1373 default: AssertFailedBreak(); /* Shut up MSC. */
1374 }
1375 break;
1376 }
1377 }
1378
1379 Assert( *aParavirtProvider == ParavirtProvider_None
1380 || *aParavirtProvider == ParavirtProvider_Minimal
1381 || *aParavirtProvider == ParavirtProvider_HyperV
1382 || *aParavirtProvider == ParavirtProvider_KVM);
1383 return S_OK;
1384}
1385
1386HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1387{
1388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1389
1390 aHardwareVersion = mHWData->mHWVersion;
1391
1392 return S_OK;
1393}
1394
1395HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1396{
1397 /* check known version */
1398 Utf8Str hwVersion = aHardwareVersion;
1399 if ( hwVersion.compare("1") != 0
1400 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1401 return setError(E_INVALIDARG,
1402 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1403
1404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1405
1406 HRESULT rc = i_checkStateDependency(MutableStateDep);
1407 if (FAILED(rc)) return rc;
1408
1409 i_setModified(IsModified_MachineData);
1410 mHWData.backup();
1411 mHWData->mHWVersion = aHardwareVersion;
1412
1413 return S_OK;
1414}
1415
1416HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1417{
1418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1419
1420 if (!mHWData->mHardwareUUID.isZero())
1421 aHardwareUUID = mHWData->mHardwareUUID;
1422 else
1423 aHardwareUUID = mData->mUuid;
1424
1425 return S_OK;
1426}
1427
1428HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1429{
1430 if (!aHardwareUUID.isValid())
1431 return E_INVALIDARG;
1432
1433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1434
1435 HRESULT rc = i_checkStateDependency(MutableStateDep);
1436 if (FAILED(rc)) return rc;
1437
1438 i_setModified(IsModified_MachineData);
1439 mHWData.backup();
1440 if (aHardwareUUID == mData->mUuid)
1441 mHWData->mHardwareUUID.clear();
1442 else
1443 mHWData->mHardwareUUID = aHardwareUUID;
1444
1445 return S_OK;
1446}
1447
1448HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1449{
1450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1451
1452 *aMemorySize = mHWData->mMemorySize;
1453
1454 return S_OK;
1455}
1456
1457HRESULT Machine::setMemorySize(ULONG aMemorySize)
1458{
1459 /* check RAM limits */
1460 if ( aMemorySize < MM_RAM_MIN_IN_MB
1461 || aMemorySize > MM_RAM_MAX_IN_MB
1462 )
1463 return setError(E_INVALIDARG,
1464 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1465 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1466
1467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1468
1469 HRESULT rc = i_checkStateDependency(MutableStateDep);
1470 if (FAILED(rc)) return rc;
1471
1472 i_setModified(IsModified_MachineData);
1473 mHWData.backup();
1474 mHWData->mMemorySize = aMemorySize;
1475
1476 return S_OK;
1477}
1478
1479HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1480{
1481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1482
1483 *aCPUCount = mHWData->mCPUCount;
1484
1485 return S_OK;
1486}
1487
1488HRESULT Machine::setCPUCount(ULONG aCPUCount)
1489{
1490 /* check CPU limits */
1491 if ( aCPUCount < SchemaDefs::MinCPUCount
1492 || aCPUCount > SchemaDefs::MaxCPUCount
1493 )
1494 return setError(E_INVALIDARG,
1495 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1496 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1497
1498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1499
1500 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1501 if (mHWData->mCPUHotPlugEnabled)
1502 {
1503 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1504 {
1505 if (mHWData->mCPUAttached[idx])
1506 return setError(E_INVALIDARG,
1507 tr("There is still a CPU attached to socket %lu."
1508 "Detach the CPU before removing the socket"),
1509 aCPUCount, idx+1);
1510 }
1511 }
1512
1513 HRESULT rc = i_checkStateDependency(MutableStateDep);
1514 if (FAILED(rc)) return rc;
1515
1516 i_setModified(IsModified_MachineData);
1517 mHWData.backup();
1518 mHWData->mCPUCount = aCPUCount;
1519
1520 return S_OK;
1521}
1522
1523HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1524{
1525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1526
1527 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1528
1529 return S_OK;
1530}
1531
1532HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1533{
1534 HRESULT rc = S_OK;
1535
1536 /* check throttle limits */
1537 if ( aCPUExecutionCap < 1
1538 || aCPUExecutionCap > 100
1539 )
1540 return setError(E_INVALIDARG,
1541 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1542 aCPUExecutionCap, 1, 100);
1543
1544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1545
1546 alock.release();
1547 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1548 alock.acquire();
1549 if (FAILED(rc)) return rc;
1550
1551 i_setModified(IsModified_MachineData);
1552 mHWData.backup();
1553 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1554
1555 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1556 if (Global::IsOnline(mData->mMachineState))
1557 i_saveSettings(NULL);
1558
1559 return S_OK;
1560}
1561
1562HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1563{
1564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1565
1566 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1567
1568 return S_OK;
1569}
1570
1571HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1572{
1573 HRESULT rc = S_OK;
1574
1575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1576
1577 rc = i_checkStateDependency(MutableStateDep);
1578 if (FAILED(rc)) return rc;
1579
1580 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1581 {
1582 if (aCPUHotPlugEnabled)
1583 {
1584 i_setModified(IsModified_MachineData);
1585 mHWData.backup();
1586
1587 /* Add the amount of CPUs currently attached */
1588 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1589 mHWData->mCPUAttached[i] = true;
1590 }
1591 else
1592 {
1593 /*
1594 * We can disable hotplug only if the amount of maximum CPUs is equal
1595 * to the amount of attached CPUs
1596 */
1597 unsigned cCpusAttached = 0;
1598 unsigned iHighestId = 0;
1599
1600 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1601 {
1602 if (mHWData->mCPUAttached[i])
1603 {
1604 cCpusAttached++;
1605 iHighestId = i;
1606 }
1607 }
1608
1609 if ( (cCpusAttached != mHWData->mCPUCount)
1610 || (iHighestId >= mHWData->mCPUCount))
1611 return setError(E_INVALIDARG,
1612 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1613
1614 i_setModified(IsModified_MachineData);
1615 mHWData.backup();
1616 }
1617 }
1618
1619 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1620
1621 return rc;
1622}
1623
1624HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1625{
1626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1627
1628 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1629
1630 return S_OK;
1631}
1632
1633HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1634{
1635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1636
1637 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1638 if (SUCCEEDED(hrc))
1639 {
1640 i_setModified(IsModified_MachineData);
1641 mHWData.backup();
1642 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1643 }
1644 return hrc;
1645}
1646
1647HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1648{
1649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1650 aCPUProfile = mHWData->mCpuProfile;
1651 return S_OK;
1652}
1653
1654HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1655{
1656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1657 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1658 if (SUCCEEDED(hrc))
1659 {
1660 i_setModified(IsModified_MachineData);
1661 mHWData.backup();
1662 /* Empty equals 'host'. */
1663 if (aCPUProfile.isNotEmpty())
1664 mHWData->mCpuProfile = aCPUProfile;
1665 else
1666 mHWData->mCpuProfile = "host";
1667 }
1668 return hrc;
1669}
1670
1671HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1672{
1673#ifdef VBOX_WITH_USB_CARDREADER
1674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1675
1676 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1677
1678 return S_OK;
1679#else
1680 NOREF(aEmulatedUSBCardReaderEnabled);
1681 return E_NOTIMPL;
1682#endif
1683}
1684
1685HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1686{
1687#ifdef VBOX_WITH_USB_CARDREADER
1688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1689
1690 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1691 if (FAILED(rc)) return rc;
1692
1693 i_setModified(IsModified_MachineData);
1694 mHWData.backup();
1695 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1696
1697 return S_OK;
1698#else
1699 NOREF(aEmulatedUSBCardReaderEnabled);
1700 return E_NOTIMPL;
1701#endif
1702}
1703
1704HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1705{
1706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1707
1708 *aHPETEnabled = mHWData->mHPETEnabled;
1709
1710 return S_OK;
1711}
1712
1713HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1714{
1715 HRESULT rc = S_OK;
1716
1717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1718
1719 rc = i_checkStateDependency(MutableStateDep);
1720 if (FAILED(rc)) return rc;
1721
1722 i_setModified(IsModified_MachineData);
1723 mHWData.backup();
1724
1725 mHWData->mHPETEnabled = aHPETEnabled;
1726
1727 return rc;
1728}
1729
1730HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1731{
1732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1735
1736 return S_OK;
1737}
1738
1739HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1740{
1741 switch (aGraphicsControllerType)
1742 {
1743 case GraphicsControllerType_Null:
1744 case GraphicsControllerType_VBoxVGA:
1745#ifdef VBOX_WITH_VMSVGA
1746 case GraphicsControllerType_VMSVGA:
1747 case GraphicsControllerType_VBoxSVGA:
1748#endif
1749 break;
1750 default:
1751 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1752 }
1753
1754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1755
1756 HRESULT rc = i_checkStateDependency(MutableStateDep);
1757 if (FAILED(rc)) return rc;
1758
1759 i_setModified(IsModified_MachineData);
1760 mHWData.backup();
1761 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1762
1763 return S_OK;
1764}
1765
1766HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1767{
1768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1769
1770 *aVRAMSize = mHWData->mVRAMSize;
1771
1772 return S_OK;
1773}
1774
1775HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1776{
1777 /* check VRAM limits */
1778 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1779 return setError(E_INVALIDARG,
1780 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1781 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1782
1783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1784
1785 HRESULT rc = i_checkStateDependency(MutableStateDep);
1786 if (FAILED(rc)) return rc;
1787
1788 i_setModified(IsModified_MachineData);
1789 mHWData.backup();
1790 mHWData->mVRAMSize = aVRAMSize;
1791
1792 return S_OK;
1793}
1794
1795/** @todo this method should not be public */
1796HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1797{
1798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1799
1800 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1801
1802 return S_OK;
1803}
1804
1805/**
1806 * Set the memory balloon size.
1807 *
1808 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1809 * we have to make sure that we never call IGuest from here.
1810 */
1811HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1812{
1813 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1814#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1815 /* check limits */
1816 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1817 return setError(E_INVALIDARG,
1818 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1819 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1820
1821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1822
1823 i_setModified(IsModified_MachineData);
1824 mHWData.backup();
1825 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1826
1827 return S_OK;
1828#else
1829 NOREF(aMemoryBalloonSize);
1830 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1831#endif
1832}
1833
1834HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1835{
1836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1837
1838 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1839 return S_OK;
1840}
1841
1842HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1843{
1844#ifdef VBOX_WITH_PAGE_SHARING
1845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1846
1847 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1848 i_setModified(IsModified_MachineData);
1849 mHWData.backup();
1850 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1851 return S_OK;
1852#else
1853 NOREF(aPageFusionEnabled);
1854 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1855#endif
1856}
1857
1858HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1859{
1860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1863
1864 return S_OK;
1865}
1866
1867HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1868{
1869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1870
1871 HRESULT rc = i_checkStateDependency(MutableStateDep);
1872 if (FAILED(rc)) return rc;
1873
1874 /** @todo check validity! */
1875
1876 i_setModified(IsModified_MachineData);
1877 mHWData.backup();
1878 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1879
1880 return S_OK;
1881}
1882
1883
1884HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1885{
1886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1887
1888 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1889
1890 return S_OK;
1891}
1892
1893HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1894{
1895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1896
1897 HRESULT rc = i_checkStateDependency(MutableStateDep);
1898 if (FAILED(rc)) return rc;
1899
1900 /** @todo check validity! */
1901 i_setModified(IsModified_MachineData);
1902 mHWData.backup();
1903 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1904
1905 return S_OK;
1906}
1907
1908HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1909{
1910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1911
1912 *aMonitorCount = mHWData->mMonitorCount;
1913
1914 return S_OK;
1915}
1916
1917HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1918{
1919 /* make sure monitor count is a sensible number */
1920 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1921 return setError(E_INVALIDARG,
1922 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1923 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1924
1925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 HRESULT rc = i_checkStateDependency(MutableStateDep);
1928 if (FAILED(rc)) return rc;
1929
1930 i_setModified(IsModified_MachineData);
1931 mHWData.backup();
1932 mHWData->mMonitorCount = aMonitorCount;
1933
1934 return S_OK;
1935}
1936
1937HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1938{
1939 /* mBIOSSettings is constant during life time, no need to lock */
1940 aBIOSSettings = mBIOSSettings;
1941
1942 return S_OK;
1943}
1944
1945HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1946{
1947 /* mRecordingSettings is constant during life time, no need to lock */
1948 aRecordingSettings = mRecordingSettings;
1949
1950 return S_OK;
1951}
1952
1953HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1954{
1955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 switch (aProperty)
1958 {
1959 case CPUPropertyType_PAE:
1960 *aValue = mHWData->mPAEEnabled;
1961 break;
1962
1963 case CPUPropertyType_LongMode:
1964 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1965 *aValue = TRUE;
1966 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1967 *aValue = FALSE;
1968#if HC_ARCH_BITS == 64
1969 else
1970 *aValue = TRUE;
1971#else
1972 else
1973 {
1974 *aValue = FALSE;
1975
1976 ComObjPtr<GuestOSType> pGuestOSType;
1977 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1978 pGuestOSType);
1979 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1980 {
1981 if (pGuestOSType->i_is64Bit())
1982 {
1983 ComObjPtr<Host> pHost = mParent->i_host();
1984 alock.release();
1985
1986 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1987 if (FAILED(hrc2))
1988 *aValue = FALSE;
1989 }
1990 }
1991 }
1992#endif
1993 break;
1994
1995 case CPUPropertyType_TripleFaultReset:
1996 *aValue = mHWData->mTripleFaultReset;
1997 break;
1998
1999 case CPUPropertyType_APIC:
2000 *aValue = mHWData->mAPIC;
2001 break;
2002
2003 case CPUPropertyType_X2APIC:
2004 *aValue = mHWData->mX2APIC;
2005 break;
2006
2007 case CPUPropertyType_IBPBOnVMExit:
2008 *aValue = mHWData->mIBPBOnVMExit;
2009 break;
2010
2011 case CPUPropertyType_IBPBOnVMEntry:
2012 *aValue = mHWData->mIBPBOnVMEntry;
2013 break;
2014
2015 case CPUPropertyType_SpecCtrl:
2016 *aValue = mHWData->mSpecCtrl;
2017 break;
2018
2019 case CPUPropertyType_SpecCtrlByHost:
2020 *aValue = mHWData->mSpecCtrlByHost;
2021 break;
2022
2023 case CPUPropertyType_HWVirt:
2024 *aValue = mHWData->mNestedHWVirt;
2025 break;
2026
2027 default:
2028 return E_INVALIDARG;
2029 }
2030 return S_OK;
2031}
2032
2033HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2034{
2035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2036
2037 HRESULT rc = i_checkStateDependency(MutableStateDep);
2038 if (FAILED(rc)) return rc;
2039
2040 switch (aProperty)
2041 {
2042 case CPUPropertyType_PAE:
2043 i_setModified(IsModified_MachineData);
2044 mHWData.backup();
2045 mHWData->mPAEEnabled = !!aValue;
2046 break;
2047
2048 case CPUPropertyType_LongMode:
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2052 break;
2053
2054 case CPUPropertyType_TripleFaultReset:
2055 i_setModified(IsModified_MachineData);
2056 mHWData.backup();
2057 mHWData->mTripleFaultReset = !!aValue;
2058 break;
2059
2060 case CPUPropertyType_APIC:
2061 if (mHWData->mX2APIC)
2062 aValue = TRUE;
2063 i_setModified(IsModified_MachineData);
2064 mHWData.backup();
2065 mHWData->mAPIC = !!aValue;
2066 break;
2067
2068 case CPUPropertyType_X2APIC:
2069 i_setModified(IsModified_MachineData);
2070 mHWData.backup();
2071 mHWData->mX2APIC = !!aValue;
2072 if (aValue)
2073 mHWData->mAPIC = !!aValue;
2074 break;
2075
2076 case CPUPropertyType_IBPBOnVMExit:
2077 i_setModified(IsModified_MachineData);
2078 mHWData.backup();
2079 mHWData->mIBPBOnVMExit = !!aValue;
2080 break;
2081
2082 case CPUPropertyType_IBPBOnVMEntry:
2083 i_setModified(IsModified_MachineData);
2084 mHWData.backup();
2085 mHWData->mIBPBOnVMEntry = !!aValue;
2086 break;
2087
2088 case CPUPropertyType_SpecCtrl:
2089 i_setModified(IsModified_MachineData);
2090 mHWData.backup();
2091 mHWData->mSpecCtrl = !!aValue;
2092 break;
2093
2094 case CPUPropertyType_SpecCtrlByHost:
2095 i_setModified(IsModified_MachineData);
2096 mHWData.backup();
2097 mHWData->mSpecCtrlByHost = !!aValue;
2098 break;
2099
2100 case CPUPropertyType_HWVirt:
2101 i_setModified(IsModified_MachineData);
2102 mHWData.backup();
2103 mHWData->mNestedHWVirt = !!aValue;
2104 break;
2105
2106 default:
2107 return E_INVALIDARG;
2108 }
2109 return S_OK;
2110}
2111
2112HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2113 ULONG *aValEcx, ULONG *aValEdx)
2114{
2115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2116 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2117 {
2118 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2119 it != mHWData->mCpuIdLeafList.end();
2120 ++it)
2121 {
2122 if (aOrdinal == 0)
2123 {
2124 const settings::CpuIdLeaf &rLeaf= *it;
2125 *aIdx = rLeaf.idx;
2126 *aSubIdx = rLeaf.idxSub;
2127 *aValEax = rLeaf.uEax;
2128 *aValEbx = rLeaf.uEbx;
2129 *aValEcx = rLeaf.uEcx;
2130 *aValEdx = rLeaf.uEdx;
2131 return S_OK;
2132 }
2133 aOrdinal--;
2134 }
2135 }
2136 return E_INVALIDARG;
2137}
2138
2139HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2140{
2141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2142
2143 /*
2144 * Search the list.
2145 */
2146 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2147 {
2148 const settings::CpuIdLeaf &rLeaf= *it;
2149 if ( rLeaf.idx == aIdx
2150 && ( aSubIdx == UINT32_MAX
2151 || rLeaf.idxSub == aSubIdx) )
2152 {
2153 *aValEax = rLeaf.uEax;
2154 *aValEbx = rLeaf.uEbx;
2155 *aValEcx = rLeaf.uEcx;
2156 *aValEdx = rLeaf.uEdx;
2157 return S_OK;
2158 }
2159 }
2160
2161 return E_INVALIDARG;
2162}
2163
2164
2165HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2166{
2167 /*
2168 * Validate input before taking locks and checking state.
2169 */
2170 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2171 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2172 if ( aIdx >= UINT32_C(0x20)
2173 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2174 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2175 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2176
2177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2178 HRESULT rc = i_checkStateDependency(MutableStateDep);
2179 if (FAILED(rc)) return rc;
2180
2181 /*
2182 * Impose a maximum number of leaves.
2183 */
2184 if (mHWData->mCpuIdLeafList.size() > 256)
2185 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2186
2187 /*
2188 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2189 */
2190 i_setModified(IsModified_MachineData);
2191 mHWData.backup();
2192
2193 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2194 {
2195 settings::CpuIdLeaf &rLeaf= *it;
2196 if ( rLeaf.idx == aIdx
2197 && ( aSubIdx == UINT32_MAX
2198 || rLeaf.idxSub == aSubIdx) )
2199 it = mHWData->mCpuIdLeafList.erase(it);
2200 else
2201 ++it;
2202 }
2203
2204 settings::CpuIdLeaf NewLeaf;
2205 NewLeaf.idx = aIdx;
2206 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2207 NewLeaf.uEax = aValEax;
2208 NewLeaf.uEbx = aValEbx;
2209 NewLeaf.uEcx = aValEcx;
2210 NewLeaf.uEdx = aValEdx;
2211 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2212 return S_OK;
2213}
2214
2215HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2216{
2217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2218
2219 HRESULT rc = i_checkStateDependency(MutableStateDep);
2220 if (FAILED(rc)) return rc;
2221
2222 /*
2223 * Do the removal.
2224 */
2225 bool fModified = false;
2226 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2227 {
2228 settings::CpuIdLeaf &rLeaf= *it;
2229 if ( rLeaf.idx == aIdx
2230 && ( aSubIdx == UINT32_MAX
2231 || rLeaf.idxSub == aSubIdx) )
2232 {
2233 if (!fModified)
2234 {
2235 fModified = true;
2236 i_setModified(IsModified_MachineData);
2237 mHWData.backup();
2238 }
2239 it = mHWData->mCpuIdLeafList.erase(it);
2240 }
2241 else
2242 ++it;
2243 }
2244
2245 return S_OK;
2246}
2247
2248HRESULT Machine::removeAllCPUIDLeaves()
2249{
2250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2251
2252 HRESULT rc = i_checkStateDependency(MutableStateDep);
2253 if (FAILED(rc)) return rc;
2254
2255 if (mHWData->mCpuIdLeafList.size() > 0)
2256 {
2257 i_setModified(IsModified_MachineData);
2258 mHWData.backup();
2259
2260 mHWData->mCpuIdLeafList.clear();
2261 }
2262
2263 return S_OK;
2264}
2265HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2266{
2267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2268
2269 switch(aProperty)
2270 {
2271 case HWVirtExPropertyType_Enabled:
2272 *aValue = mHWData->mHWVirtExEnabled;
2273 break;
2274
2275 case HWVirtExPropertyType_VPID:
2276 *aValue = mHWData->mHWVirtExVPIDEnabled;
2277 break;
2278
2279 case HWVirtExPropertyType_NestedPaging:
2280 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2281 break;
2282
2283 case HWVirtExPropertyType_UnrestrictedExecution:
2284 *aValue = mHWData->mHWVirtExUXEnabled;
2285 break;
2286
2287 case HWVirtExPropertyType_LargePages:
2288 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2289#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2290 *aValue = FALSE;
2291#endif
2292 break;
2293
2294 case HWVirtExPropertyType_Force:
2295 *aValue = mHWData->mHWVirtExForceEnabled;
2296 break;
2297
2298 case HWVirtExPropertyType_UseNativeApi:
2299 *aValue = mHWData->mHWVirtExUseNativeApi;
2300 break;
2301
2302 default:
2303 return E_INVALIDARG;
2304 }
2305 return S_OK;
2306}
2307
2308HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2309{
2310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2311
2312 HRESULT rc = i_checkStateDependency(MutableStateDep);
2313 if (FAILED(rc)) return rc;
2314
2315 switch (aProperty)
2316 {
2317 case HWVirtExPropertyType_Enabled:
2318 i_setModified(IsModified_MachineData);
2319 mHWData.backup();
2320 mHWData->mHWVirtExEnabled = !!aValue;
2321 break;
2322
2323 case HWVirtExPropertyType_VPID:
2324 i_setModified(IsModified_MachineData);
2325 mHWData.backup();
2326 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2327 break;
2328
2329 case HWVirtExPropertyType_NestedPaging:
2330 i_setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2333 break;
2334
2335 case HWVirtExPropertyType_UnrestrictedExecution:
2336 i_setModified(IsModified_MachineData);
2337 mHWData.backup();
2338 mHWData->mHWVirtExUXEnabled = !!aValue;
2339 break;
2340
2341 case HWVirtExPropertyType_LargePages:
2342 i_setModified(IsModified_MachineData);
2343 mHWData.backup();
2344 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2345 break;
2346
2347 case HWVirtExPropertyType_Force:
2348 i_setModified(IsModified_MachineData);
2349 mHWData.backup();
2350 mHWData->mHWVirtExForceEnabled = !!aValue;
2351 break;
2352
2353 case HWVirtExPropertyType_UseNativeApi:
2354 i_setModified(IsModified_MachineData);
2355 mHWData.backup();
2356 mHWData->mHWVirtExUseNativeApi = !!aValue;
2357 break;
2358
2359 default:
2360 return E_INVALIDARG;
2361 }
2362
2363 return S_OK;
2364}
2365
2366HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2367{
2368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2369
2370 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2371
2372 return S_OK;
2373}
2374
2375HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2376{
2377 /** @todo (r=dmik):
2378 * 1. Allow to change the name of the snapshot folder containing snapshots
2379 * 2. Rename the folder on disk instead of just changing the property
2380 * value (to be smart and not to leave garbage). Note that it cannot be
2381 * done here because the change may be rolled back. Thus, the right
2382 * place is #saveSettings().
2383 */
2384
2385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2386
2387 HRESULT rc = i_checkStateDependency(MutableStateDep);
2388 if (FAILED(rc)) return rc;
2389
2390 if (!mData->mCurrentSnapshot.isNull())
2391 return setError(E_FAIL,
2392 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2393
2394 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2395
2396 if (strSnapshotFolder.isEmpty())
2397 strSnapshotFolder = "Snapshots";
2398 int vrc = i_calculateFullPath(strSnapshotFolder,
2399 strSnapshotFolder);
2400 if (RT_FAILURE(vrc))
2401 return setErrorBoth(E_FAIL, vrc,
2402 tr("Invalid snapshot folder '%s' (%Rrc)"),
2403 strSnapshotFolder.c_str(), vrc);
2404
2405 i_setModified(IsModified_MachineData);
2406 mUserData.backup();
2407
2408 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2409
2410 return S_OK;
2411}
2412
2413HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2414{
2415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2416
2417 aMediumAttachments.resize(mMediumAttachments->size());
2418 size_t i = 0;
2419 for (MediumAttachmentList::const_iterator
2420 it = mMediumAttachments->begin();
2421 it != mMediumAttachments->end();
2422 ++it, ++i)
2423 aMediumAttachments[i] = *it;
2424
2425 return S_OK;
2426}
2427
2428HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2429{
2430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2431
2432 Assert(!!mVRDEServer);
2433
2434 aVRDEServer = mVRDEServer;
2435
2436 return S_OK;
2437}
2438
2439HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2440{
2441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 aAudioAdapter = mAudioAdapter;
2444
2445 return S_OK;
2446}
2447
2448HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2449{
2450#ifdef VBOX_WITH_VUSB
2451 clearError();
2452 MultiResult rc(S_OK);
2453
2454# ifdef VBOX_WITH_USB
2455 rc = mParent->i_host()->i_checkUSBProxyService();
2456 if (FAILED(rc)) return rc;
2457# endif
2458
2459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2460
2461 aUSBControllers.resize(mUSBControllers->size());
2462 size_t i = 0;
2463 for (USBControllerList::const_iterator
2464 it = mUSBControllers->begin();
2465 it != mUSBControllers->end();
2466 ++it, ++i)
2467 aUSBControllers[i] = *it;
2468
2469 return S_OK;
2470#else
2471 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2472 * extended error info to indicate that USB is simply not available
2473 * (w/o treating it as a failure), for example, as in OSE */
2474 NOREF(aUSBControllers);
2475 ReturnComNotImplemented();
2476#endif /* VBOX_WITH_VUSB */
2477}
2478
2479HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2480{
2481#ifdef VBOX_WITH_VUSB
2482 clearError();
2483 MultiResult rc(S_OK);
2484
2485# ifdef VBOX_WITH_USB
2486 rc = mParent->i_host()->i_checkUSBProxyService();
2487 if (FAILED(rc)) return rc;
2488# endif
2489
2490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
2492 aUSBDeviceFilters = mUSBDeviceFilters;
2493 return rc;
2494#else
2495 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2496 * extended error info to indicate that USB is simply not available
2497 * (w/o treating it as a failure), for example, as in OSE */
2498 NOREF(aUSBDeviceFilters);
2499 ReturnComNotImplemented();
2500#endif /* VBOX_WITH_VUSB */
2501}
2502
2503HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2504{
2505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2506
2507 aSettingsFilePath = mData->m_strConfigFileFull;
2508
2509 return S_OK;
2510}
2511
2512HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2513{
2514 RT_NOREF(aSettingsFilePath);
2515 ReturnComNotImplemented();
2516}
2517
2518HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2519{
2520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2521
2522 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2523 if (FAILED(rc)) return rc;
2524
2525 if (!mData->pMachineConfigFile->fileExists())
2526 // this is a new machine, and no config file exists yet:
2527 *aSettingsModified = TRUE;
2528 else
2529 *aSettingsModified = (mData->flModifications != 0);
2530
2531 return S_OK;
2532}
2533
2534HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2535{
2536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2537
2538 *aSessionState = mData->mSession.mState;
2539
2540 return S_OK;
2541}
2542
2543HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2544{
2545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 aSessionName = mData->mSession.mName;
2548
2549 return S_OK;
2550}
2551
2552HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2553{
2554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2555
2556 *aSessionPID = mData->mSession.mPID;
2557
2558 return S_OK;
2559}
2560
2561HRESULT Machine::getState(MachineState_T *aState)
2562{
2563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2564
2565 *aState = mData->mMachineState;
2566 Assert(mData->mMachineState != MachineState_Null);
2567
2568 return S_OK;
2569}
2570
2571HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2572{
2573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2574
2575 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2576
2577 return S_OK;
2578}
2579
2580HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2581{
2582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2583
2584 aStateFilePath = mSSData->strStateFilePath;
2585
2586 return S_OK;
2587}
2588
2589HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2590{
2591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 i_getLogFolder(aLogFolder);
2594
2595 return S_OK;
2596}
2597
2598HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2599{
2600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2601
2602 aCurrentSnapshot = mData->mCurrentSnapshot;
2603
2604 return S_OK;
2605}
2606
2607HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2608{
2609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2610
2611 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2612 ? 0
2613 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2614
2615 return S_OK;
2616}
2617
2618HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2619{
2620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 /* Note: for machines with no snapshots, we always return FALSE
2623 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2624 * reasons :) */
2625
2626 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2627 ? FALSE
2628 : mData->mCurrentStateModified;
2629
2630 return S_OK;
2631}
2632
2633HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2634{
2635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2636
2637 aSharedFolders.resize(mHWData->mSharedFolders.size());
2638 size_t i = 0;
2639 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2640 it = mHWData->mSharedFolders.begin();
2641 it != mHWData->mSharedFolders.end();
2642 ++it, ++i)
2643 aSharedFolders[i] = *it;
2644
2645 return S_OK;
2646}
2647
2648HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2649{
2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 *aClipboardMode = mHWData->mClipboardMode;
2653
2654 return S_OK;
2655}
2656
2657HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2658{
2659 HRESULT rc = S_OK;
2660
2661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 alock.release();
2664 rc = i_onClipboardModeChange(aClipboardMode);
2665 alock.acquire();
2666 if (FAILED(rc)) return rc;
2667
2668 i_setModified(IsModified_MachineData);
2669 mHWData.backup();
2670 mHWData->mClipboardMode = aClipboardMode;
2671
2672 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2673 if (Global::IsOnline(mData->mMachineState))
2674 i_saveSettings(NULL);
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2680{
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 *aDnDMode = mHWData->mDnDMode;
2684
2685 return S_OK;
2686}
2687
2688HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2689{
2690 HRESULT rc = S_OK;
2691
2692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2693
2694 alock.release();
2695 rc = i_onDnDModeChange(aDnDMode);
2696
2697 alock.acquire();
2698 if (FAILED(rc)) return rc;
2699
2700 i_setModified(IsModified_MachineData);
2701 mHWData.backup();
2702 mHWData->mDnDMode = aDnDMode;
2703
2704 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2705 if (Global::IsOnline(mData->mMachineState))
2706 i_saveSettings(NULL);
2707
2708 return S_OK;
2709}
2710
2711HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2712{
2713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 aStorageControllers.resize(mStorageControllers->size());
2716 size_t i = 0;
2717 for (StorageControllerList::const_iterator
2718 it = mStorageControllers->begin();
2719 it != mStorageControllers->end();
2720 ++it, ++i)
2721 aStorageControllers[i] = *it;
2722
2723 return S_OK;
2724}
2725
2726HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2727{
2728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2729
2730 *aEnabled = mUserData->s.fTeleporterEnabled;
2731
2732 return S_OK;
2733}
2734
2735HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2736{
2737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2738
2739 /* Only allow it to be set to true when PoweredOff or Aborted.
2740 (Clearing it is always permitted.) */
2741 if ( aTeleporterEnabled
2742 && mData->mRegistered
2743 && ( !i_isSessionMachine()
2744 || ( mData->mMachineState != MachineState_PoweredOff
2745 && mData->mMachineState != MachineState_Teleported
2746 && mData->mMachineState != MachineState_Aborted
2747 )
2748 )
2749 )
2750 return setError(VBOX_E_INVALID_VM_STATE,
2751 tr("The machine is not powered off (state is %s)"),
2752 Global::stringifyMachineState(mData->mMachineState));
2753
2754 i_setModified(IsModified_MachineData);
2755 mUserData.backup();
2756 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2757
2758 return S_OK;
2759}
2760
2761HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2762{
2763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2764
2765 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2771{
2772 if (aTeleporterPort >= _64K)
2773 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2774
2775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2776
2777 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2778 if (FAILED(rc)) return rc;
2779
2780 i_setModified(IsModified_MachineData);
2781 mUserData.backup();
2782 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2797{
2798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2801 if (FAILED(rc)) return rc;
2802
2803 i_setModified(IsModified_MachineData);
2804 mUserData.backup();
2805 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2806
2807 return S_OK;
2808}
2809
2810HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2811{
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2819{
2820 /*
2821 * Hash the password first.
2822 */
2823 com::Utf8Str aT = aTeleporterPassword;
2824
2825 if (!aT.isEmpty())
2826 {
2827 if (VBoxIsPasswordHashed(&aT))
2828 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2829 VBoxHashPassword(&aT);
2830 }
2831
2832 /*
2833 * Do the update.
2834 */
2835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2836 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2837 if (SUCCEEDED(hrc))
2838 {
2839 i_setModified(IsModified_MachineData);
2840 mUserData.backup();
2841 mUserData->s.strTeleporterPassword = aT;
2842 }
2843
2844 return hrc;
2845}
2846
2847HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2848{
2849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2850
2851 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2852 return S_OK;
2853}
2854
2855HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2856{
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 /** @todo deal with running state change. */
2860 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2861 if (FAILED(rc)) return rc;
2862
2863 i_setModified(IsModified_MachineData);
2864 mUserData.backup();
2865 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2866 return S_OK;
2867}
2868
2869HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 /** @todo deal with running state change. */
2882 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2883 if (FAILED(rc)) return rc;
2884
2885 i_setModified(IsModified_MachineData);
2886 mUserData.backup();
2887 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2888 return S_OK;
2889}
2890
2891HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2892{
2893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2896 return S_OK;
2897}
2898
2899HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2900{
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 /** @todo deal with running state change. */
2904 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2905 if (FAILED(rc)) return rc;
2906
2907 i_setModified(IsModified_MachineData);
2908 mUserData.backup();
2909 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2910 return S_OK;
2911}
2912
2913HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2914{
2915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2923{
2924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 /** @todo deal with running state change. */
2927 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2928 if (FAILED(rc)) return rc;
2929
2930 i_setModified(IsModified_MachineData);
2931 mUserData.backup();
2932 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2933
2934 return S_OK;
2935}
2936
2937HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2938{
2939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2940
2941 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2942 return S_OK;
2943}
2944
2945HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2946{
2947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2948
2949 /** @todo deal with running state change. */
2950 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2951 if (FAILED(rc)) return rc;
2952
2953 i_setModified(IsModified_MachineData);
2954 mUserData.backup();
2955 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2956 return S_OK;
2957}
2958
2959HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2960{
2961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2964
2965 return S_OK;
2966}
2967
2968HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2969{
2970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2971
2972 /* Only allow it to be set to true when PoweredOff or Aborted.
2973 (Clearing it is always permitted.) */
2974 if ( aRTCUseUTC
2975 && mData->mRegistered
2976 && ( !i_isSessionMachine()
2977 || ( mData->mMachineState != MachineState_PoweredOff
2978 && mData->mMachineState != MachineState_Teleported
2979 && mData->mMachineState != MachineState_Aborted
2980 )
2981 )
2982 )
2983 return setError(VBOX_E_INVALID_VM_STATE,
2984 tr("The machine is not powered off (state is %s)"),
2985 Global::stringifyMachineState(mData->mMachineState));
2986
2987 i_setModified(IsModified_MachineData);
2988 mUserData.backup();
2989 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2990
2991 return S_OK;
2992}
2993
2994HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2995{
2996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2999
3000 return S_OK;
3001}
3002
3003HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3004{
3005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3006
3007 HRESULT rc = i_checkStateDependency(MutableStateDep);
3008 if (FAILED(rc)) return rc;
3009
3010 i_setModified(IsModified_MachineData);
3011 mHWData.backup();
3012 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3013
3014 return S_OK;
3015}
3016
3017HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3018{
3019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3020
3021 *aIOCacheSize = mHWData->mIOCacheSize;
3022
3023 return S_OK;
3024}
3025
3026HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3027{
3028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3029
3030 HRESULT rc = i_checkStateDependency(MutableStateDep);
3031 if (FAILED(rc)) return rc;
3032
3033 i_setModified(IsModified_MachineData);
3034 mHWData.backup();
3035 mHWData->mIOCacheSize = aIOCacheSize;
3036
3037 return S_OK;
3038}
3039
3040
3041/**
3042 * @note Locks objects!
3043 */
3044HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3045 LockType_T aLockType)
3046{
3047 /* check the session state */
3048 SessionState_T state;
3049 HRESULT rc = aSession->COMGETTER(State)(&state);
3050 if (FAILED(rc)) return rc;
3051
3052 if (state != SessionState_Unlocked)
3053 return setError(VBOX_E_INVALID_OBJECT_STATE,
3054 tr("The given session is busy"));
3055
3056 // get the client's IInternalSessionControl interface
3057 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3058 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3059 E_INVALIDARG);
3060
3061 // session name (only used in some code paths)
3062 Utf8Str strSessionName;
3063
3064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 if (!mData->mRegistered)
3067 return setError(E_UNEXPECTED,
3068 tr("The machine '%s' is not registered"),
3069 mUserData->s.strName.c_str());
3070
3071 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3072
3073 SessionState_T oldState = mData->mSession.mState;
3074 /* Hack: in case the session is closing and there is a progress object
3075 * which allows waiting for the session to be closed, take the opportunity
3076 * and do a limited wait (max. 1 second). This helps a lot when the system
3077 * is busy and thus session closing can take a little while. */
3078 if ( mData->mSession.mState == SessionState_Unlocking
3079 && mData->mSession.mProgress)
3080 {
3081 alock.release();
3082 mData->mSession.mProgress->WaitForCompletion(1000);
3083 alock.acquire();
3084 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3085 }
3086
3087 // try again now
3088 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3089 // (i.e. session machine exists)
3090 && (aLockType == LockType_Shared) // caller wants a shared link to the
3091 // existing session that holds the write lock:
3092 )
3093 {
3094 // OK, share the session... we are now dealing with three processes:
3095 // 1) VBoxSVC (where this code runs);
3096 // 2) process C: the caller's client process (who wants a shared session);
3097 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3098
3099 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3100 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3101 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3102 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3103 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3104
3105 /*
3106 * Release the lock before calling the client process. It's safe here
3107 * since the only thing to do after we get the lock again is to add
3108 * the remote control to the list (which doesn't directly influence
3109 * anything).
3110 */
3111 alock.release();
3112
3113 // get the console of the session holding the write lock (this is a remote call)
3114 ComPtr<IConsole> pConsoleW;
3115 if (mData->mSession.mLockType == LockType_VM)
3116 {
3117 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3118 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3119 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3120 if (FAILED(rc))
3121 // the failure may occur w/o any error info (from RPC), so provide one
3122 return setError(VBOX_E_VM_ERROR,
3123 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3124 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3125 }
3126
3127 // share the session machine and W's console with the caller's session
3128 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3129 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3130 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3131
3132 if (FAILED(rc))
3133 // the failure may occur w/o any error info (from RPC), so provide one
3134 return setError(VBOX_E_VM_ERROR,
3135 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3136 alock.acquire();
3137
3138 // need to revalidate the state after acquiring the lock again
3139 if (mData->mSession.mState != SessionState_Locked)
3140 {
3141 pSessionControl->Uninitialize();
3142 return setError(VBOX_E_INVALID_SESSION_STATE,
3143 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3144 mUserData->s.strName.c_str());
3145 }
3146
3147 // add the caller's session to the list
3148 mData->mSession.mRemoteControls.push_back(pSessionControl);
3149 }
3150 else if ( mData->mSession.mState == SessionState_Locked
3151 || mData->mSession.mState == SessionState_Unlocking
3152 )
3153 {
3154 // sharing not permitted, or machine still unlocking:
3155 return setError(VBOX_E_INVALID_OBJECT_STATE,
3156 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3157 mUserData->s.strName.c_str());
3158 }
3159 else
3160 {
3161 // machine is not locked: then write-lock the machine (create the session machine)
3162
3163 // must not be busy
3164 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3165
3166 // get the caller's session PID
3167 RTPROCESS pid = NIL_RTPROCESS;
3168 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3169 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3170 Assert(pid != NIL_RTPROCESS);
3171
3172 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3173
3174 if (fLaunchingVMProcess)
3175 {
3176 if (mData->mSession.mPID == NIL_RTPROCESS)
3177 {
3178 // two or more clients racing for a lock, the one which set the
3179 // session state to Spawning will win, the others will get an
3180 // error as we can't decide here if waiting a little would help
3181 // (only for shared locks this would avoid an error)
3182 return setError(VBOX_E_INVALID_OBJECT_STATE,
3183 tr("The machine '%s' already has a lock request pending"),
3184 mUserData->s.strName.c_str());
3185 }
3186
3187 // this machine is awaiting for a spawning session to be opened:
3188 // then the calling process must be the one that got started by
3189 // LaunchVMProcess()
3190
3191 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3192 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3193
3194#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3195 /* Hardened windows builds spawns three processes when a VM is
3196 launched, the 3rd one is the one that will end up here. */
3197 RTPROCESS ppid;
3198 int rc = RTProcQueryParent(pid, &ppid);
3199 if (RT_SUCCESS(rc))
3200 rc = RTProcQueryParent(ppid, &ppid);
3201 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3202 || rc == VERR_ACCESS_DENIED)
3203 {
3204 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3205 mData->mSession.mPID = pid;
3206 }
3207#endif
3208
3209 if (mData->mSession.mPID != pid)
3210 return setError(E_ACCESSDENIED,
3211 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3212 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3213 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3214 }
3215
3216 // create the mutable SessionMachine from the current machine
3217 ComObjPtr<SessionMachine> sessionMachine;
3218 sessionMachine.createObject();
3219 rc = sessionMachine->init(this);
3220 AssertComRC(rc);
3221
3222 /* NOTE: doing return from this function after this point but
3223 * before the end is forbidden since it may call SessionMachine::uninit()
3224 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3225 * lock while still holding the Machine lock in alock so that a deadlock
3226 * is possible due to the wrong lock order. */
3227
3228 if (SUCCEEDED(rc))
3229 {
3230 /*
3231 * Set the session state to Spawning to protect against subsequent
3232 * attempts to open a session and to unregister the machine after
3233 * we release the lock.
3234 */
3235 SessionState_T origState = mData->mSession.mState;
3236 mData->mSession.mState = SessionState_Spawning;
3237
3238#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3239 /* Get the client token ID to be passed to the client process */
3240 Utf8Str strTokenId;
3241 sessionMachine->i_getTokenId(strTokenId);
3242 Assert(!strTokenId.isEmpty());
3243#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3244 /* Get the client token to be passed to the client process */
3245 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3246 /* The token is now "owned" by pToken, fix refcount */
3247 if (!pToken.isNull())
3248 pToken->Release();
3249#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3250
3251 /*
3252 * Release the lock before calling the client process -- it will call
3253 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3254 * because the state is Spawning, so that LaunchVMProcess() and
3255 * LockMachine() calls will fail. This method, called before we
3256 * acquire the lock again, will fail because of the wrong PID.
3257 *
3258 * Note that mData->mSession.mRemoteControls accessed outside
3259 * the lock may not be modified when state is Spawning, so it's safe.
3260 */
3261 alock.release();
3262
3263 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3264#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3265 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3266#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3267 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3268 /* Now the token is owned by the client process. */
3269 pToken.setNull();
3270#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3271 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3272
3273 /* The failure may occur w/o any error info (from RPC), so provide one */
3274 if (FAILED(rc))
3275 setError(VBOX_E_VM_ERROR,
3276 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3277
3278 // get session name, either to remember or to compare against
3279 // the already known session name.
3280 {
3281 Bstr bstrSessionName;
3282 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3283 if (SUCCEEDED(rc2))
3284 strSessionName = bstrSessionName;
3285 }
3286
3287 if ( SUCCEEDED(rc)
3288 && fLaunchingVMProcess
3289 )
3290 {
3291 /* complete the remote session initialization */
3292
3293 /* get the console from the direct session */
3294 ComPtr<IConsole> console;
3295 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3296 ComAssertComRC(rc);
3297
3298 if (SUCCEEDED(rc) && !console)
3299 {
3300 ComAssert(!!console);
3301 rc = E_FAIL;
3302 }
3303
3304 /* assign machine & console to the remote session */
3305 if (SUCCEEDED(rc))
3306 {
3307 /*
3308 * after LaunchVMProcess(), the first and the only
3309 * entry in remoteControls is that remote session
3310 */
3311 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3312 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3313 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3314
3315 /* The failure may occur w/o any error info (from RPC), so provide one */
3316 if (FAILED(rc))
3317 setError(VBOX_E_VM_ERROR,
3318 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3319 }
3320
3321 if (FAILED(rc))
3322 pSessionControl->Uninitialize();
3323 }
3324
3325 /* acquire the lock again */
3326 alock.acquire();
3327
3328 /* Restore the session state */
3329 mData->mSession.mState = origState;
3330 }
3331
3332 // finalize spawning anyway (this is why we don't return on errors above)
3333 if (fLaunchingVMProcess)
3334 {
3335 Assert(mData->mSession.mName == strSessionName);
3336 /* Note that the progress object is finalized later */
3337 /** @todo Consider checking mData->mSession.mProgress for cancellation
3338 * around here. */
3339
3340 /* We don't reset mSession.mPID here because it is necessary for
3341 * SessionMachine::uninit() to reap the child process later. */
3342
3343 if (FAILED(rc))
3344 {
3345 /* Close the remote session, remove the remote control from the list
3346 * and reset session state to Closed (@note keep the code in sync
3347 * with the relevant part in checkForSpawnFailure()). */
3348
3349 Assert(mData->mSession.mRemoteControls.size() == 1);
3350 if (mData->mSession.mRemoteControls.size() == 1)
3351 {
3352 ErrorInfoKeeper eik;
3353 mData->mSession.mRemoteControls.front()->Uninitialize();
3354 }
3355
3356 mData->mSession.mRemoteControls.clear();
3357 mData->mSession.mState = SessionState_Unlocked;
3358 }
3359 }
3360 else
3361 {
3362 /* memorize PID of the directly opened session */
3363 if (SUCCEEDED(rc))
3364 mData->mSession.mPID = pid;
3365 }
3366
3367 if (SUCCEEDED(rc))
3368 {
3369 mData->mSession.mLockType = aLockType;
3370 /* memorize the direct session control and cache IUnknown for it */
3371 mData->mSession.mDirectControl = pSessionControl;
3372 mData->mSession.mState = SessionState_Locked;
3373 if (!fLaunchingVMProcess)
3374 mData->mSession.mName = strSessionName;
3375 /* associate the SessionMachine with this Machine */
3376 mData->mSession.mMachine = sessionMachine;
3377
3378 /* request an IUnknown pointer early from the remote party for later
3379 * identity checks (it will be internally cached within mDirectControl
3380 * at least on XPCOM) */
3381 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3382 NOREF(unk);
3383 }
3384
3385 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3386 * would break the lock order */
3387 alock.release();
3388
3389 /* uninitialize the created session machine on failure */
3390 if (FAILED(rc))
3391 sessionMachine->uninit();
3392 }
3393
3394 if (SUCCEEDED(rc))
3395 {
3396 /*
3397 * tell the client watcher thread to update the set of
3398 * machines that have open sessions
3399 */
3400 mParent->i_updateClientWatcher();
3401
3402 if (oldState != SessionState_Locked)
3403 /* fire an event */
3404 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3405 }
3406
3407 return rc;
3408}
3409
3410/**
3411 * @note Locks objects!
3412 */
3413HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3414 const com::Utf8Str &aName,
3415 const com::Utf8Str &aEnvironment,
3416 ComPtr<IProgress> &aProgress)
3417{
3418 Utf8Str strFrontend(aName);
3419 /* "emergencystop" doesn't need the session, so skip the checks/interface
3420 * retrieval. This code doesn't quite fit in here, but introducing a
3421 * special API method would be even more effort, and would require explicit
3422 * support by every API client. It's better to hide the feature a bit. */
3423 if (strFrontend != "emergencystop")
3424 CheckComArgNotNull(aSession);
3425
3426 HRESULT rc = S_OK;
3427 if (strFrontend.isEmpty())
3428 {
3429 Bstr bstrFrontend;
3430 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3431 if (FAILED(rc))
3432 return rc;
3433 strFrontend = bstrFrontend;
3434 if (strFrontend.isEmpty())
3435 {
3436 ComPtr<ISystemProperties> systemProperties;
3437 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3438 if (FAILED(rc))
3439 return rc;
3440 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3441 if (FAILED(rc))
3442 return rc;
3443 strFrontend = bstrFrontend;
3444 }
3445 /* paranoia - emergencystop is not a valid default */
3446 if (strFrontend == "emergencystop")
3447 strFrontend = Utf8Str::Empty;
3448 }
3449 /* default frontend: Qt GUI */
3450 if (strFrontend.isEmpty())
3451 strFrontend = "GUI/Qt";
3452
3453 if (strFrontend != "emergencystop")
3454 {
3455 /* check the session state */
3456 SessionState_T state;
3457 rc = aSession->COMGETTER(State)(&state);
3458 if (FAILED(rc))
3459 return rc;
3460
3461 if (state != SessionState_Unlocked)
3462 return setError(VBOX_E_INVALID_OBJECT_STATE,
3463 tr("The given session is busy"));
3464
3465 /* get the IInternalSessionControl interface */
3466 ComPtr<IInternalSessionControl> control(aSession);
3467 ComAssertMsgRet(!control.isNull(),
3468 ("No IInternalSessionControl interface"),
3469 E_INVALIDARG);
3470
3471 /* get the teleporter enable state for the progress object init. */
3472 BOOL fTeleporterEnabled;
3473 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3474 if (FAILED(rc))
3475 return rc;
3476
3477 /* create a progress object */
3478 ComObjPtr<ProgressProxy> progress;
3479 progress.createObject();
3480 rc = progress->init(mParent,
3481 static_cast<IMachine*>(this),
3482 Bstr(tr("Starting VM")).raw(),
3483 TRUE /* aCancelable */,
3484 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3485 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3486 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3487 2 /* uFirstOperationWeight */,
3488 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3489
3490 if (SUCCEEDED(rc))
3491 {
3492 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3493 if (SUCCEEDED(rc))
3494 {
3495 aProgress = progress;
3496
3497 /* signal the client watcher thread */
3498 mParent->i_updateClientWatcher();
3499
3500 /* fire an event */
3501 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3502 }
3503 }
3504 }
3505 else
3506 {
3507 /* no progress object - either instant success or failure */
3508 aProgress = NULL;
3509
3510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3511
3512 if (mData->mSession.mState != SessionState_Locked)
3513 return setError(VBOX_E_INVALID_OBJECT_STATE,
3514 tr("The machine '%s' is not locked by a session"),
3515 mUserData->s.strName.c_str());
3516
3517 /* must have a VM process associated - do not kill normal API clients
3518 * with an open session */
3519 if (!Global::IsOnline(mData->mMachineState))
3520 return setError(VBOX_E_INVALID_OBJECT_STATE,
3521 tr("The machine '%s' does not have a VM process"),
3522 mUserData->s.strName.c_str());
3523
3524 /* forcibly terminate the VM process */
3525 if (mData->mSession.mPID != NIL_RTPROCESS)
3526 RTProcTerminate(mData->mSession.mPID);
3527
3528 /* signal the client watcher thread, as most likely the client has
3529 * been terminated */
3530 mParent->i_updateClientWatcher();
3531 }
3532
3533 return rc;
3534}
3535
3536HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3537{
3538 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3539 return setError(E_INVALIDARG,
3540 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3541 aPosition, SchemaDefs::MaxBootPosition);
3542
3543 if (aDevice == DeviceType_USB)
3544 return setError(E_NOTIMPL,
3545 tr("Booting from USB device is currently not supported"));
3546
3547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3548
3549 HRESULT rc = i_checkStateDependency(MutableStateDep);
3550 if (FAILED(rc)) return rc;
3551
3552 i_setModified(IsModified_MachineData);
3553 mHWData.backup();
3554 mHWData->mBootOrder[aPosition - 1] = aDevice;
3555
3556 return S_OK;
3557}
3558
3559HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3560{
3561 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3562 return setError(E_INVALIDARG,
3563 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3564 aPosition, SchemaDefs::MaxBootPosition);
3565
3566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3567
3568 *aDevice = mHWData->mBootOrder[aPosition - 1];
3569
3570 return S_OK;
3571}
3572
3573HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3574 LONG aControllerPort,
3575 LONG aDevice,
3576 DeviceType_T aType,
3577 const ComPtr<IMedium> &aMedium)
3578{
3579 IMedium *aM = aMedium;
3580 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3581 aName.c_str(), aControllerPort, aDevice, aType, aM));
3582
3583 // request the host lock first, since might be calling Host methods for getting host drives;
3584 // next, protect the media tree all the while we're in here, as well as our member variables
3585 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3586 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3587
3588 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3589 if (FAILED(rc)) return rc;
3590
3591 /// @todo NEWMEDIA implicit machine registration
3592 if (!mData->mRegistered)
3593 return setError(VBOX_E_INVALID_OBJECT_STATE,
3594 tr("Cannot attach storage devices to an unregistered machine"));
3595
3596 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3597
3598 /* Check for an existing controller. */
3599 ComObjPtr<StorageController> ctl;
3600 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3601 if (FAILED(rc)) return rc;
3602
3603 StorageControllerType_T ctrlType;
3604 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3605 if (FAILED(rc))
3606 return setError(E_FAIL,
3607 tr("Could not get type of controller '%s'"),
3608 aName.c_str());
3609
3610 bool fSilent = false;
3611 Utf8Str strReconfig;
3612
3613 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3614 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3615 if ( mData->mMachineState == MachineState_Paused
3616 && strReconfig == "1")
3617 fSilent = true;
3618
3619 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3620 bool fHotplug = false;
3621 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3622 fHotplug = true;
3623
3624 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3625 return setError(VBOX_E_INVALID_VM_STATE,
3626 tr("Controller '%s' does not support hotplugging"),
3627 aName.c_str());
3628
3629 // check that the port and device are not out of range
3630 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3631 if (FAILED(rc)) return rc;
3632
3633 /* check if the device slot is already busy */
3634 MediumAttachment *pAttachTemp;
3635 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3636 aName,
3637 aControllerPort,
3638 aDevice)))
3639 {
3640 Medium *pMedium = pAttachTemp->i_getMedium();
3641 if (pMedium)
3642 {
3643 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3644 return setError(VBOX_E_OBJECT_IN_USE,
3645 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3646 pMedium->i_getLocationFull().c_str(),
3647 aControllerPort,
3648 aDevice,
3649 aName.c_str());
3650 }
3651 else
3652 return setError(VBOX_E_OBJECT_IN_USE,
3653 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3654 aControllerPort, aDevice, aName.c_str());
3655 }
3656
3657 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3658 if (aMedium && medium.isNull())
3659 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3660
3661 AutoCaller mediumCaller(medium);
3662 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3663
3664 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3665
3666 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3667 && !medium.isNull()
3668 )
3669 return setError(VBOX_E_OBJECT_IN_USE,
3670 tr("Medium '%s' is already attached to this virtual machine"),
3671 medium->i_getLocationFull().c_str());
3672
3673 if (!medium.isNull())
3674 {
3675 MediumType_T mtype = medium->i_getType();
3676 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3677 // For DVDs it's not written to the config file, so needs no global config
3678 // version bump. For floppies it's a new attribute "type", which is ignored
3679 // by older VirtualBox version, so needs no global config version bump either.
3680 // For hard disks this type is not accepted.
3681 if (mtype == MediumType_MultiAttach)
3682 {
3683 // This type is new with VirtualBox 4.0 and therefore requires settings
3684 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3685 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3686 // two reasons: The medium type is a property of the media registry tree, which
3687 // can reside in the global config file (for pre-4.0 media); we would therefore
3688 // possibly need to bump the global config version. We don't want to do that though
3689 // because that might make downgrading to pre-4.0 impossible.
3690 // As a result, we can only use these two new types if the medium is NOT in the
3691 // global registry:
3692 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3693 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3694 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3695 )
3696 return setError(VBOX_E_INVALID_OBJECT_STATE,
3697 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3698 "to machines that were created with VirtualBox 4.0 or later"),
3699 medium->i_getLocationFull().c_str());
3700 }
3701 }
3702
3703 bool fIndirect = false;
3704 if (!medium.isNull())
3705 fIndirect = medium->i_isReadOnly();
3706 bool associate = true;
3707
3708 do
3709 {
3710 if ( aType == DeviceType_HardDisk
3711 && mMediumAttachments.isBackedUp())
3712 {
3713 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3714
3715 /* check if the medium was attached to the VM before we started
3716 * changing attachments in which case the attachment just needs to
3717 * be restored */
3718 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3719 {
3720 AssertReturn(!fIndirect, E_FAIL);
3721
3722 /* see if it's the same bus/channel/device */
3723 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3724 {
3725 /* the simplest case: restore the whole attachment
3726 * and return, nothing else to do */
3727 mMediumAttachments->push_back(pAttachTemp);
3728
3729 /* Reattach the medium to the VM. */
3730 if (fHotplug || fSilent)
3731 {
3732 mediumLock.release();
3733 treeLock.release();
3734 alock.release();
3735
3736 MediumLockList *pMediumLockList(new MediumLockList());
3737
3738 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3739 medium /* pToLockWrite */,
3740 false /* fMediumLockWriteAll */,
3741 NULL,
3742 *pMediumLockList);
3743 alock.acquire();
3744 if (FAILED(rc))
3745 delete pMediumLockList;
3746 else
3747 {
3748 mData->mSession.mLockedMedia.Unlock();
3749 alock.release();
3750 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3751 mData->mSession.mLockedMedia.Lock();
3752 alock.acquire();
3753 }
3754 alock.release();
3755
3756 if (SUCCEEDED(rc))
3757 {
3758 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3759 /* Remove lock list in case of error. */
3760 if (FAILED(rc))
3761 {
3762 mData->mSession.mLockedMedia.Unlock();
3763 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3764 mData->mSession.mLockedMedia.Lock();
3765 }
3766 }
3767 }
3768
3769 return S_OK;
3770 }
3771
3772 /* bus/channel/device differ; we need a new attachment object,
3773 * but don't try to associate it again */
3774 associate = false;
3775 break;
3776 }
3777 }
3778
3779 /* go further only if the attachment is to be indirect */
3780 if (!fIndirect)
3781 break;
3782
3783 /* perform the so called smart attachment logic for indirect
3784 * attachments. Note that smart attachment is only applicable to base
3785 * hard disks. */
3786
3787 if (medium->i_getParent().isNull())
3788 {
3789 /* first, investigate the backup copy of the current hard disk
3790 * attachments to make it possible to re-attach existing diffs to
3791 * another device slot w/o losing their contents */
3792 if (mMediumAttachments.isBackedUp())
3793 {
3794 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3795
3796 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3797 uint32_t foundLevel = 0;
3798
3799 for (MediumAttachmentList::const_iterator
3800 it = oldAtts.begin();
3801 it != oldAtts.end();
3802 ++it)
3803 {
3804 uint32_t level = 0;
3805 MediumAttachment *pAttach = *it;
3806 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3807 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3808 if (pMedium.isNull())
3809 continue;
3810
3811 if (pMedium->i_getBase(&level) == medium)
3812 {
3813 /* skip the hard disk if its currently attached (we
3814 * cannot attach the same hard disk twice) */
3815 if (i_findAttachment(*mMediumAttachments.data(),
3816 pMedium))
3817 continue;
3818
3819 /* matched device, channel and bus (i.e. attached to the
3820 * same place) will win and immediately stop the search;
3821 * otherwise the attachment that has the youngest
3822 * descendant of medium will be used
3823 */
3824 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3825 {
3826 /* the simplest case: restore the whole attachment
3827 * and return, nothing else to do */
3828 mMediumAttachments->push_back(*it);
3829
3830 /* Reattach the medium to the VM. */
3831 if (fHotplug || fSilent)
3832 {
3833 mediumLock.release();
3834 treeLock.release();
3835 alock.release();
3836
3837 MediumLockList *pMediumLockList(new MediumLockList());
3838
3839 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3840 medium /* pToLockWrite */,
3841 false /* fMediumLockWriteAll */,
3842 NULL,
3843 *pMediumLockList);
3844 alock.acquire();
3845 if (FAILED(rc))
3846 delete pMediumLockList;
3847 else
3848 {
3849 mData->mSession.mLockedMedia.Unlock();
3850 alock.release();
3851 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3852 mData->mSession.mLockedMedia.Lock();
3853 alock.acquire();
3854 }
3855 alock.release();
3856
3857 if (SUCCEEDED(rc))
3858 {
3859 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3860 /* Remove lock list in case of error. */
3861 if (FAILED(rc))
3862 {
3863 mData->mSession.mLockedMedia.Unlock();
3864 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3865 mData->mSession.mLockedMedia.Lock();
3866 }
3867 }
3868 }
3869
3870 return S_OK;
3871 }
3872 else if ( foundIt == oldAtts.end()
3873 || level > foundLevel /* prefer younger */
3874 )
3875 {
3876 foundIt = it;
3877 foundLevel = level;
3878 }
3879 }
3880 }
3881
3882 if (foundIt != oldAtts.end())
3883 {
3884 /* use the previously attached hard disk */
3885 medium = (*foundIt)->i_getMedium();
3886 mediumCaller.attach(medium);
3887 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3888 mediumLock.attach(medium);
3889 /* not implicit, doesn't require association with this VM */
3890 fIndirect = false;
3891 associate = false;
3892 /* go right to the MediumAttachment creation */
3893 break;
3894 }
3895 }
3896
3897 /* must give up the medium lock and medium tree lock as below we
3898 * go over snapshots, which needs a lock with higher lock order. */
3899 mediumLock.release();
3900 treeLock.release();
3901
3902 /* then, search through snapshots for the best diff in the given
3903 * hard disk's chain to base the new diff on */
3904
3905 ComObjPtr<Medium> base;
3906 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3907 while (snap)
3908 {
3909 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3910
3911 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3912
3913 MediumAttachment *pAttachFound = NULL;
3914 uint32_t foundLevel = 0;
3915
3916 for (MediumAttachmentList::const_iterator
3917 it = snapAtts.begin();
3918 it != snapAtts.end();
3919 ++it)
3920 {
3921 MediumAttachment *pAttach = *it;
3922 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3923 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3924 if (pMedium.isNull())
3925 continue;
3926
3927 uint32_t level = 0;
3928 if (pMedium->i_getBase(&level) == medium)
3929 {
3930 /* matched device, channel and bus (i.e. attached to the
3931 * same place) will win and immediately stop the search;
3932 * otherwise the attachment that has the youngest
3933 * descendant of medium will be used
3934 */
3935 if ( pAttach->i_getDevice() == aDevice
3936 && pAttach->i_getPort() == aControllerPort
3937 && pAttach->i_getControllerName() == aName
3938 )
3939 {
3940 pAttachFound = pAttach;
3941 break;
3942 }
3943 else if ( !pAttachFound
3944 || level > foundLevel /* prefer younger */
3945 )
3946 {
3947 pAttachFound = pAttach;
3948 foundLevel = level;
3949 }
3950 }
3951 }
3952
3953 if (pAttachFound)
3954 {
3955 base = pAttachFound->i_getMedium();
3956 break;
3957 }
3958
3959 snap = snap->i_getParent();
3960 }
3961
3962 /* re-lock medium tree and the medium, as we need it below */
3963 treeLock.acquire();
3964 mediumLock.acquire();
3965
3966 /* found a suitable diff, use it as a base */
3967 if (!base.isNull())
3968 {
3969 medium = base;
3970 mediumCaller.attach(medium);
3971 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3972 mediumLock.attach(medium);
3973 }
3974 }
3975
3976 Utf8Str strFullSnapshotFolder;
3977 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3978
3979 ComObjPtr<Medium> diff;
3980 diff.createObject();
3981 // store this diff in the same registry as the parent
3982 Guid uuidRegistryParent;
3983 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3984 {
3985 // parent image has no registry: this can happen if we're attaching a new immutable
3986 // image that has not yet been attached (medium then points to the base and we're
3987 // creating the diff image for the immutable, and the parent is not yet registered);
3988 // put the parent in the machine registry then
3989 mediumLock.release();
3990 treeLock.release();
3991 alock.release();
3992 i_addMediumToRegistry(medium);
3993 alock.acquire();
3994 treeLock.acquire();
3995 mediumLock.acquire();
3996 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3997 }
3998 rc = diff->init(mParent,
3999 medium->i_getPreferredDiffFormat(),
4000 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4001 uuidRegistryParent,
4002 DeviceType_HardDisk);
4003 if (FAILED(rc)) return rc;
4004
4005 /* Apply the normal locking logic to the entire chain. */
4006 MediumLockList *pMediumLockList(new MediumLockList());
4007 mediumLock.release();
4008 treeLock.release();
4009 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4010 diff /* pToLockWrite */,
4011 false /* fMediumLockWriteAll */,
4012 medium,
4013 *pMediumLockList);
4014 treeLock.acquire();
4015 mediumLock.acquire();
4016 if (SUCCEEDED(rc))
4017 {
4018 mediumLock.release();
4019 treeLock.release();
4020 rc = pMediumLockList->Lock();
4021 treeLock.acquire();
4022 mediumLock.acquire();
4023 if (FAILED(rc))
4024 setError(rc,
4025 tr("Could not lock medium when creating diff '%s'"),
4026 diff->i_getLocationFull().c_str());
4027 else
4028 {
4029 /* will release the lock before the potentially lengthy
4030 * operation, so protect with the special state */
4031 MachineState_T oldState = mData->mMachineState;
4032 i_setMachineState(MachineState_SettingUp);
4033
4034 mediumLock.release();
4035 treeLock.release();
4036 alock.release();
4037
4038 rc = medium->i_createDiffStorage(diff,
4039 medium->i_getPreferredDiffVariant(),
4040 pMediumLockList,
4041 NULL /* aProgress */,
4042 true /* aWait */,
4043 false /* aNotify */);
4044
4045 alock.acquire();
4046 treeLock.acquire();
4047 mediumLock.acquire();
4048
4049 i_setMachineState(oldState);
4050 }
4051 }
4052
4053 /* Unlock the media and free the associated memory. */
4054 delete pMediumLockList;
4055
4056 if (FAILED(rc)) return rc;
4057
4058 /* use the created diff for the actual attachment */
4059 medium = diff;
4060 mediumCaller.attach(medium);
4061 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4062 mediumLock.attach(medium);
4063 }
4064 while (0);
4065
4066 ComObjPtr<MediumAttachment> attachment;
4067 attachment.createObject();
4068 rc = attachment->init(this,
4069 medium,
4070 aName,
4071 aControllerPort,
4072 aDevice,
4073 aType,
4074 fIndirect,
4075 false /* fPassthrough */,
4076 false /* fTempEject */,
4077 false /* fNonRotational */,
4078 false /* fDiscard */,
4079 fHotplug /* fHotPluggable */,
4080 Utf8Str::Empty);
4081 if (FAILED(rc)) return rc;
4082
4083 if (associate && !medium.isNull())
4084 {
4085 // as the last step, associate the medium to the VM
4086 rc = medium->i_addBackReference(mData->mUuid);
4087 // here we can fail because of Deleting, or being in process of creating a Diff
4088 if (FAILED(rc)) return rc;
4089
4090 mediumLock.release();
4091 treeLock.release();
4092 alock.release();
4093 i_addMediumToRegistry(medium);
4094 alock.acquire();
4095 treeLock.acquire();
4096 mediumLock.acquire();
4097 }
4098
4099 /* success: finally remember the attachment */
4100 i_setModified(IsModified_Storage);
4101 mMediumAttachments.backup();
4102 mMediumAttachments->push_back(attachment);
4103
4104 mediumLock.release();
4105 treeLock.release();
4106 alock.release();
4107
4108 if (fHotplug || fSilent)
4109 {
4110 if (!medium.isNull())
4111 {
4112 MediumLockList *pMediumLockList(new MediumLockList());
4113
4114 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4115 medium /* pToLockWrite */,
4116 false /* fMediumLockWriteAll */,
4117 NULL,
4118 *pMediumLockList);
4119 alock.acquire();
4120 if (FAILED(rc))
4121 delete pMediumLockList;
4122 else
4123 {
4124 mData->mSession.mLockedMedia.Unlock();
4125 alock.release();
4126 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4127 mData->mSession.mLockedMedia.Lock();
4128 alock.acquire();
4129 }
4130 alock.release();
4131 }
4132
4133 if (SUCCEEDED(rc))
4134 {
4135 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4136 /* Remove lock list in case of error. */
4137 if (FAILED(rc))
4138 {
4139 mData->mSession.mLockedMedia.Unlock();
4140 mData->mSession.mLockedMedia.Remove(attachment);
4141 mData->mSession.mLockedMedia.Lock();
4142 }
4143 }
4144 }
4145
4146 /* Save modified registries, but skip this machine as it's the caller's
4147 * job to save its settings like all other settings changes. */
4148 mParent->i_unmarkRegistryModified(i_getId());
4149 mParent->i_saveModifiedRegistries();
4150
4151 if (aM)
4152 mParent->i_onMediumConfigChanged(aM);
4153 return rc;
4154}
4155
4156HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4157 LONG aDevice)
4158{
4159 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4160 aName.c_str(), aControllerPort, aDevice));
4161
4162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4163
4164 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4165 if (FAILED(rc)) return rc;
4166
4167 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4168
4169 /* Check for an existing controller. */
4170 ComObjPtr<StorageController> ctl;
4171 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4172 if (FAILED(rc)) return rc;
4173
4174 StorageControllerType_T ctrlType;
4175 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4176 if (FAILED(rc))
4177 return setError(E_FAIL,
4178 tr("Could not get type of controller '%s'"),
4179 aName.c_str());
4180
4181 bool fSilent = false;
4182 Utf8Str strReconfig;
4183
4184 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4185 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4186 if ( mData->mMachineState == MachineState_Paused
4187 && strReconfig == "1")
4188 fSilent = true;
4189
4190 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4191 bool fHotplug = false;
4192 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4193 fHotplug = true;
4194
4195 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4196 return setError(VBOX_E_INVALID_VM_STATE,
4197 tr("Controller '%s' does not support hotplugging"),
4198 aName.c_str());
4199
4200 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4201 aName,
4202 aControllerPort,
4203 aDevice);
4204 if (!pAttach)
4205 return setError(VBOX_E_OBJECT_NOT_FOUND,
4206 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4207 aDevice, aControllerPort, aName.c_str());
4208
4209 if (fHotplug && !pAttach->i_getHotPluggable())
4210 return setError(VBOX_E_NOT_SUPPORTED,
4211 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4212 aDevice, aControllerPort, aName.c_str());
4213
4214 /*
4215 * The VM has to detach the device before we delete any implicit diffs.
4216 * If this fails we can roll back without loosing data.
4217 */
4218 if (fHotplug || fSilent)
4219 {
4220 alock.release();
4221 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4222 alock.acquire();
4223 }
4224 if (FAILED(rc)) return rc;
4225
4226 /* If we are here everything went well and we can delete the implicit now. */
4227 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4228
4229 alock.release();
4230
4231 /* Save modified registries, but skip this machine as it's the caller's
4232 * job to save its settings like all other settings changes. */
4233 mParent->i_unmarkRegistryModified(i_getId());
4234 mParent->i_saveModifiedRegistries();
4235
4236 return rc;
4237}
4238
4239HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4240 LONG aDevice, BOOL aPassthrough)
4241{
4242 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4243 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4244
4245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4246
4247 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4248 if (FAILED(rc)) return rc;
4249
4250 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4251
4252 /* Check for an existing controller. */
4253 ComObjPtr<StorageController> ctl;
4254 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4255 if (FAILED(rc)) return rc;
4256
4257 StorageControllerType_T ctrlType;
4258 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4259 if (FAILED(rc))
4260 return setError(E_FAIL,
4261 tr("Could not get type of controller '%s'"),
4262 aName.c_str());
4263
4264 bool fSilent = false;
4265 Utf8Str strReconfig;
4266
4267 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4268 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4269 if ( mData->mMachineState == MachineState_Paused
4270 && strReconfig == "1")
4271 fSilent = true;
4272
4273 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4274 bool fHotplug = false;
4275 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4276 fHotplug = true;
4277
4278 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4279 return setError(VBOX_E_INVALID_VM_STATE,
4280 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4281 aName.c_str());
4282
4283 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4284 aName,
4285 aControllerPort,
4286 aDevice);
4287 if (!pAttach)
4288 return setError(VBOX_E_OBJECT_NOT_FOUND,
4289 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4290 aDevice, aControllerPort, aName.c_str());
4291
4292
4293 i_setModified(IsModified_Storage);
4294 mMediumAttachments.backup();
4295
4296 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4297
4298 if (pAttach->i_getType() != DeviceType_DVD)
4299 return setError(E_INVALIDARG,
4300 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4301 aDevice, aControllerPort, aName.c_str());
4302 pAttach->i_updatePassthrough(!!aPassthrough);
4303
4304 attLock.release();
4305 alock.release();
4306 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4307
4308 return rc;
4309}
4310
4311HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4312 LONG aDevice, BOOL aTemporaryEject)
4313{
4314
4315 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4316 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4317
4318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4319
4320 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4321 if (FAILED(rc)) return rc;
4322
4323 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4324 aName,
4325 aControllerPort,
4326 aDevice);
4327 if (!pAttach)
4328 return setError(VBOX_E_OBJECT_NOT_FOUND,
4329 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4330 aDevice, aControllerPort, aName.c_str());
4331
4332
4333 i_setModified(IsModified_Storage);
4334 mMediumAttachments.backup();
4335
4336 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4337
4338 if (pAttach->i_getType() != DeviceType_DVD)
4339 return setError(E_INVALIDARG,
4340 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4341 aDevice, aControllerPort, aName.c_str());
4342 pAttach->i_updateTempEject(!!aTemporaryEject);
4343
4344 return S_OK;
4345}
4346
4347HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4348 LONG aDevice, BOOL aNonRotational)
4349{
4350
4351 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4352 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4353
4354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4355
4356 HRESULT rc = i_checkStateDependency(MutableStateDep);
4357 if (FAILED(rc)) return rc;
4358
4359 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4360
4361 if (Global::IsOnlineOrTransient(mData->mMachineState))
4362 return setError(VBOX_E_INVALID_VM_STATE,
4363 tr("Invalid machine state: %s"),
4364 Global::stringifyMachineState(mData->mMachineState));
4365
4366 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4367 aName,
4368 aControllerPort,
4369 aDevice);
4370 if (!pAttach)
4371 return setError(VBOX_E_OBJECT_NOT_FOUND,
4372 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4373 aDevice, aControllerPort, aName.c_str());
4374
4375
4376 i_setModified(IsModified_Storage);
4377 mMediumAttachments.backup();
4378
4379 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4380
4381 if (pAttach->i_getType() != DeviceType_HardDisk)
4382 return setError(E_INVALIDARG,
4383 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"),
4384 aDevice, aControllerPort, aName.c_str());
4385 pAttach->i_updateNonRotational(!!aNonRotational);
4386
4387 return S_OK;
4388}
4389
4390HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4391 LONG aDevice, BOOL aDiscard)
4392{
4393
4394 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4395 aName.c_str(), aControllerPort, aDevice, aDiscard));
4396
4397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4398
4399 HRESULT rc = i_checkStateDependency(MutableStateDep);
4400 if (FAILED(rc)) return rc;
4401
4402 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4403
4404 if (Global::IsOnlineOrTransient(mData->mMachineState))
4405 return setError(VBOX_E_INVALID_VM_STATE,
4406 tr("Invalid machine state: %s"),
4407 Global::stringifyMachineState(mData->mMachineState));
4408
4409 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4410 aName,
4411 aControllerPort,
4412 aDevice);
4413 if (!pAttach)
4414 return setError(VBOX_E_OBJECT_NOT_FOUND,
4415 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4416 aDevice, aControllerPort, aName.c_str());
4417
4418
4419 i_setModified(IsModified_Storage);
4420 mMediumAttachments.backup();
4421
4422 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4423
4424 if (pAttach->i_getType() != DeviceType_HardDisk)
4425 return setError(E_INVALIDARG,
4426 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"),
4427 aDevice, aControllerPort, aName.c_str());
4428 pAttach->i_updateDiscard(!!aDiscard);
4429
4430 return S_OK;
4431}
4432
4433HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4434 LONG aDevice, BOOL aHotPluggable)
4435{
4436 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4437 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4438
4439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4440
4441 HRESULT rc = i_checkStateDependency(MutableStateDep);
4442 if (FAILED(rc)) return rc;
4443
4444 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4445
4446 if (Global::IsOnlineOrTransient(mData->mMachineState))
4447 return setError(VBOX_E_INVALID_VM_STATE,
4448 tr("Invalid machine state: %s"),
4449 Global::stringifyMachineState(mData->mMachineState));
4450
4451 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4452 aName,
4453 aControllerPort,
4454 aDevice);
4455 if (!pAttach)
4456 return setError(VBOX_E_OBJECT_NOT_FOUND,
4457 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4458 aDevice, aControllerPort, aName.c_str());
4459
4460 /* Check for an existing controller. */
4461 ComObjPtr<StorageController> ctl;
4462 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4463 if (FAILED(rc)) return rc;
4464
4465 StorageControllerType_T ctrlType;
4466 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4467 if (FAILED(rc))
4468 return setError(E_FAIL,
4469 tr("Could not get type of controller '%s'"),
4470 aName.c_str());
4471
4472 if (!i_isControllerHotplugCapable(ctrlType))
4473 return setError(VBOX_E_NOT_SUPPORTED,
4474 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4475 aName.c_str());
4476
4477 i_setModified(IsModified_Storage);
4478 mMediumAttachments.backup();
4479
4480 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4481
4482 if (pAttach->i_getType() == DeviceType_Floppy)
4483 return setError(E_INVALIDARG,
4484 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"),
4485 aDevice, aControllerPort, aName.c_str());
4486 pAttach->i_updateHotPluggable(!!aHotPluggable);
4487
4488 return S_OK;
4489}
4490
4491HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4492 LONG aDevice)
4493{
4494 int rc = S_OK;
4495 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4496 aName.c_str(), aControllerPort, aDevice));
4497
4498 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4499
4500 return rc;
4501}
4502
4503HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4504 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4505{
4506 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4507 aName.c_str(), aControllerPort, aDevice));
4508
4509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4510
4511 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4512 if (FAILED(rc)) return rc;
4513
4514 if (Global::IsOnlineOrTransient(mData->mMachineState))
4515 return setError(VBOX_E_INVALID_VM_STATE,
4516 tr("Invalid machine state: %s"),
4517 Global::stringifyMachineState(mData->mMachineState));
4518
4519 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4520 aName,
4521 aControllerPort,
4522 aDevice);
4523 if (!pAttach)
4524 return setError(VBOX_E_OBJECT_NOT_FOUND,
4525 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4526 aDevice, aControllerPort, aName.c_str());
4527
4528
4529 i_setModified(IsModified_Storage);
4530 mMediumAttachments.backup();
4531
4532 IBandwidthGroup *iB = aBandwidthGroup;
4533 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4534 if (aBandwidthGroup && group.isNull())
4535 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4536
4537 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4538
4539 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4540 if (strBandwidthGroupOld.isNotEmpty())
4541 {
4542 /* Get the bandwidth group object and release it - this must not fail. */
4543 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4544 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4545 Assert(SUCCEEDED(rc));
4546
4547 pBandwidthGroupOld->i_release();
4548 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4549 }
4550
4551 if (!group.isNull())
4552 {
4553 group->i_reference();
4554 pAttach->i_updateBandwidthGroup(group->i_getName());
4555 }
4556
4557 return S_OK;
4558}
4559
4560HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4561 LONG aControllerPort,
4562 LONG aDevice,
4563 DeviceType_T aType)
4564{
4565 HRESULT rc = S_OK;
4566
4567 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4568 aName.c_str(), aControllerPort, aDevice, aType));
4569
4570 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4571
4572 return rc;
4573}
4574
4575
4576HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4577 LONG aControllerPort,
4578 LONG aDevice,
4579 BOOL aForce)
4580{
4581 int rc = S_OK;
4582 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4583 aName.c_str(), aControllerPort, aForce));
4584
4585 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4586
4587 return rc;
4588}
4589
4590HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4591 LONG aControllerPort,
4592 LONG aDevice,
4593 const ComPtr<IMedium> &aMedium,
4594 BOOL aForce)
4595{
4596 int rc = S_OK;
4597 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4598 aName.c_str(), aControllerPort, aDevice, aForce));
4599
4600 // request the host lock first, since might be calling Host methods for getting host drives;
4601 // next, protect the media tree all the while we're in here, as well as our member variables
4602 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4603 this->lockHandle(),
4604 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4605
4606 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4607 aName,
4608 aControllerPort,
4609 aDevice);
4610 if (pAttach.isNull())
4611 return setError(VBOX_E_OBJECT_NOT_FOUND,
4612 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4613 aDevice, aControllerPort, aName.c_str());
4614
4615 /* Remember previously mounted medium. The medium before taking the
4616 * backup is not necessarily the same thing. */
4617 ComObjPtr<Medium> oldmedium;
4618 oldmedium = pAttach->i_getMedium();
4619
4620 IMedium *iM = aMedium;
4621 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4622 if (aMedium && pMedium.isNull())
4623 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4624
4625 AutoCaller mediumCaller(pMedium);
4626 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4627
4628 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4629 if (pMedium)
4630 {
4631 DeviceType_T mediumType = pAttach->i_getType();
4632 switch (mediumType)
4633 {
4634 case DeviceType_DVD:
4635 case DeviceType_Floppy:
4636 break;
4637
4638 default:
4639 return setError(VBOX_E_INVALID_OBJECT_STATE,
4640 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4641 aControllerPort,
4642 aDevice,
4643 aName.c_str());
4644 }
4645 }
4646
4647 i_setModified(IsModified_Storage);
4648 mMediumAttachments.backup();
4649
4650 {
4651 // The backup operation makes the pAttach reference point to the
4652 // old settings. Re-get the correct reference.
4653 pAttach = i_findAttachment(*mMediumAttachments.data(),
4654 aName,
4655 aControllerPort,
4656 aDevice);
4657 if (!oldmedium.isNull())
4658 oldmedium->i_removeBackReference(mData->mUuid);
4659 if (!pMedium.isNull())
4660 {
4661 pMedium->i_addBackReference(mData->mUuid);
4662
4663 mediumLock.release();
4664 multiLock.release();
4665 i_addMediumToRegistry(pMedium);
4666 multiLock.acquire();
4667 mediumLock.acquire();
4668 }
4669
4670 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4671 pAttach->i_updateMedium(pMedium);
4672 }
4673
4674 i_setModified(IsModified_Storage);
4675
4676 mediumLock.release();
4677 multiLock.release();
4678 rc = i_onMediumChange(pAttach, aForce);
4679 multiLock.acquire();
4680 mediumLock.acquire();
4681
4682 /* On error roll back this change only. */
4683 if (FAILED(rc))
4684 {
4685 if (!pMedium.isNull())
4686 pMedium->i_removeBackReference(mData->mUuid);
4687 pAttach = i_findAttachment(*mMediumAttachments.data(),
4688 aName,
4689 aControllerPort,
4690 aDevice);
4691 /* If the attachment is gone in the meantime, bail out. */
4692 if (pAttach.isNull())
4693 return rc;
4694 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4695 if (!oldmedium.isNull())
4696 oldmedium->i_addBackReference(mData->mUuid);
4697 pAttach->i_updateMedium(oldmedium);
4698 }
4699
4700 mediumLock.release();
4701 multiLock.release();
4702
4703 /* Save modified registries, but skip this machine as it's the caller's
4704 * job to save its settings like all other settings changes. */
4705 mParent->i_unmarkRegistryModified(i_getId());
4706 mParent->i_saveModifiedRegistries();
4707
4708 return rc;
4709}
4710HRESULT Machine::getMedium(const com::Utf8Str &aName,
4711 LONG aControllerPort,
4712 LONG aDevice,
4713 ComPtr<IMedium> &aMedium)
4714{
4715 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4716 aName.c_str(), aControllerPort, aDevice));
4717
4718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4719
4720 aMedium = NULL;
4721
4722 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4723 aName,
4724 aControllerPort,
4725 aDevice);
4726 if (pAttach.isNull())
4727 return setError(VBOX_E_OBJECT_NOT_FOUND,
4728 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4729 aDevice, aControllerPort, aName.c_str());
4730
4731 aMedium = pAttach->i_getMedium();
4732
4733 return S_OK;
4734}
4735
4736HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4737{
4738
4739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4740
4741 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4742
4743 return S_OK;
4744}
4745
4746HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4747{
4748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4749
4750 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4751
4752 return S_OK;
4753}
4754
4755HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4756{
4757 /* Do not assert if slot is out of range, just return the advertised
4758 status. testdriver/vbox.py triggers this in logVmInfo. */
4759 if (aSlot >= mNetworkAdapters.size())
4760 return setError(E_INVALIDARG,
4761 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4762 aSlot, mNetworkAdapters.size());
4763
4764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4765
4766 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4767
4768 return S_OK;
4769}
4770
4771HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4772{
4773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4774
4775 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4776 size_t i = 0;
4777 for (settings::StringsMap::const_iterator
4778 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4779 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4780 ++it, ++i)
4781 aKeys[i] = it->first;
4782
4783 return S_OK;
4784}
4785
4786 /**
4787 * @note Locks this object for reading.
4788 */
4789HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4790 com::Utf8Str &aValue)
4791{
4792 /* start with nothing found */
4793 aValue = "";
4794
4795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4796
4797 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4798 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4799 // found:
4800 aValue = it->second; // source is a Utf8Str
4801
4802 /* return the result to caller (may be empty) */
4803 return S_OK;
4804}
4805
4806 /**
4807 * @note Locks mParent for writing + this object for writing.
4808 */
4809HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4810{
4811 /* Because non-ASCII characters in aKey have caused problems in the settings
4812 * they are rejected unless the key should be deleted. */
4813 if (!aValue.isEmpty())
4814 {
4815 for (size_t i = 0; i < aKey.length(); ++i)
4816 {
4817 char ch = aKey[i];
4818 if (!RTLocCIsPrint(ch))
4819 return E_INVALIDARG;
4820 }
4821 }
4822
4823 Utf8Str strOldValue; // empty
4824
4825 // locking note: we only hold the read lock briefly to look up the old value,
4826 // then release it and call the onExtraCanChange callbacks. There is a small
4827 // chance of a race insofar as the callback might be called twice if two callers
4828 // change the same key at the same time, but that's a much better solution
4829 // than the deadlock we had here before. The actual changing of the extradata
4830 // is then performed under the write lock and race-free.
4831
4832 // look up the old value first; if nothing has changed then we need not do anything
4833 {
4834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4835
4836 // For snapshots don't even think about allowing changes, extradata
4837 // is global for a machine, so there is nothing snapshot specific.
4838 if (i_isSnapshotMachine())
4839 return setError(VBOX_E_INVALID_VM_STATE,
4840 tr("Cannot set extradata for a snapshot"));
4841
4842 // check if the right IMachine instance is used
4843 if (mData->mRegistered && !i_isSessionMachine())
4844 return setError(VBOX_E_INVALID_VM_STATE,
4845 tr("Cannot set extradata for an immutable machine"));
4846
4847 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4848 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4849 strOldValue = it->second;
4850 }
4851
4852 bool fChanged;
4853 if ((fChanged = (strOldValue != aValue)))
4854 {
4855 // ask for permission from all listeners outside the locks;
4856 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4857 // lock to copy the list of callbacks to invoke
4858 Bstr error;
4859 Bstr bstrValue(aValue);
4860
4861 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4862 {
4863 const char *sep = error.isEmpty() ? "" : ": ";
4864 CBSTR err = error.raw();
4865 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4866 return setError(E_ACCESSDENIED,
4867 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4868 aKey.c_str(),
4869 aValue.c_str(),
4870 sep,
4871 err);
4872 }
4873
4874 // data is changing and change not vetoed: then write it out under the lock
4875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4876
4877 if (aValue.isEmpty())
4878 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4879 else
4880 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4881 // creates a new key if needed
4882
4883 bool fNeedsGlobalSaveSettings = false;
4884 // This saving of settings is tricky: there is no "old state" for the
4885 // extradata items at all (unlike all other settings), so the old/new
4886 // settings comparison would give a wrong result!
4887 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4888
4889 if (fNeedsGlobalSaveSettings)
4890 {
4891 // save the global settings; for that we should hold only the VirtualBox lock
4892 alock.release();
4893 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4894 mParent->i_saveSettings();
4895 }
4896 }
4897
4898 // fire notification outside the lock
4899 if (fChanged)
4900 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4901
4902 return S_OK;
4903}
4904
4905HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4906{
4907 aProgress = NULL;
4908 NOREF(aSettingsFilePath);
4909 ReturnComNotImplemented();
4910}
4911
4912HRESULT Machine::saveSettings()
4913{
4914 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4915
4916 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4917 if (FAILED(rc)) return rc;
4918
4919 /* the settings file path may never be null */
4920 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4921
4922 /* save all VM data excluding snapshots */
4923 bool fNeedsGlobalSaveSettings = false;
4924 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4925 mlock.release();
4926
4927 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4928 {
4929 // save the global settings; for that we should hold only the VirtualBox lock
4930 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4931 rc = mParent->i_saveSettings();
4932 }
4933
4934 return rc;
4935}
4936
4937
4938HRESULT Machine::discardSettings()
4939{
4940 /*
4941 * We need to take the machine list lock here as well as the machine one
4942 * or we'll get into trouble should any media stuff require rolling back.
4943 *
4944 * Details:
4945 *
4946 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4947 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4948 * 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]
4949 * 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
4950 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4951 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4952 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4953 * 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
4954 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4955 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4956 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4957 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4958 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4959 * 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]
4960 * 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] (*)
4961 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4962 * 0:005> k
4963 * # Child-SP RetAddr Call Site
4964 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4965 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4966 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4967 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4968 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4969 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4970 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4971 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4972 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4973 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4974 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4975 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4976 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4977 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4978 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4979 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4980 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4981 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4982 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4983 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4984 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4985 *
4986 */
4987 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4989
4990 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4991 if (FAILED(rc)) return rc;
4992
4993 /*
4994 * during this rollback, the session will be notified if data has
4995 * been actually changed
4996 */
4997 i_rollback(true /* aNotify */);
4998
4999 return S_OK;
5000}
5001
5002/** @note Locks objects! */
5003HRESULT Machine::unregister(AutoCaller &autoCaller,
5004 CleanupMode_T aCleanupMode,
5005 std::vector<ComPtr<IMedium> > &aMedia)
5006{
5007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5008
5009 Guid id(i_getId());
5010
5011 if (mData->mSession.mState != SessionState_Unlocked)
5012 return setError(VBOX_E_INVALID_OBJECT_STATE,
5013 tr("Cannot unregister the machine '%s' while it is locked"),
5014 mUserData->s.strName.c_str());
5015
5016 // wait for state dependents to drop to zero
5017 i_ensureNoStateDependencies();
5018
5019 if (!mData->mAccessible)
5020 {
5021 // inaccessible maschines can only be unregistered; uninitialize ourselves
5022 // here because currently there may be no unregistered that are inaccessible
5023 // (this state combination is not supported). Note releasing the caller and
5024 // leaving the lock before calling uninit()
5025 alock.release();
5026 autoCaller.release();
5027
5028 uninit();
5029
5030 mParent->i_unregisterMachine(this, id);
5031 // calls VirtualBox::i_saveSettings()
5032
5033 return S_OK;
5034 }
5035
5036 HRESULT rc = S_OK;
5037
5038 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5039 // discard saved state
5040 if (mData->mMachineState == MachineState_Saved)
5041 {
5042 // add the saved state file to the list of files the caller should delete
5043 Assert(!mSSData->strStateFilePath.isEmpty());
5044 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5045
5046 mSSData->strStateFilePath.setNull();
5047
5048 // unconditionally set the machine state to powered off, we now
5049 // know no session has locked the machine
5050 mData->mMachineState = MachineState_PoweredOff;
5051 }
5052
5053 size_t cSnapshots = 0;
5054 if (mData->mFirstSnapshot)
5055 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5056 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5057 // fail now before we start detaching media
5058 return setError(VBOX_E_INVALID_OBJECT_STATE,
5059 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5060 mUserData->s.strName.c_str(), cSnapshots);
5061
5062 // This list collects the medium objects from all medium attachments
5063 // which we will detach from the machine and its snapshots, in a specific
5064 // order which allows for closing all media without getting "media in use"
5065 // errors, simply by going through the list from the front to the back:
5066 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5067 // and must be closed before the parent media from the snapshots, or closing the parents
5068 // will fail because they still have children);
5069 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5070 // the root ("first") snapshot of the machine.
5071 MediaList llMedia;
5072
5073 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5074 && mMediumAttachments->size()
5075 )
5076 {
5077 // we have media attachments: detach them all and add the Medium objects to our list
5078 if (aCleanupMode != CleanupMode_UnregisterOnly)
5079 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5080 else
5081 return setError(VBOX_E_INVALID_OBJECT_STATE,
5082 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5083 mUserData->s.strName.c_str(), mMediumAttachments->size());
5084 }
5085
5086 if (cSnapshots)
5087 {
5088 // add the media from the medium attachments of the snapshots to llMedia
5089 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5090 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5091 // into the children first
5092
5093 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5094 MachineState_T oldState = mData->mMachineState;
5095 mData->mMachineState = MachineState_DeletingSnapshot;
5096
5097 // make a copy of the first snapshot so the refcount does not drop to 0
5098 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5099 // because of the AutoCaller voodoo)
5100 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5101
5102 // GO!
5103 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5104
5105 mData->mMachineState = oldState;
5106 }
5107
5108 if (FAILED(rc))
5109 {
5110 i_rollbackMedia();
5111 return rc;
5112 }
5113
5114 // commit all the media changes made above
5115 i_commitMedia();
5116
5117 mData->mRegistered = false;
5118
5119 // machine lock no longer needed
5120 alock.release();
5121
5122 // return media to caller
5123 aMedia.resize(llMedia.size());
5124 size_t i = 0;
5125 for (MediaList::const_iterator
5126 it = llMedia.begin();
5127 it != llMedia.end();
5128 ++it, ++i)
5129 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5130
5131 mParent->i_unregisterMachine(this, id);
5132 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5133
5134 return S_OK;
5135}
5136
5137/**
5138 * Task record for deleting a machine config.
5139 */
5140class Machine::DeleteConfigTask
5141 : public Machine::Task
5142{
5143public:
5144 DeleteConfigTask(Machine *m,
5145 Progress *p,
5146 const Utf8Str &t,
5147 const RTCList<ComPtr<IMedium> > &llMediums,
5148 const StringsList &llFilesToDelete)
5149 : Task(m, p, t),
5150 m_llMediums(llMediums),
5151 m_llFilesToDelete(llFilesToDelete)
5152 {}
5153
5154private:
5155 void handler()
5156 {
5157 try
5158 {
5159 m_pMachine->i_deleteConfigHandler(*this);
5160 }
5161 catch (...)
5162 {
5163 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5164 }
5165 }
5166
5167 RTCList<ComPtr<IMedium> > m_llMediums;
5168 StringsList m_llFilesToDelete;
5169
5170 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5171};
5172
5173/**
5174 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5175 * SessionMachine::taskHandler().
5176 *
5177 * @note Locks this object for writing.
5178 *
5179 * @param task
5180 * @return
5181 */
5182void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5183{
5184 LogFlowThisFuncEnter();
5185
5186 AutoCaller autoCaller(this);
5187 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5188 if (FAILED(autoCaller.rc()))
5189 {
5190 /* we might have been uninitialized because the session was accidentally
5191 * closed by the client, so don't assert */
5192 HRESULT rc = setError(E_FAIL,
5193 tr("The session has been accidentally closed"));
5194 task.m_pProgress->i_notifyComplete(rc);
5195 LogFlowThisFuncLeave();
5196 return;
5197 }
5198
5199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5200
5201 HRESULT rc = S_OK;
5202
5203 try
5204 {
5205 ULONG uLogHistoryCount = 3;
5206 ComPtr<ISystemProperties> systemProperties;
5207 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5208 if (FAILED(rc)) throw rc;
5209
5210 if (!systemProperties.isNull())
5211 {
5212 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5213 if (FAILED(rc)) throw rc;
5214 }
5215
5216 MachineState_T oldState = mData->mMachineState;
5217 i_setMachineState(MachineState_SettingUp);
5218 alock.release();
5219 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5220 {
5221 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5222 {
5223 AutoCaller mac(pMedium);
5224 if (FAILED(mac.rc())) throw mac.rc();
5225 Utf8Str strLocation = pMedium->i_getLocationFull();
5226 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5227 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5228 if (FAILED(rc)) throw rc;
5229 }
5230 if (pMedium->i_isMediumFormatFile())
5231 {
5232 ComPtr<IProgress> pProgress2;
5233 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5234 if (FAILED(rc)) throw rc;
5235 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5236 if (FAILED(rc)) throw rc;
5237 }
5238
5239 /* Close the medium, deliberately without checking the return
5240 * code, and without leaving any trace in the error info, as
5241 * a failure here is a very minor issue, which shouldn't happen
5242 * as above we even managed to delete the medium. */
5243 {
5244 ErrorInfoKeeper eik;
5245 pMedium->Close();
5246 }
5247 }
5248 i_setMachineState(oldState);
5249 alock.acquire();
5250
5251 // delete the files pushed on the task list by Machine::Delete()
5252 // (this includes saved states of the machine and snapshots and
5253 // medium storage files from the IMedium list passed in, and the
5254 // machine XML file)
5255 for (StringsList::const_iterator
5256 it = task.m_llFilesToDelete.begin();
5257 it != task.m_llFilesToDelete.end();
5258 ++it)
5259 {
5260 const Utf8Str &strFile = *it;
5261 LogFunc(("Deleting file %s\n", strFile.c_str()));
5262 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5263 if (FAILED(rc)) throw rc;
5264
5265 int vrc = RTFileDelete(strFile.c_str());
5266 if (RT_FAILURE(vrc))
5267 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5268 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5269 }
5270
5271 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5272 if (FAILED(rc)) throw rc;
5273
5274 /* delete the settings only when the file actually exists */
5275 if (mData->pMachineConfigFile->fileExists())
5276 {
5277 /* Delete any backup or uncommitted XML files. Ignore failures.
5278 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5279 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5280 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5281 RTFileDelete(otherXml.c_str());
5282 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5283 RTFileDelete(otherXml.c_str());
5284
5285 /* delete the Logs folder, nothing important should be left
5286 * there (we don't check for errors because the user might have
5287 * some private files there that we don't want to delete) */
5288 Utf8Str logFolder;
5289 getLogFolder(logFolder);
5290 Assert(logFolder.length());
5291 if (RTDirExists(logFolder.c_str()))
5292 {
5293 /* Delete all VBox.log[.N] files from the Logs folder
5294 * (this must be in sync with the rotation logic in
5295 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5296 * files that may have been created by the GUI. */
5297 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5298 logFolder.c_str(), RTPATH_DELIMITER);
5299 RTFileDelete(log.c_str());
5300 log = Utf8StrFmt("%s%cVBox.png",
5301 logFolder.c_str(), RTPATH_DELIMITER);
5302 RTFileDelete(log.c_str());
5303 for (int i = uLogHistoryCount; i > 0; i--)
5304 {
5305 log = Utf8StrFmt("%s%cVBox.log.%d",
5306 logFolder.c_str(), RTPATH_DELIMITER, i);
5307 RTFileDelete(log.c_str());
5308 log = Utf8StrFmt("%s%cVBox.png.%d",
5309 logFolder.c_str(), RTPATH_DELIMITER, i);
5310 RTFileDelete(log.c_str());
5311 }
5312#if defined(RT_OS_WINDOWS)
5313 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5314 RTFileDelete(log.c_str());
5315 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5316 RTFileDelete(log.c_str());
5317#endif
5318
5319 RTDirRemove(logFolder.c_str());
5320 }
5321
5322 /* delete the Snapshots folder, nothing important should be left
5323 * there (we don't check for errors because the user might have
5324 * some private files there that we don't want to delete) */
5325 Utf8Str strFullSnapshotFolder;
5326 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5327 Assert(!strFullSnapshotFolder.isEmpty());
5328 if (RTDirExists(strFullSnapshotFolder.c_str()))
5329 RTDirRemove(strFullSnapshotFolder.c_str());
5330
5331 // delete the directory that contains the settings file, but only
5332 // if it matches the VM name
5333 Utf8Str settingsDir;
5334 if (i_isInOwnDir(&settingsDir))
5335 RTDirRemove(settingsDir.c_str());
5336 }
5337
5338 alock.release();
5339
5340 mParent->i_saveModifiedRegistries();
5341 }
5342 catch (HRESULT aRC) { rc = aRC; }
5343
5344 task.m_pProgress->i_notifyComplete(rc);
5345
5346 LogFlowThisFuncLeave();
5347}
5348
5349HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5350{
5351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5352
5353 HRESULT rc = i_checkStateDependency(MutableStateDep);
5354 if (FAILED(rc)) return rc;
5355
5356 if (mData->mRegistered)
5357 return setError(VBOX_E_INVALID_VM_STATE,
5358 tr("Cannot delete settings of a registered machine"));
5359
5360 // collect files to delete
5361 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5362 if (mData->pMachineConfigFile->fileExists())
5363 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5364
5365 RTCList<ComPtr<IMedium> > llMediums;
5366 for (size_t i = 0; i < aMedia.size(); ++i)
5367 {
5368 IMedium *pIMedium(aMedia[i]);
5369 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5370 if (pMedium.isNull())
5371 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5372 SafeArray<BSTR> ids;
5373 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5374 if (FAILED(rc)) return rc;
5375 /* At this point the medium should not have any back references
5376 * anymore. If it has it is attached to another VM and *must* not
5377 * deleted. */
5378 if (ids.size() < 1)
5379 llMediums.append(pMedium);
5380 }
5381
5382 ComObjPtr<Progress> pProgress;
5383 pProgress.createObject();
5384 rc = pProgress->init(i_getVirtualBox(),
5385 static_cast<IMachine*>(this) /* aInitiator */,
5386 tr("Deleting files"),
5387 true /* fCancellable */,
5388 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5389 tr("Collecting file inventory"));
5390 if (FAILED(rc))
5391 return rc;
5392
5393 /* create and start the task on a separate thread (note that it will not
5394 * start working until we release alock) */
5395 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5396 rc = pTask->createThread();
5397 if (FAILED(rc))
5398 return rc;
5399
5400 pProgress.queryInterfaceTo(aProgress.asOutParam());
5401
5402 LogFlowFuncLeave();
5403
5404 return S_OK;
5405}
5406
5407HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5408{
5409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5410
5411 ComObjPtr<Snapshot> pSnapshot;
5412 HRESULT rc;
5413
5414 if (aNameOrId.isEmpty())
5415 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5416 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5417 else
5418 {
5419 Guid uuid(aNameOrId);
5420 if (uuid.isValid())
5421 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5422 else
5423 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5424 }
5425 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5426
5427 return rc;
5428}
5429
5430HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5431 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5432{
5433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5434
5435 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5436 if (FAILED(rc)) return rc;
5437
5438 ComObjPtr<SharedFolder> sharedFolder;
5439 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5440 if (SUCCEEDED(rc))
5441 return setError(VBOX_E_OBJECT_IN_USE,
5442 tr("Shared folder named '%s' already exists"),
5443 aName.c_str());
5444
5445 sharedFolder.createObject();
5446 rc = sharedFolder->init(i_getMachine(),
5447 aName,
5448 aHostPath,
5449 !!aWritable,
5450 !!aAutomount,
5451 aAutoMountPoint,
5452 true /* fFailOnError */);
5453 if (FAILED(rc)) return rc;
5454
5455 i_setModified(IsModified_SharedFolders);
5456 mHWData.backup();
5457 mHWData->mSharedFolders.push_back(sharedFolder);
5458
5459 /* inform the direct session if any */
5460 alock.release();
5461 i_onSharedFolderChange();
5462
5463 return S_OK;
5464}
5465
5466HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5467{
5468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5469
5470 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5471 if (FAILED(rc)) return rc;
5472
5473 ComObjPtr<SharedFolder> sharedFolder;
5474 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5475 if (FAILED(rc)) return rc;
5476
5477 i_setModified(IsModified_SharedFolders);
5478 mHWData.backup();
5479 mHWData->mSharedFolders.remove(sharedFolder);
5480
5481 /* inform the direct session if any */
5482 alock.release();
5483 i_onSharedFolderChange();
5484
5485 return S_OK;
5486}
5487
5488HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5489{
5490 /* start with No */
5491 *aCanShow = FALSE;
5492
5493 ComPtr<IInternalSessionControl> directControl;
5494 {
5495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5496
5497 if (mData->mSession.mState != SessionState_Locked)
5498 return setError(VBOX_E_INVALID_VM_STATE,
5499 tr("Machine is not locked for session (session state: %s)"),
5500 Global::stringifySessionState(mData->mSession.mState));
5501
5502 if (mData->mSession.mLockType == LockType_VM)
5503 directControl = mData->mSession.mDirectControl;
5504 }
5505
5506 /* ignore calls made after #OnSessionEnd() is called */
5507 if (!directControl)
5508 return S_OK;
5509
5510 LONG64 dummy;
5511 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5512}
5513
5514HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5515{
5516 ComPtr<IInternalSessionControl> directControl;
5517 {
5518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5519
5520 if (mData->mSession.mState != SessionState_Locked)
5521 return setError(E_FAIL,
5522 tr("Machine is not locked for session (session state: %s)"),
5523 Global::stringifySessionState(mData->mSession.mState));
5524
5525 if (mData->mSession.mLockType == LockType_VM)
5526 directControl = mData->mSession.mDirectControl;
5527 }
5528
5529 /* ignore calls made after #OnSessionEnd() is called */
5530 if (!directControl)
5531 return S_OK;
5532
5533 BOOL dummy;
5534 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5535}
5536
5537#ifdef VBOX_WITH_GUEST_PROPS
5538/**
5539 * Look up a guest property in VBoxSVC's internal structures.
5540 */
5541HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5542 com::Utf8Str &aValue,
5543 LONG64 *aTimestamp,
5544 com::Utf8Str &aFlags) const
5545{
5546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5547
5548 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5549 if (it != mHWData->mGuestProperties.end())
5550 {
5551 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5552 aValue = it->second.strValue;
5553 *aTimestamp = it->second.mTimestamp;
5554 GuestPropWriteFlags(it->second.mFlags, szFlags);
5555 aFlags = Utf8Str(szFlags);
5556 }
5557
5558 return S_OK;
5559}
5560
5561/**
5562 * Query the VM that a guest property belongs to for the property.
5563 * @returns E_ACCESSDENIED if the VM process is not available or not
5564 * currently handling queries and the lookup should then be done in
5565 * VBoxSVC.
5566 */
5567HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5568 com::Utf8Str &aValue,
5569 LONG64 *aTimestamp,
5570 com::Utf8Str &aFlags) const
5571{
5572 HRESULT rc = S_OK;
5573 BSTR bValue = NULL;
5574 BSTR bFlags = NULL;
5575
5576 ComPtr<IInternalSessionControl> directControl;
5577 {
5578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5579 if (mData->mSession.mLockType == LockType_VM)
5580 directControl = mData->mSession.mDirectControl;
5581 }
5582
5583 /* ignore calls made after #OnSessionEnd() is called */
5584 if (!directControl)
5585 rc = E_ACCESSDENIED;
5586 else
5587 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5588 0 /* accessMode */,
5589 &bValue, aTimestamp, &bFlags);
5590
5591 aValue = bValue;
5592 aFlags = bFlags;
5593
5594 return rc;
5595}
5596#endif // VBOX_WITH_GUEST_PROPS
5597
5598HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5599 com::Utf8Str &aValue,
5600 LONG64 *aTimestamp,
5601 com::Utf8Str &aFlags)
5602{
5603#ifndef VBOX_WITH_GUEST_PROPS
5604 ReturnComNotImplemented();
5605#else // VBOX_WITH_GUEST_PROPS
5606
5607 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5608
5609 if (rc == E_ACCESSDENIED)
5610 /* The VM is not running or the service is not (yet) accessible */
5611 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5612 return rc;
5613#endif // VBOX_WITH_GUEST_PROPS
5614}
5615
5616HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5617{
5618 LONG64 dummyTimestamp;
5619 com::Utf8Str dummyFlags;
5620 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5621 return rc;
5622
5623}
5624HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5625{
5626 com::Utf8Str dummyFlags;
5627 com::Utf8Str dummyValue;
5628 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5629 return rc;
5630}
5631
5632#ifdef VBOX_WITH_GUEST_PROPS
5633/**
5634 * Set a guest property in VBoxSVC's internal structures.
5635 */
5636HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5637 const com::Utf8Str &aFlags, bool fDelete)
5638{
5639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5640 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5641 if (FAILED(rc)) return rc;
5642
5643 try
5644 {
5645 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5646 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5647 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5648
5649 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5650 if (it == mHWData->mGuestProperties.end())
5651 {
5652 if (!fDelete)
5653 {
5654 i_setModified(IsModified_MachineData);
5655 mHWData.backupEx();
5656
5657 RTTIMESPEC time;
5658 HWData::GuestProperty prop;
5659 prop.strValue = Bstr(aValue).raw();
5660 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5661 prop.mFlags = fFlags;
5662 mHWData->mGuestProperties[aName] = prop;
5663 }
5664 }
5665 else
5666 {
5667 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5668 {
5669 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5670 }
5671 else
5672 {
5673 i_setModified(IsModified_MachineData);
5674 mHWData.backupEx();
5675
5676 /* The backupEx() operation invalidates our iterator,
5677 * so get a new one. */
5678 it = mHWData->mGuestProperties.find(aName);
5679 Assert(it != mHWData->mGuestProperties.end());
5680
5681 if (!fDelete)
5682 {
5683 RTTIMESPEC time;
5684 it->second.strValue = aValue;
5685 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5686 it->second.mFlags = fFlags;
5687 }
5688 else
5689 mHWData->mGuestProperties.erase(it);
5690 }
5691 }
5692
5693 if (SUCCEEDED(rc))
5694 {
5695 alock.release();
5696
5697 mParent->i_onGuestPropertyChange(mData->mUuid,
5698 Bstr(aName).raw(),
5699 Bstr(aValue).raw(),
5700 Bstr(aFlags).raw());
5701 }
5702 }
5703 catch (std::bad_alloc &)
5704 {
5705 rc = E_OUTOFMEMORY;
5706 }
5707
5708 return rc;
5709}
5710
5711/**
5712 * Set a property on the VM that that property belongs to.
5713 * @returns E_ACCESSDENIED if the VM process is not available or not
5714 * currently handling queries and the setting should then be done in
5715 * VBoxSVC.
5716 */
5717HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5718 const com::Utf8Str &aFlags, bool fDelete)
5719{
5720 HRESULT rc;
5721
5722 try
5723 {
5724 ComPtr<IInternalSessionControl> directControl;
5725 {
5726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5727 if (mData->mSession.mLockType == LockType_VM)
5728 directControl = mData->mSession.mDirectControl;
5729 }
5730
5731 BSTR dummy = NULL; /* will not be changed (setter) */
5732 LONG64 dummy64;
5733 if (!directControl)
5734 rc = E_ACCESSDENIED;
5735 else
5736 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5737 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5738 fDelete? 2: 1 /* accessMode */,
5739 &dummy, &dummy64, &dummy);
5740 }
5741 catch (std::bad_alloc &)
5742 {
5743 rc = E_OUTOFMEMORY;
5744 }
5745
5746 return rc;
5747}
5748#endif // VBOX_WITH_GUEST_PROPS
5749
5750HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5751 const com::Utf8Str &aFlags)
5752{
5753#ifndef VBOX_WITH_GUEST_PROPS
5754 ReturnComNotImplemented();
5755#else // VBOX_WITH_GUEST_PROPS
5756 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5757 if (rc == E_ACCESSDENIED)
5758 /* The VM is not running or the service is not (yet) accessible */
5759 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5760 return rc;
5761#endif // VBOX_WITH_GUEST_PROPS
5762}
5763
5764HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5765{
5766 return setGuestProperty(aProperty, aValue, "");
5767}
5768
5769HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5770{
5771#ifndef VBOX_WITH_GUEST_PROPS
5772 ReturnComNotImplemented();
5773#else // VBOX_WITH_GUEST_PROPS
5774 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5775 if (rc == E_ACCESSDENIED)
5776 /* The VM is not running or the service is not (yet) accessible */
5777 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5778 return rc;
5779#endif // VBOX_WITH_GUEST_PROPS
5780}
5781
5782#ifdef VBOX_WITH_GUEST_PROPS
5783/**
5784 * Enumerate the guest properties in VBoxSVC's internal structures.
5785 */
5786HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5787 std::vector<com::Utf8Str> &aNames,
5788 std::vector<com::Utf8Str> &aValues,
5789 std::vector<LONG64> &aTimestamps,
5790 std::vector<com::Utf8Str> &aFlags)
5791{
5792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5793 Utf8Str strPatterns(aPatterns);
5794
5795 /*
5796 * Look for matching patterns and build up a list.
5797 */
5798 HWData::GuestPropertyMap propMap;
5799 for (HWData::GuestPropertyMap::const_iterator
5800 it = mHWData->mGuestProperties.begin();
5801 it != mHWData->mGuestProperties.end();
5802 ++it)
5803 {
5804 if ( strPatterns.isEmpty()
5805 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5806 RTSTR_MAX,
5807 it->first.c_str(),
5808 RTSTR_MAX,
5809 NULL)
5810 )
5811 propMap.insert(*it);
5812 }
5813
5814 alock.release();
5815
5816 /*
5817 * And build up the arrays for returning the property information.
5818 */
5819 size_t cEntries = propMap.size();
5820
5821 aNames.resize(cEntries);
5822 aValues.resize(cEntries);
5823 aTimestamps.resize(cEntries);
5824 aFlags.resize(cEntries);
5825
5826 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5827 size_t i = 0;
5828 for (HWData::GuestPropertyMap::const_iterator
5829 it = propMap.begin();
5830 it != propMap.end();
5831 ++it, ++i)
5832 {
5833 aNames[i] = it->first;
5834 aValues[i] = it->second.strValue;
5835 aTimestamps[i] = it->second.mTimestamp;
5836 GuestPropWriteFlags(it->second.mFlags, szFlags);
5837 aFlags[i] = Utf8Str(szFlags);
5838 }
5839
5840 return S_OK;
5841}
5842
5843/**
5844 * Enumerate the properties managed by a VM.
5845 * @returns E_ACCESSDENIED if the VM process is not available or not
5846 * currently handling queries and the setting should then be done in
5847 * VBoxSVC.
5848 */
5849HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5850 std::vector<com::Utf8Str> &aNames,
5851 std::vector<com::Utf8Str> &aValues,
5852 std::vector<LONG64> &aTimestamps,
5853 std::vector<com::Utf8Str> &aFlags)
5854{
5855 HRESULT rc;
5856 ComPtr<IInternalSessionControl> directControl;
5857 {
5858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5859 if (mData->mSession.mLockType == LockType_VM)
5860 directControl = mData->mSession.mDirectControl;
5861 }
5862
5863 com::SafeArray<BSTR> bNames;
5864 com::SafeArray<BSTR> bValues;
5865 com::SafeArray<LONG64> bTimestamps;
5866 com::SafeArray<BSTR> bFlags;
5867
5868 if (!directControl)
5869 rc = E_ACCESSDENIED;
5870 else
5871 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5872 ComSafeArrayAsOutParam(bNames),
5873 ComSafeArrayAsOutParam(bValues),
5874 ComSafeArrayAsOutParam(bTimestamps),
5875 ComSafeArrayAsOutParam(bFlags));
5876 size_t i;
5877 aNames.resize(bNames.size());
5878 for (i = 0; i < bNames.size(); ++i)
5879 aNames[i] = Utf8Str(bNames[i]);
5880 aValues.resize(bValues.size());
5881 for (i = 0; i < bValues.size(); ++i)
5882 aValues[i] = Utf8Str(bValues[i]);
5883 aTimestamps.resize(bTimestamps.size());
5884 for (i = 0; i < bTimestamps.size(); ++i)
5885 aTimestamps[i] = bTimestamps[i];
5886 aFlags.resize(bFlags.size());
5887 for (i = 0; i < bFlags.size(); ++i)
5888 aFlags[i] = Utf8Str(bFlags[i]);
5889
5890 return rc;
5891}
5892#endif // VBOX_WITH_GUEST_PROPS
5893HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5894 std::vector<com::Utf8Str> &aNames,
5895 std::vector<com::Utf8Str> &aValues,
5896 std::vector<LONG64> &aTimestamps,
5897 std::vector<com::Utf8Str> &aFlags)
5898{
5899#ifndef VBOX_WITH_GUEST_PROPS
5900 ReturnComNotImplemented();
5901#else // VBOX_WITH_GUEST_PROPS
5902
5903 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5904
5905 if (rc == E_ACCESSDENIED)
5906 /* The VM is not running or the service is not (yet) accessible */
5907 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5908 return rc;
5909#endif // VBOX_WITH_GUEST_PROPS
5910}
5911
5912HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5913 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5914{
5915 MediumAttachmentList atts;
5916
5917 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5918 if (FAILED(rc)) return rc;
5919
5920 aMediumAttachments.resize(atts.size());
5921 size_t i = 0;
5922 for (MediumAttachmentList::const_iterator
5923 it = atts.begin();
5924 it != atts.end();
5925 ++it, ++i)
5926 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5927
5928 return S_OK;
5929}
5930
5931HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5932 LONG aControllerPort,
5933 LONG aDevice,
5934 ComPtr<IMediumAttachment> &aAttachment)
5935{
5936 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5937 aName.c_str(), aControllerPort, aDevice));
5938
5939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5940
5941 aAttachment = NULL;
5942
5943 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5944 aName,
5945 aControllerPort,
5946 aDevice);
5947 if (pAttach.isNull())
5948 return setError(VBOX_E_OBJECT_NOT_FOUND,
5949 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5950 aDevice, aControllerPort, aName.c_str());
5951
5952 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5953
5954 return S_OK;
5955}
5956
5957
5958HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5959 StorageBus_T aConnectionType,
5960 ComPtr<IStorageController> &aController)
5961{
5962 if ( (aConnectionType <= StorageBus_Null)
5963 || (aConnectionType > StorageBus_PCIe))
5964 return setError(E_INVALIDARG,
5965 tr("Invalid connection type: %d"),
5966 aConnectionType);
5967
5968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5969
5970 HRESULT rc = i_checkStateDependency(MutableStateDep);
5971 if (FAILED(rc)) return rc;
5972
5973 /* try to find one with the name first. */
5974 ComObjPtr<StorageController> ctrl;
5975
5976 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5977 if (SUCCEEDED(rc))
5978 return setError(VBOX_E_OBJECT_IN_USE,
5979 tr("Storage controller named '%s' already exists"),
5980 aName.c_str());
5981
5982 ctrl.createObject();
5983
5984 /* get a new instance number for the storage controller */
5985 ULONG ulInstance = 0;
5986 bool fBootable = true;
5987 for (StorageControllerList::const_iterator
5988 it = mStorageControllers->begin();
5989 it != mStorageControllers->end();
5990 ++it)
5991 {
5992 if ((*it)->i_getStorageBus() == aConnectionType)
5993 {
5994 ULONG ulCurInst = (*it)->i_getInstance();
5995
5996 if (ulCurInst >= ulInstance)
5997 ulInstance = ulCurInst + 1;
5998
5999 /* Only one controller of each type can be marked as bootable. */
6000 if ((*it)->i_getBootable())
6001 fBootable = false;
6002 }
6003 }
6004
6005 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6006 if (FAILED(rc)) return rc;
6007
6008 i_setModified(IsModified_Storage);
6009 mStorageControllers.backup();
6010 mStorageControllers->push_back(ctrl);
6011
6012 ctrl.queryInterfaceTo(aController.asOutParam());
6013
6014 /* inform the direct session if any */
6015 alock.release();
6016 i_onStorageControllerChange();
6017
6018 return S_OK;
6019}
6020
6021HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6022 ComPtr<IStorageController> &aStorageController)
6023{
6024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6025
6026 ComObjPtr<StorageController> ctrl;
6027
6028 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6029 if (SUCCEEDED(rc))
6030 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6031
6032 return rc;
6033}
6034
6035HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6036 ULONG aInstance,
6037 ComPtr<IStorageController> &aStorageController)
6038{
6039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6040
6041 for (StorageControllerList::const_iterator
6042 it = mStorageControllers->begin();
6043 it != mStorageControllers->end();
6044 ++it)
6045 {
6046 if ( (*it)->i_getStorageBus() == aConnectionType
6047 && (*it)->i_getInstance() == aInstance)
6048 {
6049 (*it).queryInterfaceTo(aStorageController.asOutParam());
6050 return S_OK;
6051 }
6052 }
6053
6054 return setError(VBOX_E_OBJECT_NOT_FOUND,
6055 tr("Could not find a storage controller with instance number '%lu'"),
6056 aInstance);
6057}
6058
6059HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6060{
6061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6062
6063 HRESULT rc = i_checkStateDependency(MutableStateDep);
6064 if (FAILED(rc)) return rc;
6065
6066 ComObjPtr<StorageController> ctrl;
6067
6068 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6069 if (SUCCEEDED(rc))
6070 {
6071 /* Ensure that only one controller of each type is marked as bootable. */
6072 if (aBootable == TRUE)
6073 {
6074 for (StorageControllerList::const_iterator
6075 it = mStorageControllers->begin();
6076 it != mStorageControllers->end();
6077 ++it)
6078 {
6079 ComObjPtr<StorageController> aCtrl = (*it);
6080
6081 if ( (aCtrl->i_getName() != aName)
6082 && aCtrl->i_getBootable() == TRUE
6083 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6084 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6085 {
6086 aCtrl->i_setBootable(FALSE);
6087 break;
6088 }
6089 }
6090 }
6091
6092 if (SUCCEEDED(rc))
6093 {
6094 ctrl->i_setBootable(aBootable);
6095 i_setModified(IsModified_Storage);
6096 }
6097 }
6098
6099 if (SUCCEEDED(rc))
6100 {
6101 /* inform the direct session if any */
6102 alock.release();
6103 i_onStorageControllerChange();
6104 }
6105
6106 return rc;
6107}
6108
6109HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6110{
6111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6112
6113 HRESULT rc = i_checkStateDependency(MutableStateDep);
6114 if (FAILED(rc)) return rc;
6115
6116 ComObjPtr<StorageController> ctrl;
6117 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6118 if (FAILED(rc)) return rc;
6119
6120 {
6121 /* find all attached devices to the appropriate storage controller and detach them all */
6122 // make a temporary list because detachDevice invalidates iterators into
6123 // mMediumAttachments
6124 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6125
6126 for (MediumAttachmentList::const_iterator
6127 it = llAttachments2.begin();
6128 it != llAttachments2.end();
6129 ++it)
6130 {
6131 MediumAttachment *pAttachTemp = *it;
6132
6133 AutoCaller localAutoCaller(pAttachTemp);
6134 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6135
6136 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6137
6138 if (pAttachTemp->i_getControllerName() == aName)
6139 {
6140 rc = i_detachDevice(pAttachTemp, alock, NULL);
6141 if (FAILED(rc)) return rc;
6142 }
6143 }
6144 }
6145
6146 /* We can remove it now. */
6147 i_setModified(IsModified_Storage);
6148 mStorageControllers.backup();
6149
6150 ctrl->i_unshare();
6151
6152 mStorageControllers->remove(ctrl);
6153
6154 /* inform the direct session if any */
6155 alock.release();
6156 i_onStorageControllerChange();
6157
6158 return S_OK;
6159}
6160
6161HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6162 ComPtr<IUSBController> &aController)
6163{
6164 if ( (aType <= USBControllerType_Null)
6165 || (aType >= USBControllerType_Last))
6166 return setError(E_INVALIDARG,
6167 tr("Invalid USB controller type: %d"),
6168 aType);
6169
6170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6171
6172 HRESULT rc = i_checkStateDependency(MutableStateDep);
6173 if (FAILED(rc)) return rc;
6174
6175 /* try to find one with the same type first. */
6176 ComObjPtr<USBController> ctrl;
6177
6178 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6179 if (SUCCEEDED(rc))
6180 return setError(VBOX_E_OBJECT_IN_USE,
6181 tr("USB controller named '%s' already exists"),
6182 aName.c_str());
6183
6184 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6185 ULONG maxInstances;
6186 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6187 if (FAILED(rc))
6188 return rc;
6189
6190 ULONG cInstances = i_getUSBControllerCountByType(aType);
6191 if (cInstances >= maxInstances)
6192 return setError(E_INVALIDARG,
6193 tr("Too many USB controllers of this type"));
6194
6195 ctrl.createObject();
6196
6197 rc = ctrl->init(this, aName, aType);
6198 if (FAILED(rc)) return rc;
6199
6200 i_setModified(IsModified_USB);
6201 mUSBControllers.backup();
6202 mUSBControllers->push_back(ctrl);
6203
6204 ctrl.queryInterfaceTo(aController.asOutParam());
6205
6206 /* inform the direct session if any */
6207 alock.release();
6208 i_onUSBControllerChange();
6209
6210 return S_OK;
6211}
6212
6213HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6214{
6215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6216
6217 ComObjPtr<USBController> ctrl;
6218
6219 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6220 if (SUCCEEDED(rc))
6221 ctrl.queryInterfaceTo(aController.asOutParam());
6222
6223 return rc;
6224}
6225
6226HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6227 ULONG *aControllers)
6228{
6229 if ( (aType <= USBControllerType_Null)
6230 || (aType >= USBControllerType_Last))
6231 return setError(E_INVALIDARG,
6232 tr("Invalid USB controller type: %d"),
6233 aType);
6234
6235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6236
6237 ComObjPtr<USBController> ctrl;
6238
6239 *aControllers = i_getUSBControllerCountByType(aType);
6240
6241 return S_OK;
6242}
6243
6244HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6245{
6246
6247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6248
6249 HRESULT rc = i_checkStateDependency(MutableStateDep);
6250 if (FAILED(rc)) return rc;
6251
6252 ComObjPtr<USBController> ctrl;
6253 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6254 if (FAILED(rc)) return rc;
6255
6256 i_setModified(IsModified_USB);
6257 mUSBControllers.backup();
6258
6259 ctrl->i_unshare();
6260
6261 mUSBControllers->remove(ctrl);
6262
6263 /* inform the direct session if any */
6264 alock.release();
6265 i_onUSBControllerChange();
6266
6267 return S_OK;
6268}
6269
6270HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6271 ULONG *aOriginX,
6272 ULONG *aOriginY,
6273 ULONG *aWidth,
6274 ULONG *aHeight,
6275 BOOL *aEnabled)
6276{
6277 uint32_t u32OriginX= 0;
6278 uint32_t u32OriginY= 0;
6279 uint32_t u32Width = 0;
6280 uint32_t u32Height = 0;
6281 uint16_t u16Flags = 0;
6282
6283 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6284 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6285 if (RT_FAILURE(vrc))
6286 {
6287#ifdef RT_OS_WINDOWS
6288 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6289 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6290 * So just assign fEnable to TRUE again.
6291 * The right fix would be to change GUI API wrappers to make sure that parameters
6292 * are changed only if API succeeds.
6293 */
6294 *aEnabled = TRUE;
6295#endif
6296 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6297 tr("Saved guest size is not available (%Rrc)"),
6298 vrc);
6299 }
6300
6301 *aOriginX = u32OriginX;
6302 *aOriginY = u32OriginY;
6303 *aWidth = u32Width;
6304 *aHeight = u32Height;
6305 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6306
6307 return S_OK;
6308}
6309
6310HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6311 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6312{
6313 if (aScreenId != 0)
6314 return E_NOTIMPL;
6315
6316 if ( aBitmapFormat != BitmapFormat_BGR0
6317 && aBitmapFormat != BitmapFormat_BGRA
6318 && aBitmapFormat != BitmapFormat_RGBA
6319 && aBitmapFormat != BitmapFormat_PNG)
6320 return setError(E_NOTIMPL,
6321 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6322
6323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 uint8_t *pu8Data = NULL;
6326 uint32_t cbData = 0;
6327 uint32_t u32Width = 0;
6328 uint32_t u32Height = 0;
6329
6330 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6331
6332 if (RT_FAILURE(vrc))
6333 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6334 tr("Saved thumbnail data is not available (%Rrc)"),
6335 vrc);
6336
6337 HRESULT hr = S_OK;
6338
6339 *aWidth = u32Width;
6340 *aHeight = u32Height;
6341
6342 if (cbData > 0)
6343 {
6344 /* Convert pixels to the format expected by the API caller. */
6345 if (aBitmapFormat == BitmapFormat_BGR0)
6346 {
6347 /* [0] B, [1] G, [2] R, [3] 0. */
6348 aData.resize(cbData);
6349 memcpy(&aData.front(), pu8Data, cbData);
6350 }
6351 else if (aBitmapFormat == BitmapFormat_BGRA)
6352 {
6353 /* [0] B, [1] G, [2] R, [3] A. */
6354 aData.resize(cbData);
6355 for (uint32_t i = 0; i < cbData; i += 4)
6356 {
6357 aData[i] = pu8Data[i];
6358 aData[i + 1] = pu8Data[i + 1];
6359 aData[i + 2] = pu8Data[i + 2];
6360 aData[i + 3] = 0xff;
6361 }
6362 }
6363 else if (aBitmapFormat == BitmapFormat_RGBA)
6364 {
6365 /* [0] R, [1] G, [2] B, [3] A. */
6366 aData.resize(cbData);
6367 for (uint32_t i = 0; i < cbData; i += 4)
6368 {
6369 aData[i] = pu8Data[i + 2];
6370 aData[i + 1] = pu8Data[i + 1];
6371 aData[i + 2] = pu8Data[i];
6372 aData[i + 3] = 0xff;
6373 }
6374 }
6375 else if (aBitmapFormat == BitmapFormat_PNG)
6376 {
6377 uint8_t *pu8PNG = NULL;
6378 uint32_t cbPNG = 0;
6379 uint32_t cxPNG = 0;
6380 uint32_t cyPNG = 0;
6381
6382 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6383
6384 if (RT_SUCCESS(vrc))
6385 {
6386 aData.resize(cbPNG);
6387 if (cbPNG)
6388 memcpy(&aData.front(), pu8PNG, cbPNG);
6389 }
6390 else
6391 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6392 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6393 vrc);
6394
6395 RTMemFree(pu8PNG);
6396 }
6397 }
6398
6399 freeSavedDisplayScreenshot(pu8Data);
6400
6401 return hr;
6402}
6403
6404HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6405 ULONG *aWidth,
6406 ULONG *aHeight,
6407 std::vector<BitmapFormat_T> &aBitmapFormats)
6408{
6409 if (aScreenId != 0)
6410 return E_NOTIMPL;
6411
6412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6413
6414 uint8_t *pu8Data = NULL;
6415 uint32_t cbData = 0;
6416 uint32_t u32Width = 0;
6417 uint32_t u32Height = 0;
6418
6419 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6420
6421 if (RT_FAILURE(vrc))
6422 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6423 tr("Saved screenshot data is not available (%Rrc)"),
6424 vrc);
6425
6426 *aWidth = u32Width;
6427 *aHeight = u32Height;
6428 aBitmapFormats.resize(1);
6429 aBitmapFormats[0] = BitmapFormat_PNG;
6430
6431 freeSavedDisplayScreenshot(pu8Data);
6432
6433 return S_OK;
6434}
6435
6436HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6437 BitmapFormat_T aBitmapFormat,
6438 ULONG *aWidth,
6439 ULONG *aHeight,
6440 std::vector<BYTE> &aData)
6441{
6442 if (aScreenId != 0)
6443 return E_NOTIMPL;
6444
6445 if (aBitmapFormat != BitmapFormat_PNG)
6446 return E_NOTIMPL;
6447
6448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6449
6450 uint8_t *pu8Data = NULL;
6451 uint32_t cbData = 0;
6452 uint32_t u32Width = 0;
6453 uint32_t u32Height = 0;
6454
6455 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6456
6457 if (RT_FAILURE(vrc))
6458 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6459 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6460 vrc);
6461
6462 *aWidth = u32Width;
6463 *aHeight = u32Height;
6464
6465 aData.resize(cbData);
6466 if (cbData)
6467 memcpy(&aData.front(), pu8Data, cbData);
6468
6469 freeSavedDisplayScreenshot(pu8Data);
6470
6471 return S_OK;
6472}
6473
6474HRESULT Machine::hotPlugCPU(ULONG aCpu)
6475{
6476 HRESULT rc = S_OK;
6477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6478
6479 if (!mHWData->mCPUHotPlugEnabled)
6480 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6481
6482 if (aCpu >= mHWData->mCPUCount)
6483 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6484
6485 if (mHWData->mCPUAttached[aCpu])
6486 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6487
6488 alock.release();
6489 rc = i_onCPUChange(aCpu, false);
6490 alock.acquire();
6491 if (FAILED(rc)) return rc;
6492
6493 i_setModified(IsModified_MachineData);
6494 mHWData.backup();
6495 mHWData->mCPUAttached[aCpu] = true;
6496
6497 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6498 if (Global::IsOnline(mData->mMachineState))
6499 i_saveSettings(NULL);
6500
6501 return S_OK;
6502}
6503
6504HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6505{
6506 HRESULT rc = S_OK;
6507
6508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6509
6510 if (!mHWData->mCPUHotPlugEnabled)
6511 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6512
6513 if (aCpu >= SchemaDefs::MaxCPUCount)
6514 return setError(E_INVALIDARG,
6515 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6516 SchemaDefs::MaxCPUCount);
6517
6518 if (!mHWData->mCPUAttached[aCpu])
6519 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6520
6521 /* CPU 0 can't be detached */
6522 if (aCpu == 0)
6523 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6524
6525 alock.release();
6526 rc = i_onCPUChange(aCpu, true);
6527 alock.acquire();
6528 if (FAILED(rc)) return rc;
6529
6530 i_setModified(IsModified_MachineData);
6531 mHWData.backup();
6532 mHWData->mCPUAttached[aCpu] = false;
6533
6534 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6535 if (Global::IsOnline(mData->mMachineState))
6536 i_saveSettings(NULL);
6537
6538 return S_OK;
6539}
6540
6541HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6542{
6543 *aAttached = false;
6544
6545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6546
6547 /* If hotplug is enabled the CPU is always enabled. */
6548 if (!mHWData->mCPUHotPlugEnabled)
6549 {
6550 if (aCpu < mHWData->mCPUCount)
6551 *aAttached = true;
6552 }
6553 else
6554 {
6555 if (aCpu < SchemaDefs::MaxCPUCount)
6556 *aAttached = mHWData->mCPUAttached[aCpu];
6557 }
6558
6559 return S_OK;
6560}
6561
6562HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6563{
6564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6565
6566 Utf8Str log = i_getLogFilename(aIdx);
6567 if (!RTFileExists(log.c_str()))
6568 log.setNull();
6569 aFilename = log;
6570
6571 return S_OK;
6572}
6573
6574HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6575{
6576 if (aSize < 0)
6577 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6578
6579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6580
6581 HRESULT rc = S_OK;
6582 Utf8Str log = i_getLogFilename(aIdx);
6583
6584 /* do not unnecessarily hold the lock while doing something which does
6585 * not need the lock and potentially takes a long time. */
6586 alock.release();
6587
6588 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6589 * keeps the SOAP reply size under 1M for the webservice (we're using
6590 * base64 encoded strings for binary data for years now, avoiding the
6591 * expansion of each byte array element to approx. 25 bytes of XML. */
6592 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6593 aData.resize(cbData);
6594
6595 RTFILE LogFile;
6596 int vrc = RTFileOpen(&LogFile, log.c_str(),
6597 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6598 if (RT_SUCCESS(vrc))
6599 {
6600 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6601 if (RT_SUCCESS(vrc))
6602 aData.resize(cbData);
6603 else
6604 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6605 tr("Could not read log file '%s' (%Rrc)"),
6606 log.c_str(), vrc);
6607 RTFileClose(LogFile);
6608 }
6609 else
6610 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6611 tr("Could not open log file '%s' (%Rrc)"),
6612 log.c_str(), vrc);
6613
6614 if (FAILED(rc))
6615 aData.resize(0);
6616
6617 return rc;
6618}
6619
6620
6621/**
6622 * Currently this method doesn't attach device to the running VM,
6623 * just makes sure it's plugged on next VM start.
6624 */
6625HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6626{
6627 // lock scope
6628 {
6629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6630
6631 HRESULT rc = i_checkStateDependency(MutableStateDep);
6632 if (FAILED(rc)) return rc;
6633
6634 ChipsetType_T aChipset = ChipsetType_PIIX3;
6635 COMGETTER(ChipsetType)(&aChipset);
6636
6637 if (aChipset != ChipsetType_ICH9)
6638 {
6639 return setError(E_INVALIDARG,
6640 tr("Host PCI attachment only supported with ICH9 chipset"));
6641 }
6642
6643 // check if device with this host PCI address already attached
6644 for (HWData::PCIDeviceAssignmentList::const_iterator
6645 it = mHWData->mPCIDeviceAssignments.begin();
6646 it != mHWData->mPCIDeviceAssignments.end();
6647 ++it)
6648 {
6649 LONG iHostAddress = -1;
6650 ComPtr<PCIDeviceAttachment> pAttach;
6651 pAttach = *it;
6652 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6653 if (iHostAddress == aHostAddress)
6654 return setError(E_INVALIDARG,
6655 tr("Device with host PCI address already attached to this VM"));
6656 }
6657
6658 ComObjPtr<PCIDeviceAttachment> pda;
6659 char name[32];
6660
6661 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6662 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6663 pda.createObject();
6664 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6665 i_setModified(IsModified_MachineData);
6666 mHWData.backup();
6667 mHWData->mPCIDeviceAssignments.push_back(pda);
6668 }
6669
6670 return S_OK;
6671}
6672
6673/**
6674 * Currently this method doesn't detach device from the running VM,
6675 * just makes sure it's not plugged on next VM start.
6676 */
6677HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6678{
6679 ComObjPtr<PCIDeviceAttachment> pAttach;
6680 bool fRemoved = false;
6681 HRESULT rc;
6682
6683 // lock scope
6684 {
6685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6686
6687 rc = i_checkStateDependency(MutableStateDep);
6688 if (FAILED(rc)) return rc;
6689
6690 for (HWData::PCIDeviceAssignmentList::const_iterator
6691 it = mHWData->mPCIDeviceAssignments.begin();
6692 it != mHWData->mPCIDeviceAssignments.end();
6693 ++it)
6694 {
6695 LONG iHostAddress = -1;
6696 pAttach = *it;
6697 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6698 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6699 {
6700 i_setModified(IsModified_MachineData);
6701 mHWData.backup();
6702 mHWData->mPCIDeviceAssignments.remove(pAttach);
6703 fRemoved = true;
6704 break;
6705 }
6706 }
6707 }
6708
6709
6710 /* Fire event outside of the lock */
6711 if (fRemoved)
6712 {
6713 Assert(!pAttach.isNull());
6714 ComPtr<IEventSource> es;
6715 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6716 Assert(SUCCEEDED(rc));
6717 Bstr mid;
6718 rc = this->COMGETTER(Id)(mid.asOutParam());
6719 Assert(SUCCEEDED(rc));
6720 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6721 }
6722
6723 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6724 tr("No host PCI device %08x attached"),
6725 aHostAddress
6726 );
6727}
6728
6729HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6730{
6731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6732
6733 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6734 size_t i = 0;
6735 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6736 it = mHWData->mPCIDeviceAssignments.begin();
6737 it != mHWData->mPCIDeviceAssignments.end();
6738 ++it, ++i)
6739 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6740
6741 return S_OK;
6742}
6743
6744HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6745{
6746 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6747
6748 return S_OK;
6749}
6750
6751HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6752{
6753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6754
6755 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6756
6757 return S_OK;
6758}
6759
6760HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6761{
6762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6763 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6764 if (SUCCEEDED(hrc))
6765 {
6766 hrc = mHWData.backupEx();
6767 if (SUCCEEDED(hrc))
6768 {
6769 i_setModified(IsModified_MachineData);
6770 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6771 }
6772 }
6773 return hrc;
6774}
6775
6776HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6777{
6778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6779 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6780 return S_OK;
6781}
6782
6783HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6784{
6785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6786 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6787 if (SUCCEEDED(hrc))
6788 {
6789 hrc = mHWData.backupEx();
6790 if (SUCCEEDED(hrc))
6791 {
6792 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6793 if (SUCCEEDED(hrc))
6794 i_setModified(IsModified_MachineData);
6795 }
6796 }
6797 return hrc;
6798}
6799
6800HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6801{
6802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6803
6804 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6805
6806 return S_OK;
6807}
6808
6809HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6810{
6811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6812 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6813 if (SUCCEEDED(hrc))
6814 {
6815 hrc = mHWData.backupEx();
6816 if (SUCCEEDED(hrc))
6817 {
6818 i_setModified(IsModified_MachineData);
6819 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6820 }
6821 }
6822 return hrc;
6823}
6824
6825HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6826{
6827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6828
6829 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6830
6831 return S_OK;
6832}
6833
6834HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6835{
6836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6839 if ( SUCCEEDED(hrc)
6840 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6841 {
6842 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6843 int vrc;
6844
6845 if (aAutostartEnabled)
6846 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6847 else
6848 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6849
6850 if (RT_SUCCESS(vrc))
6851 {
6852 hrc = mHWData.backupEx();
6853 if (SUCCEEDED(hrc))
6854 {
6855 i_setModified(IsModified_MachineData);
6856 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6857 }
6858 }
6859 else if (vrc == VERR_NOT_SUPPORTED)
6860 hrc = setError(VBOX_E_NOT_SUPPORTED,
6861 tr("The VM autostart feature is not supported on this platform"));
6862 else if (vrc == VERR_PATH_NOT_FOUND)
6863 hrc = setError(E_FAIL,
6864 tr("The path to the autostart database is not set"));
6865 else
6866 hrc = setError(E_UNEXPECTED,
6867 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6868 aAutostartEnabled ? "Adding" : "Removing",
6869 mUserData->s.strName.c_str(), vrc);
6870 }
6871 return hrc;
6872}
6873
6874HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6875{
6876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6877
6878 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6879
6880 return S_OK;
6881}
6882
6883HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6884{
6885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6886 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6887 if (SUCCEEDED(hrc))
6888 {
6889 hrc = mHWData.backupEx();
6890 if (SUCCEEDED(hrc))
6891 {
6892 i_setModified(IsModified_MachineData);
6893 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6894 }
6895 }
6896 return hrc;
6897}
6898
6899HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6900{
6901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6902
6903 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6904
6905 return S_OK;
6906}
6907
6908HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6909{
6910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6911 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6912 if ( SUCCEEDED(hrc)
6913 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6914 {
6915 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6916 int vrc;
6917
6918 if (aAutostopType != AutostopType_Disabled)
6919 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6920 else
6921 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6922
6923 if (RT_SUCCESS(vrc))
6924 {
6925 hrc = mHWData.backupEx();
6926 if (SUCCEEDED(hrc))
6927 {
6928 i_setModified(IsModified_MachineData);
6929 mHWData->mAutostart.enmAutostopType = aAutostopType;
6930 }
6931 }
6932 else if (vrc == VERR_NOT_SUPPORTED)
6933 hrc = setError(VBOX_E_NOT_SUPPORTED,
6934 tr("The VM autostop feature is not supported on this platform"));
6935 else if (vrc == VERR_PATH_NOT_FOUND)
6936 hrc = setError(E_FAIL,
6937 tr("The path to the autostart database is not set"));
6938 else
6939 hrc = setError(E_UNEXPECTED,
6940 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6941 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6942 mUserData->s.strName.c_str(), vrc);
6943 }
6944 return hrc;
6945}
6946
6947HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6948{
6949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6950
6951 aDefaultFrontend = mHWData->mDefaultFrontend;
6952
6953 return S_OK;
6954}
6955
6956HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6957{
6958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6959 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6960 if (SUCCEEDED(hrc))
6961 {
6962 hrc = mHWData.backupEx();
6963 if (SUCCEEDED(hrc))
6964 {
6965 i_setModified(IsModified_MachineData);
6966 mHWData->mDefaultFrontend = aDefaultFrontend;
6967 }
6968 }
6969 return hrc;
6970}
6971
6972HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6973{
6974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6975 size_t cbIcon = mUserData->s.ovIcon.size();
6976 aIcon.resize(cbIcon);
6977 if (cbIcon)
6978 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6979 return S_OK;
6980}
6981
6982HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6983{
6984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6985 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6986 if (SUCCEEDED(hrc))
6987 {
6988 i_setModified(IsModified_MachineData);
6989 mUserData.backup();
6990 size_t cbIcon = aIcon.size();
6991 mUserData->s.ovIcon.resize(cbIcon);
6992 if (cbIcon)
6993 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6994 }
6995 return hrc;
6996}
6997
6998HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6999{
7000#ifdef VBOX_WITH_USB
7001 *aUSBProxyAvailable = true;
7002#else
7003 *aUSBProxyAvailable = false;
7004#endif
7005 return S_OK;
7006}
7007
7008HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7009{
7010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7011
7012 aVMProcessPriority = mUserData->s.strVMPriority;
7013
7014 return S_OK;
7015}
7016
7017HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7018{
7019 RT_NOREF(aVMProcessPriority);
7020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7021 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7022 if (SUCCEEDED(hrc))
7023 {
7024 /** @todo r=klaus: currently this is marked as not implemented, as
7025 * the code for setting the priority of the process is not there
7026 * (neither when starting the VM nor at runtime). */
7027 ReturnComNotImplemented();
7028#if 0
7029 hrc = mUserData.backupEx();
7030 if (SUCCEEDED(hrc))
7031 {
7032 i_setModified(IsModified_MachineData);
7033 mUserData->s.strVMPriority = aVMProcessPriority;
7034 }
7035#endif
7036 }
7037 return hrc;
7038}
7039
7040HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7041 ComPtr<IProgress> &aProgress)
7042{
7043 ComObjPtr<Progress> pP;
7044 Progress *ppP = pP;
7045 IProgress *iP = static_cast<IProgress *>(ppP);
7046 IProgress **pProgress = &iP;
7047
7048 IMachine *pTarget = aTarget;
7049
7050 /* Convert the options. */
7051 RTCList<CloneOptions_T> optList;
7052 if (aOptions.size())
7053 for (size_t i = 0; i < aOptions.size(); ++i)
7054 optList.append(aOptions[i]);
7055
7056 if (optList.contains(CloneOptions_Link))
7057 {
7058 if (!i_isSnapshotMachine())
7059 return setError(E_INVALIDARG,
7060 tr("Linked clone can only be created from a snapshot"));
7061 if (aMode != CloneMode_MachineState)
7062 return setError(E_INVALIDARG,
7063 tr("Linked clone can only be created for a single machine state"));
7064 }
7065 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7066
7067 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7068
7069 HRESULT rc = pWorker->start(pProgress);
7070
7071 pP = static_cast<Progress *>(*pProgress);
7072 pP.queryInterfaceTo(aProgress.asOutParam());
7073
7074 return rc;
7075
7076}
7077
7078HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7079 const com::Utf8Str &aType,
7080 ComPtr<IProgress> &aProgress)
7081{
7082 LogFlowThisFuncEnter();
7083
7084 ComObjPtr<Progress> progress;
7085
7086 progress.createObject();
7087
7088 HRESULT rc = S_OK;
7089 Utf8Str targetPath = aTargetPath;
7090 Utf8Str type = aType;
7091
7092 /* Initialize our worker task */
7093 MachineMoveVM* task = NULL;
7094 try
7095 {
7096 task = new MachineMoveVM(this, targetPath, type, progress);
7097 }
7098 catch(...)
7099 {
7100 delete task;
7101 return rc;
7102 }
7103
7104 /*
7105 * task pointer will be owned by the ThreadTask class.
7106 * There is no need to call operator "delete" in the end.
7107 */
7108 rc = task->init();
7109 if (SUCCEEDED(rc))
7110 {
7111 rc = task->createThread();
7112 if (FAILED(rc))
7113 {
7114 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7115 }
7116
7117 /* Return progress to the caller */
7118 progress.queryInterfaceTo(aProgress.asOutParam());
7119 }
7120
7121 LogFlowThisFuncLeave();
7122 return rc;
7123
7124}
7125
7126HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7127{
7128 NOREF(aProgress);
7129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 // This check should always fail.
7132 HRESULT rc = i_checkStateDependency(MutableStateDep);
7133 if (FAILED(rc)) return rc;
7134
7135 AssertFailedReturn(E_NOTIMPL);
7136}
7137
7138HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7139{
7140 NOREF(aSavedStateFile);
7141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7142
7143 // This check should always fail.
7144 HRESULT rc = i_checkStateDependency(MutableStateDep);
7145 if (FAILED(rc)) return rc;
7146
7147 AssertFailedReturn(E_NOTIMPL);
7148}
7149
7150HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7151{
7152 NOREF(aFRemoveFile);
7153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 // This check should always fail.
7156 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7157 if (FAILED(rc)) return rc;
7158
7159 AssertFailedReturn(E_NOTIMPL);
7160}
7161
7162// public methods for internal purposes
7163/////////////////////////////////////////////////////////////////////////////
7164
7165/**
7166 * Adds the given IsModified_* flag to the dirty flags of the machine.
7167 * This must be called either during i_loadSettings or under the machine write lock.
7168 * @param fl Flag
7169 * @param fAllowStateModification If state modifications are allowed.
7170 */
7171void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7172{
7173 mData->flModifications |= fl;
7174 if (fAllowStateModification && i_isStateModificationAllowed())
7175 mData->mCurrentStateModified = true;
7176}
7177
7178/**
7179 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7180 * care of the write locking.
7181 *
7182 * @param fModification The flag to add.
7183 * @param fAllowStateModification If state modifications are allowed.
7184 */
7185void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7186{
7187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7188 i_setModified(fModification, fAllowStateModification);
7189}
7190
7191/**
7192 * Saves the registry entry of this machine to the given configuration node.
7193 *
7194 * @param data Machine registry data.
7195 *
7196 * @note locks this object for reading.
7197 */
7198HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7199{
7200 AutoLimitedCaller autoCaller(this);
7201 AssertComRCReturnRC(autoCaller.rc());
7202
7203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7204
7205 data.uuid = mData->mUuid;
7206 data.strSettingsFile = mData->m_strConfigFile;
7207
7208 return S_OK;
7209}
7210
7211/**
7212 * Calculates the absolute path of the given path taking the directory of the
7213 * machine settings file as the current directory.
7214 *
7215 * @param strPath Path to calculate the absolute path for.
7216 * @param aResult Where to put the result (used only on success, can be the
7217 * same Utf8Str instance as passed in @a aPath).
7218 * @return IPRT result.
7219 *
7220 * @note Locks this object for reading.
7221 */
7222int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7223{
7224 AutoCaller autoCaller(this);
7225 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7226
7227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7228
7229 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7230
7231 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7232
7233 strSettingsDir.stripFilename();
7234 char folder[RTPATH_MAX];
7235 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7236 if (RT_SUCCESS(vrc))
7237 aResult = folder;
7238
7239 return vrc;
7240}
7241
7242/**
7243 * Copies strSource to strTarget, making it relative to the machine folder
7244 * if it is a subdirectory thereof, or simply copying it otherwise.
7245 *
7246 * @param strSource Path to evaluate and copy.
7247 * @param strTarget Buffer to receive target path.
7248 *
7249 * @note Locks this object for reading.
7250 */
7251void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7252 Utf8Str &strTarget)
7253{
7254 AutoCaller autoCaller(this);
7255 AssertComRCReturn(autoCaller.rc(), (void)0);
7256
7257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7258
7259 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7260 // use strTarget as a temporary buffer to hold the machine settings dir
7261 strTarget = mData->m_strConfigFileFull;
7262 strTarget.stripFilename();
7263 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7264 {
7265 // is relative: then append what's left
7266 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7267 // for empty paths (only possible for subdirs) use "." to avoid
7268 // triggering default settings for not present config attributes.
7269 if (strTarget.isEmpty())
7270 strTarget = ".";
7271 }
7272 else
7273 // is not relative: then overwrite
7274 strTarget = strSource;
7275}
7276
7277/**
7278 * Returns the full path to the machine's log folder in the
7279 * \a aLogFolder argument.
7280 */
7281void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7282{
7283 AutoCaller autoCaller(this);
7284 AssertComRCReturnVoid(autoCaller.rc());
7285
7286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7287
7288 char szTmp[RTPATH_MAX];
7289 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7290 if (RT_SUCCESS(vrc))
7291 {
7292 if (szTmp[0] && !mUserData.isNull())
7293 {
7294 char szTmp2[RTPATH_MAX];
7295 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7296 if (RT_SUCCESS(vrc))
7297 aLogFolder = Utf8StrFmt("%s%c%s",
7298 szTmp2,
7299 RTPATH_DELIMITER,
7300 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7301 }
7302 else
7303 vrc = VERR_PATH_IS_RELATIVE;
7304 }
7305
7306 if (RT_FAILURE(vrc))
7307 {
7308 // fallback if VBOX_USER_LOGHOME is not set or invalid
7309 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7310 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7311 aLogFolder.append(RTPATH_DELIMITER);
7312 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7313 }
7314}
7315
7316/**
7317 * Returns the full path to the machine's log file for an given index.
7318 */
7319Utf8Str Machine::i_getLogFilename(ULONG idx)
7320{
7321 Utf8Str logFolder;
7322 getLogFolder(logFolder);
7323 Assert(logFolder.length());
7324
7325 Utf8Str log;
7326 if (idx == 0)
7327 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7328#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7329 else if (idx == 1)
7330 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7331 else
7332 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7333#else
7334 else
7335 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7336#endif
7337 return log;
7338}
7339
7340/**
7341 * Returns the full path to the machine's hardened log file.
7342 */
7343Utf8Str Machine::i_getHardeningLogFilename(void)
7344{
7345 Utf8Str strFilename;
7346 getLogFolder(strFilename);
7347 Assert(strFilename.length());
7348 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7349 return strFilename;
7350}
7351
7352
7353/**
7354 * Composes a unique saved state filename based on the current system time. The filename is
7355 * granular to the second so this will work so long as no more than one snapshot is taken on
7356 * a machine per second.
7357 *
7358 * Before version 4.1, we used this formula for saved state files:
7359 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7360 * which no longer works because saved state files can now be shared between the saved state of the
7361 * "saved" machine and an online snapshot, and the following would cause problems:
7362 * 1) save machine
7363 * 2) create online snapshot from that machine state --> reusing saved state file
7364 * 3) save machine again --> filename would be reused, breaking the online snapshot
7365 *
7366 * So instead we now use a timestamp.
7367 *
7368 * @param strStateFilePath
7369 */
7370
7371void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7372{
7373 AutoCaller autoCaller(this);
7374 AssertComRCReturnVoid(autoCaller.rc());
7375
7376 {
7377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7378 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7379 }
7380
7381 RTTIMESPEC ts;
7382 RTTimeNow(&ts);
7383 RTTIME time;
7384 RTTimeExplode(&time, &ts);
7385
7386 strStateFilePath += RTPATH_DELIMITER;
7387 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7388 time.i32Year, time.u8Month, time.u8MonthDay,
7389 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7390}
7391
7392/**
7393 * Returns whether at least one USB controller is present for the VM.
7394 */
7395bool Machine::i_isUSBControllerPresent()
7396{
7397 AutoCaller autoCaller(this);
7398 AssertComRCReturn(autoCaller.rc(), false);
7399
7400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7401
7402 return (mUSBControllers->size() > 0);
7403}
7404
7405/**
7406 * @note Locks this object for writing, calls the client process
7407 * (inside the lock).
7408 */
7409HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7410 const Utf8Str &strFrontend,
7411 const Utf8Str &strEnvironment,
7412 ProgressProxy *aProgress)
7413{
7414 LogFlowThisFuncEnter();
7415
7416 AssertReturn(aControl, E_FAIL);
7417 AssertReturn(aProgress, E_FAIL);
7418 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7419
7420 AutoCaller autoCaller(this);
7421 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7422
7423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7424
7425 if (!mData->mRegistered)
7426 return setError(E_UNEXPECTED,
7427 tr("The machine '%s' is not registered"),
7428 mUserData->s.strName.c_str());
7429
7430 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7431
7432 /* The process started when launching a VM with separate UI/VM processes is always
7433 * the UI process, i.e. needs special handling as it won't claim the session. */
7434 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7435
7436 if (fSeparate)
7437 {
7438 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7439 return setError(VBOX_E_INVALID_OBJECT_STATE,
7440 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7441 mUserData->s.strName.c_str());
7442 }
7443 else
7444 {
7445 if ( mData->mSession.mState == SessionState_Locked
7446 || mData->mSession.mState == SessionState_Spawning
7447 || mData->mSession.mState == SessionState_Unlocking)
7448 return setError(VBOX_E_INVALID_OBJECT_STATE,
7449 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7450 mUserData->s.strName.c_str());
7451
7452 /* may not be busy */
7453 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7454 }
7455
7456 /* get the path to the executable */
7457 char szPath[RTPATH_MAX];
7458 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7459 size_t cchBufLeft = strlen(szPath);
7460 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7461 szPath[cchBufLeft] = 0;
7462 char *pszNamePart = szPath + cchBufLeft;
7463 cchBufLeft = sizeof(szPath) - cchBufLeft;
7464
7465 int vrc = VINF_SUCCESS;
7466 RTPROCESS pid = NIL_RTPROCESS;
7467
7468 RTENV env = RTENV_DEFAULT;
7469
7470 if (!strEnvironment.isEmpty())
7471 {
7472 char *newEnvStr = NULL;
7473
7474 do
7475 {
7476 /* clone the current environment */
7477 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7478 AssertRCBreakStmt(vrc2, vrc = vrc2);
7479
7480 newEnvStr = RTStrDup(strEnvironment.c_str());
7481 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7482
7483 /* put new variables to the environment
7484 * (ignore empty variable names here since RTEnv API
7485 * intentionally doesn't do that) */
7486 char *var = newEnvStr;
7487 for (char *p = newEnvStr; *p; ++p)
7488 {
7489 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7490 {
7491 *p = '\0';
7492 if (*var)
7493 {
7494 char *val = strchr(var, '=');
7495 if (val)
7496 {
7497 *val++ = '\0';
7498 vrc2 = RTEnvSetEx(env, var, val);
7499 }
7500 else
7501 vrc2 = RTEnvUnsetEx(env, var);
7502 if (RT_FAILURE(vrc2))
7503 break;
7504 }
7505 var = p + 1;
7506 }
7507 }
7508 if (RT_SUCCESS(vrc2) && *var)
7509 vrc2 = RTEnvPutEx(env, var);
7510
7511 AssertRCBreakStmt(vrc2, vrc = vrc2);
7512 }
7513 while (0);
7514
7515 if (newEnvStr != NULL)
7516 RTStrFree(newEnvStr);
7517 }
7518
7519 /* Hardening logging */
7520#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7521 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7522 {
7523 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7524 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7525 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7526 {
7527 Utf8Str strStartupLogDir = strHardeningLogFile;
7528 strStartupLogDir.stripFilename();
7529 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7530 file without stripping the file. */
7531 }
7532 strSupHardeningLogArg.append(strHardeningLogFile);
7533
7534 /* Remove legacy log filename to avoid confusion. */
7535 Utf8Str strOldStartupLogFile;
7536 getLogFolder(strOldStartupLogFile);
7537 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7538 RTFileDelete(strOldStartupLogFile.c_str());
7539 }
7540 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7541#else
7542 const char *pszSupHardeningLogArg = NULL;
7543#endif
7544
7545 Utf8Str strCanonicalName;
7546
7547#ifdef VBOX_WITH_QTGUI
7548 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7549 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7550 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7551 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7552 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7553 {
7554 strCanonicalName = "GUI/Qt";
7555# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7556 /* Modify the base path so that we don't need to use ".." below. */
7557 RTPathStripTrailingSlash(szPath);
7558 RTPathStripFilename(szPath);
7559 cchBufLeft = strlen(szPath);
7560 pszNamePart = szPath + cchBufLeft;
7561 cchBufLeft = sizeof(szPath) - cchBufLeft;
7562
7563# define OSX_APP_NAME "VirtualBoxVM"
7564# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7565
7566 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7567 if ( strAppOverride.contains(".")
7568 || strAppOverride.contains("/")
7569 || strAppOverride.contains("\\")
7570 || strAppOverride.contains(":"))
7571 strAppOverride.setNull();
7572 Utf8Str strAppPath;
7573 if (!strAppOverride.isEmpty())
7574 {
7575 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7576 Utf8Str strFullPath(szPath);
7577 strFullPath.append(strAppPath);
7578 /* there is a race, but people using this deserve the failure */
7579 if (!RTFileExists(strFullPath.c_str()))
7580 strAppOverride.setNull();
7581 }
7582 if (strAppOverride.isEmpty())
7583 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7584 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7585 strcpy(pszNamePart, strAppPath.c_str());
7586# else
7587# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7588 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7589# else
7590 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7591# endif
7592 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7593 strcpy(pszNamePart, s_szVirtualBox_exe);
7594# endif
7595
7596 Utf8Str idStr = mData->mUuid.toString();
7597 const char *apszArgs[] =
7598 {
7599 szPath,
7600 "--comment", mUserData->s.strName.c_str(),
7601 "--startvm", idStr.c_str(),
7602 "--no-startvm-errormsgbox",
7603 NULL, /* For "--separate". */
7604 NULL, /* For "--sup-startup-log". */
7605 NULL
7606 };
7607 unsigned iArg = 6;
7608 if (fSeparate)
7609 apszArgs[iArg++] = "--separate";
7610 apszArgs[iArg++] = pszSupHardeningLogArg;
7611
7612 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7613 }
7614#else /* !VBOX_WITH_QTGUI */
7615 if (0)
7616 ;
7617#endif /* VBOX_WITH_QTGUI */
7618
7619 else
7620
7621#ifdef VBOX_WITH_VBOXSDL
7622 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7623 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7624 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7625 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7626 {
7627 strCanonicalName = "GUI/SDL";
7628 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7629 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7630 strcpy(pszNamePart, s_szVBoxSDL_exe);
7631
7632 Utf8Str idStr = mData->mUuid.toString();
7633 const char *apszArgs[] =
7634 {
7635 szPath,
7636 "--comment", mUserData->s.strName.c_str(),
7637 "--startvm", idStr.c_str(),
7638 NULL, /* For "--separate". */
7639 NULL, /* For "--sup-startup-log". */
7640 NULL
7641 };
7642 unsigned iArg = 5;
7643 if (fSeparate)
7644 apszArgs[iArg++] = "--separate";
7645 apszArgs[iArg++] = pszSupHardeningLogArg;
7646
7647 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7648 }
7649#else /* !VBOX_WITH_VBOXSDL */
7650 if (0)
7651 ;
7652#endif /* !VBOX_WITH_VBOXSDL */
7653
7654 else
7655
7656#ifdef VBOX_WITH_HEADLESS
7657 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7658 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7659 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7660 )
7661 {
7662 strCanonicalName = "headless";
7663 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7664 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7665 * and a VM works even if the server has not been installed.
7666 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7667 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7668 * differently in 4.0 and 3.x.
7669 */
7670 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7671 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7672 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7673
7674 Utf8Str idStr = mData->mUuid.toString();
7675 const char *apszArgs[] =
7676 {
7677 szPath,
7678 "--comment", mUserData->s.strName.c_str(),
7679 "--startvm", idStr.c_str(),
7680 "--vrde", "config",
7681 NULL, /* For "--capture". */
7682 NULL, /* For "--sup-startup-log". */
7683 NULL
7684 };
7685 unsigned iArg = 7;
7686 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7687 apszArgs[iArg++] = "--capture";
7688 apszArgs[iArg++] = pszSupHardeningLogArg;
7689
7690# ifdef RT_OS_WINDOWS
7691 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7692# else
7693 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7694# endif
7695 }
7696#else /* !VBOX_WITH_HEADLESS */
7697 if (0)
7698 ;
7699#endif /* !VBOX_WITH_HEADLESS */
7700 else
7701 {
7702 RTEnvDestroy(env);
7703 return setError(E_INVALIDARG,
7704 tr("Invalid frontend name: '%s'"),
7705 strFrontend.c_str());
7706 }
7707
7708 RTEnvDestroy(env);
7709
7710 if (RT_FAILURE(vrc))
7711 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7712 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7713 mUserData->s.strName.c_str(), vrc);
7714
7715 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7716
7717 if (!fSeparate)
7718 {
7719 /*
7720 * Note that we don't release the lock here before calling the client,
7721 * because it doesn't need to call us back if called with a NULL argument.
7722 * Releasing the lock here is dangerous because we didn't prepare the
7723 * launch data yet, but the client we've just started may happen to be
7724 * too fast and call LockMachine() that will fail (because of PID, etc.),
7725 * so that the Machine will never get out of the Spawning session state.
7726 */
7727
7728 /* inform the session that it will be a remote one */
7729 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7730#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7731 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7732#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7733 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7734#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7735 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7736
7737 if (FAILED(rc))
7738 {
7739 /* restore the session state */
7740 mData->mSession.mState = SessionState_Unlocked;
7741 alock.release();
7742 mParent->i_addProcessToReap(pid);
7743 /* The failure may occur w/o any error info (from RPC), so provide one */
7744 return setError(VBOX_E_VM_ERROR,
7745 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7746 }
7747
7748 /* attach launch data to the machine */
7749 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7750 mData->mSession.mRemoteControls.push_back(aControl);
7751 mData->mSession.mProgress = aProgress;
7752 mData->mSession.mPID = pid;
7753 mData->mSession.mState = SessionState_Spawning;
7754 Assert(strCanonicalName.isNotEmpty());
7755 mData->mSession.mName = strCanonicalName;
7756 }
7757 else
7758 {
7759 /* For separate UI process we declare the launch as completed instantly, as the
7760 * actual headless VM start may or may not come. No point in remembering anything
7761 * yet, as what matters for us is when the headless VM gets started. */
7762 aProgress->i_notifyComplete(S_OK);
7763 }
7764
7765 alock.release();
7766 mParent->i_addProcessToReap(pid);
7767
7768 LogFlowThisFuncLeave();
7769 return S_OK;
7770}
7771
7772/**
7773 * Returns @c true if the given session machine instance has an open direct
7774 * session (and optionally also for direct sessions which are closing) and
7775 * returns the session control machine instance if so.
7776 *
7777 * Note that when the method returns @c false, the arguments remain unchanged.
7778 *
7779 * @param aMachine Session machine object.
7780 * @param aControl Direct session control object (optional).
7781 * @param aRequireVM If true then only allow VM sessions.
7782 * @param aAllowClosing If true then additionally a session which is currently
7783 * being closed will also be allowed.
7784 *
7785 * @note locks this object for reading.
7786 */
7787bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7788 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7789 bool aRequireVM /*= false*/,
7790 bool aAllowClosing /*= false*/)
7791{
7792 AutoLimitedCaller autoCaller(this);
7793 AssertComRCReturn(autoCaller.rc(), false);
7794
7795 /* just return false for inaccessible machines */
7796 if (getObjectState().getState() != ObjectState::Ready)
7797 return false;
7798
7799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7800
7801 if ( ( mData->mSession.mState == SessionState_Locked
7802 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7803 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7804 )
7805 {
7806 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7807
7808 aMachine = mData->mSession.mMachine;
7809
7810 if (aControl != NULL)
7811 *aControl = mData->mSession.mDirectControl;
7812
7813 return true;
7814 }
7815
7816 return false;
7817}
7818
7819/**
7820 * Returns @c true if the given machine has an spawning direct session.
7821 *
7822 * @note locks this object for reading.
7823 */
7824bool Machine::i_isSessionSpawning()
7825{
7826 AutoLimitedCaller autoCaller(this);
7827 AssertComRCReturn(autoCaller.rc(), false);
7828
7829 /* just return false for inaccessible machines */
7830 if (getObjectState().getState() != ObjectState::Ready)
7831 return false;
7832
7833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7834
7835 if (mData->mSession.mState == SessionState_Spawning)
7836 return true;
7837
7838 return false;
7839}
7840
7841/**
7842 * Called from the client watcher thread to check for unexpected client process
7843 * death during Session_Spawning state (e.g. before it successfully opened a
7844 * direct session).
7845 *
7846 * On Win32 and on OS/2, this method is called only when we've got the
7847 * direct client's process termination notification, so it always returns @c
7848 * true.
7849 *
7850 * On other platforms, this method returns @c true if the client process is
7851 * terminated and @c false if it's still alive.
7852 *
7853 * @note Locks this object for writing.
7854 */
7855bool Machine::i_checkForSpawnFailure()
7856{
7857 AutoCaller autoCaller(this);
7858 if (!autoCaller.isOk())
7859 {
7860 /* nothing to do */
7861 LogFlowThisFunc(("Already uninitialized!\n"));
7862 return true;
7863 }
7864
7865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7866
7867 if (mData->mSession.mState != SessionState_Spawning)
7868 {
7869 /* nothing to do */
7870 LogFlowThisFunc(("Not spawning any more!\n"));
7871 return true;
7872 }
7873
7874 HRESULT rc = S_OK;
7875
7876 /* PID not yet initialized, skip check. */
7877 if (mData->mSession.mPID == NIL_RTPROCESS)
7878 return false;
7879
7880 RTPROCSTATUS status;
7881 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7882
7883 if (vrc != VERR_PROCESS_RUNNING)
7884 {
7885 Utf8Str strExtraInfo;
7886
7887#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7888 /* If the startup logfile exists and is of non-zero length, tell the
7889 user to look there for more details to encourage them to attach it
7890 when reporting startup issues. */
7891 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7892 uint64_t cbStartupLogFile = 0;
7893 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7894 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7895 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7896#endif
7897
7898 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7899 rc = setError(E_FAIL,
7900 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7901 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7902 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7903 rc = setError(E_FAIL,
7904 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7905 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7906 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7907 rc = setError(E_FAIL,
7908 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7909 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7910 else
7911 rc = setErrorBoth(E_FAIL, vrc,
7912 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7913 i_getName().c_str(), vrc, strExtraInfo.c_str());
7914 }
7915
7916 if (FAILED(rc))
7917 {
7918 /* Close the remote session, remove the remote control from the list
7919 * and reset session state to Closed (@note keep the code in sync with
7920 * the relevant part in LockMachine()). */
7921
7922 Assert(mData->mSession.mRemoteControls.size() == 1);
7923 if (mData->mSession.mRemoteControls.size() == 1)
7924 {
7925 ErrorInfoKeeper eik;
7926 mData->mSession.mRemoteControls.front()->Uninitialize();
7927 }
7928
7929 mData->mSession.mRemoteControls.clear();
7930 mData->mSession.mState = SessionState_Unlocked;
7931
7932 /* finalize the progress after setting the state */
7933 if (!mData->mSession.mProgress.isNull())
7934 {
7935 mData->mSession.mProgress->notifyComplete(rc);
7936 mData->mSession.mProgress.setNull();
7937 }
7938
7939 mData->mSession.mPID = NIL_RTPROCESS;
7940
7941 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7942 return true;
7943 }
7944
7945 return false;
7946}
7947
7948/**
7949 * Checks whether the machine can be registered. If so, commits and saves
7950 * all settings.
7951 *
7952 * @note Must be called from mParent's write lock. Locks this object and
7953 * children for writing.
7954 */
7955HRESULT Machine::i_prepareRegister()
7956{
7957 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7958
7959 AutoLimitedCaller autoCaller(this);
7960 AssertComRCReturnRC(autoCaller.rc());
7961
7962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7963
7964 /* wait for state dependents to drop to zero */
7965 i_ensureNoStateDependencies();
7966
7967 if (!mData->mAccessible)
7968 return setError(VBOX_E_INVALID_OBJECT_STATE,
7969 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7970 mUserData->s.strName.c_str(),
7971 mData->mUuid.toString().c_str());
7972
7973 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7974
7975 if (mData->mRegistered)
7976 return setError(VBOX_E_INVALID_OBJECT_STATE,
7977 tr("The machine '%s' with UUID {%s} is already registered"),
7978 mUserData->s.strName.c_str(),
7979 mData->mUuid.toString().c_str());
7980
7981 HRESULT rc = S_OK;
7982
7983 // Ensure the settings are saved. If we are going to be registered and
7984 // no config file exists yet, create it by calling i_saveSettings() too.
7985 if ( (mData->flModifications)
7986 || (!mData->pMachineConfigFile->fileExists())
7987 )
7988 {
7989 rc = i_saveSettings(NULL);
7990 // no need to check whether VirtualBox.xml needs saving too since
7991 // we can't have a machine XML file rename pending
7992 if (FAILED(rc)) return rc;
7993 }
7994
7995 /* more config checking goes here */
7996
7997 if (SUCCEEDED(rc))
7998 {
7999 /* we may have had implicit modifications we want to fix on success */
8000 i_commit();
8001
8002 mData->mRegistered = true;
8003 }
8004 else
8005 {
8006 /* we may have had implicit modifications we want to cancel on failure*/
8007 i_rollback(false /* aNotify */);
8008 }
8009
8010 return rc;
8011}
8012
8013/**
8014 * Increases the number of objects dependent on the machine state or on the
8015 * registered state. Guarantees that these two states will not change at least
8016 * until #i_releaseStateDependency() is called.
8017 *
8018 * Depending on the @a aDepType value, additional state checks may be made.
8019 * These checks will set extended error info on failure. See
8020 * #i_checkStateDependency() for more info.
8021 *
8022 * If this method returns a failure, the dependency is not added and the caller
8023 * is not allowed to rely on any particular machine state or registration state
8024 * value and may return the failed result code to the upper level.
8025 *
8026 * @param aDepType Dependency type to add.
8027 * @param aState Current machine state (NULL if not interested).
8028 * @param aRegistered Current registered state (NULL if not interested).
8029 *
8030 * @note Locks this object for writing.
8031 */
8032HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8033 MachineState_T *aState /* = NULL */,
8034 BOOL *aRegistered /* = NULL */)
8035{
8036 AutoCaller autoCaller(this);
8037 AssertComRCReturnRC(autoCaller.rc());
8038
8039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8040
8041 HRESULT rc = i_checkStateDependency(aDepType);
8042 if (FAILED(rc)) return rc;
8043
8044 {
8045 if (mData->mMachineStateChangePending != 0)
8046 {
8047 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8048 * drop to zero so don't add more. It may make sense to wait a bit
8049 * and retry before reporting an error (since the pending state
8050 * transition should be really quick) but let's just assert for
8051 * now to see if it ever happens on practice. */
8052
8053 AssertFailed();
8054
8055 return setError(E_ACCESSDENIED,
8056 tr("Machine state change is in progress. Please retry the operation later."));
8057 }
8058
8059 ++mData->mMachineStateDeps;
8060 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8061 }
8062
8063 if (aState)
8064 *aState = mData->mMachineState;
8065 if (aRegistered)
8066 *aRegistered = mData->mRegistered;
8067
8068 return S_OK;
8069}
8070
8071/**
8072 * Decreases the number of objects dependent on the machine state.
8073 * Must always complete the #i_addStateDependency() call after the state
8074 * dependency is no more necessary.
8075 */
8076void Machine::i_releaseStateDependency()
8077{
8078 AutoCaller autoCaller(this);
8079 AssertComRCReturnVoid(autoCaller.rc());
8080
8081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8082
8083 /* releaseStateDependency() w/o addStateDependency()? */
8084 AssertReturnVoid(mData->mMachineStateDeps != 0);
8085 -- mData->mMachineStateDeps;
8086
8087 if (mData->mMachineStateDeps == 0)
8088 {
8089 /* inform i_ensureNoStateDependencies() that there are no more deps */
8090 if (mData->mMachineStateChangePending != 0)
8091 {
8092 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8093 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8094 }
8095 }
8096}
8097
8098Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8099{
8100 /* start with nothing found */
8101 Utf8Str strResult("");
8102
8103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8104
8105 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8106 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8107 // found:
8108 strResult = it->second; // source is a Utf8Str
8109
8110 return strResult;
8111}
8112
8113// protected methods
8114/////////////////////////////////////////////////////////////////////////////
8115
8116/**
8117 * Performs machine state checks based on the @a aDepType value. If a check
8118 * fails, this method will set extended error info, otherwise it will return
8119 * S_OK. It is supposed, that on failure, the caller will immediately return
8120 * the return value of this method to the upper level.
8121 *
8122 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8123 *
8124 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8125 * current state of this machine object allows to change settings of the
8126 * machine (i.e. the machine is not registered, or registered but not running
8127 * and not saved). It is useful to call this method from Machine setters
8128 * before performing any change.
8129 *
8130 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8131 * as for MutableStateDep except that if the machine is saved, S_OK is also
8132 * returned. This is useful in setters which allow changing machine
8133 * properties when it is in the saved state.
8134 *
8135 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8136 * if the current state of this machine object allows to change runtime
8137 * changeable settings of the machine (i.e. the machine is not registered, or
8138 * registered but either running or not running and not saved). It is useful
8139 * to call this method from Machine setters before performing any changes to
8140 * runtime changeable settings.
8141 *
8142 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8143 * the same as for MutableOrRunningStateDep except that if the machine is
8144 * saved, S_OK is also returned. This is useful in setters which allow
8145 * changing runtime and saved state changeable machine properties.
8146 *
8147 * @param aDepType Dependency type to check.
8148 *
8149 * @note Non Machine based classes should use #i_addStateDependency() and
8150 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8151 * template.
8152 *
8153 * @note This method must be called from under this object's read or write
8154 * lock.
8155 */
8156HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8157{
8158 switch (aDepType)
8159 {
8160 case AnyStateDep:
8161 {
8162 break;
8163 }
8164 case MutableStateDep:
8165 {
8166 if ( mData->mRegistered
8167 && ( !i_isSessionMachine()
8168 || ( mData->mMachineState != MachineState_Aborted
8169 && mData->mMachineState != MachineState_Teleported
8170 && mData->mMachineState != MachineState_PoweredOff
8171 )
8172 )
8173 )
8174 return setError(VBOX_E_INVALID_VM_STATE,
8175 tr("The machine is not mutable (state is %s)"),
8176 Global::stringifyMachineState(mData->mMachineState));
8177 break;
8178 }
8179 case MutableOrSavedStateDep:
8180 {
8181 if ( mData->mRegistered
8182 && ( !i_isSessionMachine()
8183 || ( mData->mMachineState != MachineState_Aborted
8184 && mData->mMachineState != MachineState_Teleported
8185 && mData->mMachineState != MachineState_Saved
8186 && mData->mMachineState != MachineState_PoweredOff
8187 )
8188 )
8189 )
8190 return setError(VBOX_E_INVALID_VM_STATE,
8191 tr("The machine is not mutable or saved (state is %s)"),
8192 Global::stringifyMachineState(mData->mMachineState));
8193 break;
8194 }
8195 case MutableOrRunningStateDep:
8196 {
8197 if ( mData->mRegistered
8198 && ( !i_isSessionMachine()
8199 || ( mData->mMachineState != MachineState_Aborted
8200 && mData->mMachineState != MachineState_Teleported
8201 && mData->mMachineState != MachineState_PoweredOff
8202 && !Global::IsOnline(mData->mMachineState)
8203 )
8204 )
8205 )
8206 return setError(VBOX_E_INVALID_VM_STATE,
8207 tr("The machine is not mutable or running (state is %s)"),
8208 Global::stringifyMachineState(mData->mMachineState));
8209 break;
8210 }
8211 case MutableOrSavedOrRunningStateDep:
8212 {
8213 if ( mData->mRegistered
8214 && ( !i_isSessionMachine()
8215 || ( mData->mMachineState != MachineState_Aborted
8216 && mData->mMachineState != MachineState_Teleported
8217 && mData->mMachineState != MachineState_Saved
8218 && mData->mMachineState != MachineState_PoweredOff
8219 && !Global::IsOnline(mData->mMachineState)
8220 )
8221 )
8222 )
8223 return setError(VBOX_E_INVALID_VM_STATE,
8224 tr("The machine is not mutable, saved or running (state is %s)"),
8225 Global::stringifyMachineState(mData->mMachineState));
8226 break;
8227 }
8228 }
8229
8230 return S_OK;
8231}
8232
8233/**
8234 * Helper to initialize all associated child objects and allocate data
8235 * structures.
8236 *
8237 * This method must be called as a part of the object's initialization procedure
8238 * (usually done in the #init() method).
8239 *
8240 * @note Must be called only from #init() or from #i_registeredInit().
8241 */
8242HRESULT Machine::initDataAndChildObjects()
8243{
8244 AutoCaller autoCaller(this);
8245 AssertComRCReturnRC(autoCaller.rc());
8246 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8247 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8248
8249 AssertReturn(!mData->mAccessible, E_FAIL);
8250
8251 /* allocate data structures */
8252 mSSData.allocate();
8253 mUserData.allocate();
8254 mHWData.allocate();
8255 mMediumAttachments.allocate();
8256 mStorageControllers.allocate();
8257 mUSBControllers.allocate();
8258
8259 /* initialize mOSTypeId */
8260 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8261
8262/** @todo r=bird: init() methods never fails, right? Why don't we make them
8263 * return void then! */
8264
8265 /* create associated BIOS settings object */
8266 unconst(mBIOSSettings).createObject();
8267 mBIOSSettings->init(this);
8268
8269 /* create associated record settings object */
8270 unconst(mRecordingSettings).createObject();
8271 mRecordingSettings->init(this);
8272
8273 /* create an associated VRDE object (default is disabled) */
8274 unconst(mVRDEServer).createObject();
8275 mVRDEServer->init(this);
8276
8277 /* create associated serial port objects */
8278 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8279 {
8280 unconst(mSerialPorts[slot]).createObject();
8281 mSerialPorts[slot]->init(this, slot);
8282 }
8283
8284 /* create associated parallel port objects */
8285 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8286 {
8287 unconst(mParallelPorts[slot]).createObject();
8288 mParallelPorts[slot]->init(this, slot);
8289 }
8290
8291 /* create the audio adapter object (always present, default is disabled) */
8292 unconst(mAudioAdapter).createObject();
8293 mAudioAdapter->init(this);
8294
8295 /* create the USB device filters object (always present) */
8296 unconst(mUSBDeviceFilters).createObject();
8297 mUSBDeviceFilters->init(this);
8298
8299 /* create associated network adapter objects */
8300 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8301 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8302 {
8303 unconst(mNetworkAdapters[slot]).createObject();
8304 mNetworkAdapters[slot]->init(this, slot);
8305 }
8306
8307 /* create the bandwidth control */
8308 unconst(mBandwidthControl).createObject();
8309 mBandwidthControl->init(this);
8310
8311 return S_OK;
8312}
8313
8314/**
8315 * Helper to uninitialize all associated child objects and to free all data
8316 * structures.
8317 *
8318 * This method must be called as a part of the object's uninitialization
8319 * procedure (usually done in the #uninit() method).
8320 *
8321 * @note Must be called only from #uninit() or from #i_registeredInit().
8322 */
8323void Machine::uninitDataAndChildObjects()
8324{
8325 AutoCaller autoCaller(this);
8326 AssertComRCReturnVoid(autoCaller.rc());
8327 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8328 || getObjectState().getState() == ObjectState::Limited);
8329
8330 /* tell all our other child objects we've been uninitialized */
8331 if (mBandwidthControl)
8332 {
8333 mBandwidthControl->uninit();
8334 unconst(mBandwidthControl).setNull();
8335 }
8336
8337 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8338 {
8339 if (mNetworkAdapters[slot])
8340 {
8341 mNetworkAdapters[slot]->uninit();
8342 unconst(mNetworkAdapters[slot]).setNull();
8343 }
8344 }
8345
8346 if (mUSBDeviceFilters)
8347 {
8348 mUSBDeviceFilters->uninit();
8349 unconst(mUSBDeviceFilters).setNull();
8350 }
8351
8352 if (mAudioAdapter)
8353 {
8354 mAudioAdapter->uninit();
8355 unconst(mAudioAdapter).setNull();
8356 }
8357
8358 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8359 {
8360 if (mParallelPorts[slot])
8361 {
8362 mParallelPorts[slot]->uninit();
8363 unconst(mParallelPorts[slot]).setNull();
8364 }
8365 }
8366
8367 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8368 {
8369 if (mSerialPorts[slot])
8370 {
8371 mSerialPorts[slot]->uninit();
8372 unconst(mSerialPorts[slot]).setNull();
8373 }
8374 }
8375
8376 if (mVRDEServer)
8377 {
8378 mVRDEServer->uninit();
8379 unconst(mVRDEServer).setNull();
8380 }
8381
8382 if (mBIOSSettings)
8383 {
8384 mBIOSSettings->uninit();
8385 unconst(mBIOSSettings).setNull();
8386 }
8387
8388 if (mRecordingSettings)
8389 {
8390 mRecordingSettings->uninit();
8391 unconst(mRecordingSettings).setNull();
8392 }
8393
8394 /* Deassociate media (only when a real Machine or a SnapshotMachine
8395 * instance is uninitialized; SessionMachine instances refer to real
8396 * Machine media). This is necessary for a clean re-initialization of
8397 * the VM after successfully re-checking the accessibility state. Note
8398 * that in case of normal Machine or SnapshotMachine uninitialization (as
8399 * a result of unregistering or deleting the snapshot), outdated media
8400 * attachments will already be uninitialized and deleted, so this
8401 * code will not affect them. */
8402 if ( !mMediumAttachments.isNull()
8403 && !i_isSessionMachine()
8404 )
8405 {
8406 for (MediumAttachmentList::const_iterator
8407 it = mMediumAttachments->begin();
8408 it != mMediumAttachments->end();
8409 ++it)
8410 {
8411 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8412 if (pMedium.isNull())
8413 continue;
8414 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8415 AssertComRC(rc);
8416 }
8417 }
8418
8419 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8420 {
8421 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8422 if (mData->mFirstSnapshot)
8423 {
8424 // snapshots tree is protected by machine write lock; strictly
8425 // this isn't necessary here since we're deleting the entire
8426 // machine, but otherwise we assert in Snapshot::uninit()
8427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8428 mData->mFirstSnapshot->uninit();
8429 mData->mFirstSnapshot.setNull();
8430 }
8431
8432 mData->mCurrentSnapshot.setNull();
8433 }
8434
8435 /* free data structures (the essential mData structure is not freed here
8436 * since it may be still in use) */
8437 mMediumAttachments.free();
8438 mStorageControllers.free();
8439 mUSBControllers.free();
8440 mHWData.free();
8441 mUserData.free();
8442 mSSData.free();
8443}
8444
8445/**
8446 * Returns a pointer to the Machine object for this machine that acts like a
8447 * parent for complex machine data objects such as shared folders, etc.
8448 *
8449 * For primary Machine objects and for SnapshotMachine objects, returns this
8450 * object's pointer itself. For SessionMachine objects, returns the peer
8451 * (primary) machine pointer.
8452 */
8453Machine *Machine::i_getMachine()
8454{
8455 if (i_isSessionMachine())
8456 return (Machine*)mPeer;
8457 return this;
8458}
8459
8460/**
8461 * Makes sure that there are no machine state dependents. If necessary, waits
8462 * for the number of dependents to drop to zero.
8463 *
8464 * Make sure this method is called from under this object's write lock to
8465 * guarantee that no new dependents may be added when this method returns
8466 * control to the caller.
8467 *
8468 * @note Locks this object for writing. The lock will be released while waiting
8469 * (if necessary).
8470 *
8471 * @warning To be used only in methods that change the machine state!
8472 */
8473void Machine::i_ensureNoStateDependencies()
8474{
8475 AssertReturnVoid(isWriteLockOnCurrentThread());
8476
8477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8478
8479 /* Wait for all state dependents if necessary */
8480 if (mData->mMachineStateDeps != 0)
8481 {
8482 /* lazy semaphore creation */
8483 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8484 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8485
8486 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8487 mData->mMachineStateDeps));
8488
8489 ++mData->mMachineStateChangePending;
8490
8491 /* reset the semaphore before waiting, the last dependent will signal
8492 * it */
8493 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8494
8495 alock.release();
8496
8497 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8498
8499 alock.acquire();
8500
8501 -- mData->mMachineStateChangePending;
8502 }
8503}
8504
8505/**
8506 * Changes the machine state and informs callbacks.
8507 *
8508 * This method is not intended to fail so it either returns S_OK or asserts (and
8509 * returns a failure).
8510 *
8511 * @note Locks this object for writing.
8512 */
8513HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8514{
8515 LogFlowThisFuncEnter();
8516 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8517 Assert(aMachineState != MachineState_Null);
8518
8519 AutoCaller autoCaller(this);
8520 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8521
8522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8523
8524 /* wait for state dependents to drop to zero */
8525 i_ensureNoStateDependencies();
8526
8527 MachineState_T const enmOldState = mData->mMachineState;
8528 if (enmOldState != aMachineState)
8529 {
8530 mData->mMachineState = aMachineState;
8531 RTTimeNow(&mData->mLastStateChange);
8532
8533#ifdef VBOX_WITH_DTRACE_R3_MAIN
8534 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8535#endif
8536 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8537 }
8538
8539 LogFlowThisFuncLeave();
8540 return S_OK;
8541}
8542
8543/**
8544 * Searches for a shared folder with the given logical name
8545 * in the collection of shared folders.
8546 *
8547 * @param aName logical name of the shared folder
8548 * @param aSharedFolder where to return the found object
8549 * @param aSetError whether to set the error info if the folder is
8550 * not found
8551 * @return
8552 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8553 *
8554 * @note
8555 * must be called from under the object's lock!
8556 */
8557HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8558 ComObjPtr<SharedFolder> &aSharedFolder,
8559 bool aSetError /* = false */)
8560{
8561 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8562 for (HWData::SharedFolderList::const_iterator
8563 it = mHWData->mSharedFolders.begin();
8564 it != mHWData->mSharedFolders.end();
8565 ++it)
8566 {
8567 SharedFolder *pSF = *it;
8568 AutoCaller autoCaller(pSF);
8569 if (pSF->i_getName() == aName)
8570 {
8571 aSharedFolder = pSF;
8572 rc = S_OK;
8573 break;
8574 }
8575 }
8576
8577 if (aSetError && FAILED(rc))
8578 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8579
8580 return rc;
8581}
8582
8583/**
8584 * Initializes all machine instance data from the given settings structures
8585 * from XML. The exception is the machine UUID which needs special handling
8586 * depending on the caller's use case, so the caller needs to set that herself.
8587 *
8588 * This gets called in several contexts during machine initialization:
8589 *
8590 * -- When machine XML exists on disk already and needs to be loaded into memory,
8591 * for example, from #i_registeredInit() to load all registered machines on
8592 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8593 * attached to the machine should be part of some media registry already.
8594 *
8595 * -- During OVF import, when a machine config has been constructed from an
8596 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8597 * ensure that the media listed as attachments in the config (which have
8598 * been imported from the OVF) receive the correct registry ID.
8599 *
8600 * -- During VM cloning.
8601 *
8602 * @param config Machine settings from XML.
8603 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8604 * for each attached medium in the config.
8605 * @return
8606 */
8607HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8608 const Guid *puuidRegistry)
8609{
8610 // copy name, description, OS type, teleporter, UTC etc.
8611 mUserData->s = config.machineUserData;
8612
8613 // look up the object by Id to check it is valid
8614 ComObjPtr<GuestOSType> pGuestOSType;
8615 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8616 if (!pGuestOSType.isNull())
8617 mUserData->s.strOsType = pGuestOSType->i_id();
8618
8619 // stateFile (optional)
8620 if (config.strStateFile.isEmpty())
8621 mSSData->strStateFilePath.setNull();
8622 else
8623 {
8624 Utf8Str stateFilePathFull(config.strStateFile);
8625 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8626 if (RT_FAILURE(vrc))
8627 return setErrorBoth(E_FAIL, vrc,
8628 tr("Invalid saved state file path '%s' (%Rrc)"),
8629 config.strStateFile.c_str(),
8630 vrc);
8631 mSSData->strStateFilePath = stateFilePathFull;
8632 }
8633
8634 // snapshot folder needs special processing so set it again
8635 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8636 if (FAILED(rc)) return rc;
8637
8638 /* Copy the extra data items (config may or may not be the same as
8639 * mData->pMachineConfigFile) if necessary. When loading the XML files
8640 * from disk they are the same, but not for OVF import. */
8641 if (mData->pMachineConfigFile != &config)
8642 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8643
8644 /* currentStateModified (optional, default is true) */
8645 mData->mCurrentStateModified = config.fCurrentStateModified;
8646
8647 mData->mLastStateChange = config.timeLastStateChange;
8648
8649 /*
8650 * note: all mUserData members must be assigned prior this point because
8651 * we need to commit changes in order to let mUserData be shared by all
8652 * snapshot machine instances.
8653 */
8654 mUserData.commitCopy();
8655
8656 // machine registry, if present (must be loaded before snapshots)
8657 if (config.canHaveOwnMediaRegistry())
8658 {
8659 // determine machine folder
8660 Utf8Str strMachineFolder = i_getSettingsFileFull();
8661 strMachineFolder.stripFilename();
8662 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8663 config.mediaRegistry,
8664 strMachineFolder);
8665 if (FAILED(rc)) return rc;
8666 }
8667
8668 /* Snapshot node (optional) */
8669 size_t cRootSnapshots;
8670 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8671 {
8672 // there must be only one root snapshot
8673 Assert(cRootSnapshots == 1);
8674
8675 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8676
8677 rc = i_loadSnapshot(snap,
8678 config.uuidCurrentSnapshot,
8679 NULL); // no parent == first snapshot
8680 if (FAILED(rc)) return rc;
8681 }
8682
8683 // hardware data
8684 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8685 if (FAILED(rc)) return rc;
8686
8687 /*
8688 * NOTE: the assignment below must be the last thing to do,
8689 * otherwise it will be not possible to change the settings
8690 * somewhere in the code above because all setters will be
8691 * blocked by i_checkStateDependency(MutableStateDep).
8692 */
8693
8694 /* set the machine state to Aborted or Saved when appropriate */
8695 if (config.fAborted)
8696 {
8697 mSSData->strStateFilePath.setNull();
8698
8699 /* no need to use i_setMachineState() during init() */
8700 mData->mMachineState = MachineState_Aborted;
8701 }
8702 else if (!mSSData->strStateFilePath.isEmpty())
8703 {
8704 /* no need to use i_setMachineState() during init() */
8705 mData->mMachineState = MachineState_Saved;
8706 }
8707
8708 // after loading settings, we are no longer different from the XML on disk
8709 mData->flModifications = 0;
8710
8711 return S_OK;
8712}
8713
8714/**
8715 * Recursively loads all snapshots starting from the given.
8716 *
8717 * @param data snapshot settings.
8718 * @param aCurSnapshotId Current snapshot ID from the settings file.
8719 * @param aParentSnapshot Parent snapshot.
8720 */
8721HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8722 const Guid &aCurSnapshotId,
8723 Snapshot *aParentSnapshot)
8724{
8725 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8726 AssertReturn(!i_isSessionMachine(), E_FAIL);
8727
8728 HRESULT rc = S_OK;
8729
8730 Utf8Str strStateFile;
8731 if (!data.strStateFile.isEmpty())
8732 {
8733 /* optional */
8734 strStateFile = data.strStateFile;
8735 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8736 if (RT_FAILURE(vrc))
8737 return setErrorBoth(E_FAIL, vrc,
8738 tr("Invalid saved state file path '%s' (%Rrc)"),
8739 strStateFile.c_str(),
8740 vrc);
8741 }
8742
8743 /* create a snapshot machine object */
8744 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8745 pSnapshotMachine.createObject();
8746 rc = pSnapshotMachine->initFromSettings(this,
8747 data.hardware,
8748 &data.debugging,
8749 &data.autostart,
8750 data.uuid.ref(),
8751 strStateFile);
8752 if (FAILED(rc)) return rc;
8753
8754 /* create a snapshot object */
8755 ComObjPtr<Snapshot> pSnapshot;
8756 pSnapshot.createObject();
8757 /* initialize the snapshot */
8758 rc = pSnapshot->init(mParent, // VirtualBox object
8759 data.uuid,
8760 data.strName,
8761 data.strDescription,
8762 data.timestamp,
8763 pSnapshotMachine,
8764 aParentSnapshot);
8765 if (FAILED(rc)) return rc;
8766
8767 /* memorize the first snapshot if necessary */
8768 if (!mData->mFirstSnapshot)
8769 mData->mFirstSnapshot = pSnapshot;
8770
8771 /* memorize the current snapshot when appropriate */
8772 if ( !mData->mCurrentSnapshot
8773 && pSnapshot->i_getId() == aCurSnapshotId
8774 )
8775 mData->mCurrentSnapshot = pSnapshot;
8776
8777 // now create the children
8778 for (settings::SnapshotsList::const_iterator
8779 it = data.llChildSnapshots.begin();
8780 it != data.llChildSnapshots.end();
8781 ++it)
8782 {
8783 const settings::Snapshot &childData = *it;
8784 // recurse
8785 rc = i_loadSnapshot(childData,
8786 aCurSnapshotId,
8787 pSnapshot); // parent = the one we created above
8788 if (FAILED(rc)) return rc;
8789 }
8790
8791 return rc;
8792}
8793
8794/**
8795 * Loads settings into mHWData.
8796 *
8797 * @param puuidRegistry Registry ID.
8798 * @param puuidSnapshot Snapshot ID
8799 * @param data Reference to the hardware settings.
8800 * @param pDbg Pointer to the debugging settings.
8801 * @param pAutostart Pointer to the autostart settings.
8802 */
8803HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8804 const Guid *puuidSnapshot,
8805 const settings::Hardware &data,
8806 const settings::Debugging *pDbg,
8807 const settings::Autostart *pAutostart)
8808{
8809 AssertReturn(!i_isSessionMachine(), E_FAIL);
8810
8811 HRESULT rc = S_OK;
8812
8813 try
8814 {
8815 ComObjPtr<GuestOSType> pGuestOSType;
8816 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8817
8818 /* The hardware version attribute (optional). */
8819 mHWData->mHWVersion = data.strVersion;
8820 mHWData->mHardwareUUID = data.uuid;
8821
8822 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8823 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8824 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8825 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8826 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8827 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8828 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8829 mHWData->mPAEEnabled = data.fPAE;
8830 mHWData->mLongMode = data.enmLongMode;
8831 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8832 mHWData->mAPIC = data.fAPIC;
8833 mHWData->mX2APIC = data.fX2APIC;
8834 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8835 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8836 mHWData->mSpecCtrl = data.fSpecCtrl;
8837 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8838 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8839 mHWData->mCPUCount = data.cCPUs;
8840 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8841 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8842 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8843 mHWData->mCpuProfile = data.strCpuProfile;
8844
8845 // cpu
8846 if (mHWData->mCPUHotPlugEnabled)
8847 {
8848 for (settings::CpuList::const_iterator
8849 it = data.llCpus.begin();
8850 it != data.llCpus.end();
8851 ++it)
8852 {
8853 const settings::Cpu &cpu = *it;
8854
8855 mHWData->mCPUAttached[cpu.ulId] = true;
8856 }
8857 }
8858
8859 // cpuid leafs
8860 for (settings::CpuIdLeafsList::const_iterator
8861 it = data.llCpuIdLeafs.begin();
8862 it != data.llCpuIdLeafs.end();
8863 ++it)
8864 {
8865 const settings::CpuIdLeaf &rLeaf= *it;
8866 if ( rLeaf.idx < UINT32_C(0x20)
8867 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8868 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8869 mHWData->mCpuIdLeafList.push_back(rLeaf);
8870 /* else: just ignore */
8871 }
8872
8873 mHWData->mMemorySize = data.ulMemorySizeMB;
8874 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8875
8876 // boot order
8877 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8878 {
8879 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8880 if (it == data.mapBootOrder.end())
8881 mHWData->mBootOrder[i] = DeviceType_Null;
8882 else
8883 mHWData->mBootOrder[i] = it->second;
8884 }
8885
8886 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8887 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8888 mHWData->mMonitorCount = data.cMonitors;
8889 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8890 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8891 mHWData->mFirmwareType = data.firmwareType;
8892 mHWData->mPointingHIDType = data.pointingHIDType;
8893 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8894 mHWData->mChipsetType = data.chipsetType;
8895 mHWData->mParavirtProvider = data.paravirtProvider;
8896 mHWData->mParavirtDebug = data.strParavirtDebug;
8897 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8898 mHWData->mHPETEnabled = data.fHPETEnabled;
8899
8900 /* VRDEServer */
8901 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8902 if (FAILED(rc)) return rc;
8903
8904 /* BIOS */
8905 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8906 if (FAILED(rc)) return rc;
8907
8908 /* Recording settings */
8909 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8910 if (FAILED(rc)) return rc;
8911
8912 // Bandwidth control (must come before network adapters)
8913 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8914 if (FAILED(rc)) return rc;
8915
8916 /* USB controllers */
8917 for (settings::USBControllerList::const_iterator
8918 it = data.usbSettings.llUSBControllers.begin();
8919 it != data.usbSettings.llUSBControllers.end();
8920 ++it)
8921 {
8922 const settings::USBController &settingsCtrl = *it;
8923 ComObjPtr<USBController> newCtrl;
8924
8925 newCtrl.createObject();
8926 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8927 mUSBControllers->push_back(newCtrl);
8928 }
8929
8930 /* USB device filters */
8931 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8932 if (FAILED(rc)) return rc;
8933
8934 // network adapters (establish array size first and apply defaults, to
8935 // ensure reading the same settings as we saved, since the list skips
8936 // adapters having defaults)
8937 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8938 size_t oldCount = mNetworkAdapters.size();
8939 if (newCount > oldCount)
8940 {
8941 mNetworkAdapters.resize(newCount);
8942 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8943 {
8944 unconst(mNetworkAdapters[slot]).createObject();
8945 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8946 }
8947 }
8948 else if (newCount < oldCount)
8949 mNetworkAdapters.resize(newCount);
8950 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8951 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8952 for (settings::NetworkAdaptersList::const_iterator
8953 it = data.llNetworkAdapters.begin();
8954 it != data.llNetworkAdapters.end();
8955 ++it)
8956 {
8957 const settings::NetworkAdapter &nic = *it;
8958
8959 /* slot uniqueness is guaranteed by XML Schema */
8960 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8961 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8962 if (FAILED(rc)) return rc;
8963 }
8964
8965 // serial ports (establish defaults first, to ensure reading the same
8966 // settings as we saved, since the list skips ports having defaults)
8967 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8968 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8969 for (settings::SerialPortsList::const_iterator
8970 it = data.llSerialPorts.begin();
8971 it != data.llSerialPorts.end();
8972 ++it)
8973 {
8974 const settings::SerialPort &s = *it;
8975
8976 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8977 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8978 if (FAILED(rc)) return rc;
8979 }
8980
8981 // parallel ports (establish defaults first, to ensure reading the same
8982 // settings as we saved, since the list skips ports having defaults)
8983 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8984 mParallelPorts[i]->i_applyDefaults();
8985 for (settings::ParallelPortsList::const_iterator
8986 it = data.llParallelPorts.begin();
8987 it != data.llParallelPorts.end();
8988 ++it)
8989 {
8990 const settings::ParallelPort &p = *it;
8991
8992 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8993 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8994 if (FAILED(rc)) return rc;
8995 }
8996
8997 /* AudioAdapter */
8998 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8999 if (FAILED(rc)) return rc;
9000
9001 /* storage controllers */
9002 rc = i_loadStorageControllers(data.storage,
9003 puuidRegistry,
9004 puuidSnapshot);
9005 if (FAILED(rc)) return rc;
9006
9007 /* Shared folders */
9008 for (settings::SharedFoldersList::const_iterator
9009 it = data.llSharedFolders.begin();
9010 it != data.llSharedFolders.end();
9011 ++it)
9012 {
9013 const settings::SharedFolder &sf = *it;
9014
9015 ComObjPtr<SharedFolder> sharedFolder;
9016 /* Check for double entries. Not allowed! */
9017 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9018 if (SUCCEEDED(rc))
9019 return setError(VBOX_E_OBJECT_IN_USE,
9020 tr("Shared folder named '%s' already exists"),
9021 sf.strName.c_str());
9022
9023 /* Create the new shared folder. Don't break on error. This will be
9024 * reported when the machine starts. */
9025 sharedFolder.createObject();
9026 rc = sharedFolder->init(i_getMachine(),
9027 sf.strName,
9028 sf.strHostPath,
9029 RT_BOOL(sf.fWritable),
9030 RT_BOOL(sf.fAutoMount),
9031 sf.strAutoMountPoint,
9032 false /* fFailOnError */);
9033 if (FAILED(rc)) return rc;
9034 mHWData->mSharedFolders.push_back(sharedFolder);
9035 }
9036
9037 // Clipboard
9038 mHWData->mClipboardMode = data.clipboardMode;
9039
9040 // drag'n'drop
9041 mHWData->mDnDMode = data.dndMode;
9042
9043 // guest settings
9044 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9045
9046 // IO settings
9047 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9048 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9049
9050 // Host PCI devices
9051 for (settings::HostPCIDeviceAttachmentList::const_iterator
9052 it = data.pciAttachments.begin();
9053 it != data.pciAttachments.end();
9054 ++it)
9055 {
9056 const settings::HostPCIDeviceAttachment &hpda = *it;
9057 ComObjPtr<PCIDeviceAttachment> pda;
9058
9059 pda.createObject();
9060 pda->i_loadSettings(this, hpda);
9061 mHWData->mPCIDeviceAssignments.push_back(pda);
9062 }
9063
9064 /*
9065 * (The following isn't really real hardware, but it lives in HWData
9066 * for reasons of convenience.)
9067 */
9068
9069#ifdef VBOX_WITH_GUEST_PROPS
9070 /* Guest properties (optional) */
9071
9072 /* Only load transient guest properties for configs which have saved
9073 * state, because there shouldn't be any for powered off VMs. The same
9074 * logic applies for snapshots, as offline snapshots shouldn't have
9075 * any such properties. They confuse the code in various places.
9076 * Note: can't rely on the machine state, as it isn't set yet. */
9077 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9078 /* apologies for the hacky unconst() usage, but this needs hacking
9079 * actually inconsistent settings into consistency, otherwise there
9080 * will be some corner cases where the inconsistency survives
9081 * surprisingly long without getting fixed, especially for snapshots
9082 * as there are no config changes. */
9083 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9084 for (settings::GuestPropertiesList::iterator
9085 it = llGuestProperties.begin();
9086 it != llGuestProperties.end();
9087 /*nothing*/)
9088 {
9089 const settings::GuestProperty &prop = *it;
9090 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9091 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9092 if ( fSkipTransientGuestProperties
9093 && ( fFlags & GUEST_PROP_F_TRANSIENT
9094 || fFlags & GUEST_PROP_F_TRANSRESET))
9095 {
9096 it = llGuestProperties.erase(it);
9097 continue;
9098 }
9099 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9100 mHWData->mGuestProperties[prop.strName] = property;
9101 ++it;
9102 }
9103#endif /* VBOX_WITH_GUEST_PROPS defined */
9104
9105 rc = i_loadDebugging(pDbg);
9106 if (FAILED(rc))
9107 return rc;
9108
9109 mHWData->mAutostart = *pAutostart;
9110
9111 /* default frontend */
9112 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9113 }
9114 catch (std::bad_alloc &)
9115 {
9116 return E_OUTOFMEMORY;
9117 }
9118
9119 AssertComRC(rc);
9120 return rc;
9121}
9122
9123/**
9124 * Called from i_loadHardware() to load the debugging settings of the
9125 * machine.
9126 *
9127 * @param pDbg Pointer to the settings.
9128 */
9129HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9130{
9131 mHWData->mDebugging = *pDbg;
9132 /* no more processing currently required, this will probably change. */
9133 return S_OK;
9134}
9135
9136/**
9137 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9138 *
9139 * @param data storage settings.
9140 * @param puuidRegistry media registry ID to set media to or NULL;
9141 * see Machine::i_loadMachineDataFromSettings()
9142 * @param puuidSnapshot snapshot ID
9143 * @return
9144 */
9145HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9146 const Guid *puuidRegistry,
9147 const Guid *puuidSnapshot)
9148{
9149 AssertReturn(!i_isSessionMachine(), E_FAIL);
9150
9151 HRESULT rc = S_OK;
9152
9153 for (settings::StorageControllersList::const_iterator
9154 it = data.llStorageControllers.begin();
9155 it != data.llStorageControllers.end();
9156 ++it)
9157 {
9158 const settings::StorageController &ctlData = *it;
9159
9160 ComObjPtr<StorageController> pCtl;
9161 /* Try to find one with the name first. */
9162 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9163 if (SUCCEEDED(rc))
9164 return setError(VBOX_E_OBJECT_IN_USE,
9165 tr("Storage controller named '%s' already exists"),
9166 ctlData.strName.c_str());
9167
9168 pCtl.createObject();
9169 rc = pCtl->init(this,
9170 ctlData.strName,
9171 ctlData.storageBus,
9172 ctlData.ulInstance,
9173 ctlData.fBootable);
9174 if (FAILED(rc)) return rc;
9175
9176 mStorageControllers->push_back(pCtl);
9177
9178 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9179 if (FAILED(rc)) return rc;
9180
9181 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9182 if (FAILED(rc)) return rc;
9183
9184 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9185 if (FAILED(rc)) return rc;
9186
9187 /* Load the attached devices now. */
9188 rc = i_loadStorageDevices(pCtl,
9189 ctlData,
9190 puuidRegistry,
9191 puuidSnapshot);
9192 if (FAILED(rc)) return rc;
9193 }
9194
9195 return S_OK;
9196}
9197
9198/**
9199 * Called from i_loadStorageControllers for a controller's devices.
9200 *
9201 * @param aStorageController
9202 * @param data
9203 * @param puuidRegistry media registry ID to set media to or NULL; see
9204 * Machine::i_loadMachineDataFromSettings()
9205 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9206 * @return
9207 */
9208HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9209 const settings::StorageController &data,
9210 const Guid *puuidRegistry,
9211 const Guid *puuidSnapshot)
9212{
9213 HRESULT rc = S_OK;
9214
9215 /* paranoia: detect duplicate attachments */
9216 for (settings::AttachedDevicesList::const_iterator
9217 it = data.llAttachedDevices.begin();
9218 it != data.llAttachedDevices.end();
9219 ++it)
9220 {
9221 const settings::AttachedDevice &ad = *it;
9222
9223 for (settings::AttachedDevicesList::const_iterator it2 = it;
9224 it2 != data.llAttachedDevices.end();
9225 ++it2)
9226 {
9227 if (it == it2)
9228 continue;
9229
9230 const settings::AttachedDevice &ad2 = *it2;
9231
9232 if ( ad.lPort == ad2.lPort
9233 && ad.lDevice == ad2.lDevice)
9234 {
9235 return setError(E_FAIL,
9236 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9237 aStorageController->i_getName().c_str(),
9238 ad.lPort,
9239 ad.lDevice,
9240 mUserData->s.strName.c_str());
9241 }
9242 }
9243 }
9244
9245 for (settings::AttachedDevicesList::const_iterator
9246 it = data.llAttachedDevices.begin();
9247 it != data.llAttachedDevices.end();
9248 ++it)
9249 {
9250 const settings::AttachedDevice &dev = *it;
9251 ComObjPtr<Medium> medium;
9252
9253 switch (dev.deviceType)
9254 {
9255 case DeviceType_Floppy:
9256 case DeviceType_DVD:
9257 if (dev.strHostDriveSrc.isNotEmpty())
9258 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9259 false /* fRefresh */, medium);
9260 else
9261 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9262 dev.uuid,
9263 false /* fRefresh */,
9264 false /* aSetError */,
9265 medium);
9266 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9267 // This is not an error. The host drive or UUID might have vanished, so just go
9268 // ahead without this removeable medium attachment
9269 rc = S_OK;
9270 break;
9271
9272 case DeviceType_HardDisk:
9273 {
9274 /* find a hard disk by UUID */
9275 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9276 if (FAILED(rc))
9277 {
9278 if (i_isSnapshotMachine())
9279 {
9280 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9281 // so the user knows that the bad disk is in a snapshot somewhere
9282 com::ErrorInfo info;
9283 return setError(E_FAIL,
9284 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9285 puuidSnapshot->raw(),
9286 info.getText().raw());
9287 }
9288 else
9289 return rc;
9290 }
9291
9292 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9293
9294 if (medium->i_getType() == MediumType_Immutable)
9295 {
9296 if (i_isSnapshotMachine())
9297 return setError(E_FAIL,
9298 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9299 "of the virtual machine '%s' ('%s')"),
9300 medium->i_getLocationFull().c_str(),
9301 dev.uuid.raw(),
9302 puuidSnapshot->raw(),
9303 mUserData->s.strName.c_str(),
9304 mData->m_strConfigFileFull.c_str());
9305
9306 return setError(E_FAIL,
9307 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9308 medium->i_getLocationFull().c_str(),
9309 dev.uuid.raw(),
9310 mUserData->s.strName.c_str(),
9311 mData->m_strConfigFileFull.c_str());
9312 }
9313
9314 if (medium->i_getType() == MediumType_MultiAttach)
9315 {
9316 if (i_isSnapshotMachine())
9317 return setError(E_FAIL,
9318 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9319 "of the virtual machine '%s' ('%s')"),
9320 medium->i_getLocationFull().c_str(),
9321 dev.uuid.raw(),
9322 puuidSnapshot->raw(),
9323 mUserData->s.strName.c_str(),
9324 mData->m_strConfigFileFull.c_str());
9325
9326 return setError(E_FAIL,
9327 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9328 medium->i_getLocationFull().c_str(),
9329 dev.uuid.raw(),
9330 mUserData->s.strName.c_str(),
9331 mData->m_strConfigFileFull.c_str());
9332 }
9333
9334 if ( !i_isSnapshotMachine()
9335 && medium->i_getChildren().size() != 0
9336 )
9337 return setError(E_FAIL,
9338 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9339 "because it has %d differencing child hard disks"),
9340 medium->i_getLocationFull().c_str(),
9341 dev.uuid.raw(),
9342 mUserData->s.strName.c_str(),
9343 mData->m_strConfigFileFull.c_str(),
9344 medium->i_getChildren().size());
9345
9346 if (i_findAttachment(*mMediumAttachments.data(),
9347 medium))
9348 return setError(E_FAIL,
9349 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9350 medium->i_getLocationFull().c_str(),
9351 dev.uuid.raw(),
9352 mUserData->s.strName.c_str(),
9353 mData->m_strConfigFileFull.c_str());
9354
9355 break;
9356 }
9357
9358 default:
9359 return setError(E_FAIL,
9360 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9361 medium->i_getLocationFull().c_str(),
9362 mUserData->s.strName.c_str(),
9363 mData->m_strConfigFileFull.c_str());
9364 }
9365
9366 if (FAILED(rc))
9367 break;
9368
9369 /* Bandwidth groups are loaded at this point. */
9370 ComObjPtr<BandwidthGroup> pBwGroup;
9371
9372 if (!dev.strBwGroup.isEmpty())
9373 {
9374 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9375 if (FAILED(rc))
9376 return setError(E_FAIL,
9377 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9378 medium->i_getLocationFull().c_str(),
9379 dev.strBwGroup.c_str(),
9380 mUserData->s.strName.c_str(),
9381 mData->m_strConfigFileFull.c_str());
9382 pBwGroup->i_reference();
9383 }
9384
9385 const Utf8Str controllerName = aStorageController->i_getName();
9386 ComObjPtr<MediumAttachment> pAttachment;
9387 pAttachment.createObject();
9388 rc = pAttachment->init(this,
9389 medium,
9390 controllerName,
9391 dev.lPort,
9392 dev.lDevice,
9393 dev.deviceType,
9394 false,
9395 dev.fPassThrough,
9396 dev.fTempEject,
9397 dev.fNonRotational,
9398 dev.fDiscard,
9399 dev.fHotPluggable,
9400 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9401 if (FAILED(rc)) break;
9402
9403 /* associate the medium with this machine and snapshot */
9404 if (!medium.isNull())
9405 {
9406 AutoCaller medCaller(medium);
9407 if (FAILED(medCaller.rc())) return medCaller.rc();
9408 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9409
9410 if (i_isSnapshotMachine())
9411 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9412 else
9413 rc = medium->i_addBackReference(mData->mUuid);
9414 /* If the medium->addBackReference fails it sets an appropriate
9415 * error message, so no need to do any guesswork here. */
9416
9417 if (puuidRegistry)
9418 // caller wants registry ID to be set on all attached media (OVF import case)
9419 medium->i_addRegistry(*puuidRegistry);
9420 }
9421
9422 if (FAILED(rc))
9423 break;
9424
9425 /* back up mMediumAttachments to let registeredInit() properly rollback
9426 * on failure (= limited accessibility) */
9427 i_setModified(IsModified_Storage);
9428 mMediumAttachments.backup();
9429 mMediumAttachments->push_back(pAttachment);
9430 }
9431
9432 return rc;
9433}
9434
9435/**
9436 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9437 *
9438 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9439 * @param aSnapshot where to return the found snapshot
9440 * @param aSetError true to set extended error info on failure
9441 */
9442HRESULT Machine::i_findSnapshotById(const Guid &aId,
9443 ComObjPtr<Snapshot> &aSnapshot,
9444 bool aSetError /* = false */)
9445{
9446 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9447
9448 if (!mData->mFirstSnapshot)
9449 {
9450 if (aSetError)
9451 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9452 return E_FAIL;
9453 }
9454
9455 if (aId.isZero())
9456 aSnapshot = mData->mFirstSnapshot;
9457 else
9458 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9459
9460 if (!aSnapshot)
9461 {
9462 if (aSetError)
9463 return setError(E_FAIL,
9464 tr("Could not find a snapshot with UUID {%s}"),
9465 aId.toString().c_str());
9466 return E_FAIL;
9467 }
9468
9469 return S_OK;
9470}
9471
9472/**
9473 * Returns the snapshot with the given name or fails of no such snapshot.
9474 *
9475 * @param strName snapshot name to find
9476 * @param aSnapshot where to return the found snapshot
9477 * @param aSetError true to set extended error info on failure
9478 */
9479HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9480 ComObjPtr<Snapshot> &aSnapshot,
9481 bool aSetError /* = false */)
9482{
9483 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9484
9485 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9486
9487 if (!mData->mFirstSnapshot)
9488 {
9489 if (aSetError)
9490 return setError(VBOX_E_OBJECT_NOT_FOUND,
9491 tr("This machine does not have any snapshots"));
9492 return VBOX_E_OBJECT_NOT_FOUND;
9493 }
9494
9495 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9496
9497 if (!aSnapshot)
9498 {
9499 if (aSetError)
9500 return setError(VBOX_E_OBJECT_NOT_FOUND,
9501 tr("Could not find a snapshot named '%s'"), strName.c_str());
9502 return VBOX_E_OBJECT_NOT_FOUND;
9503 }
9504
9505 return S_OK;
9506}
9507
9508/**
9509 * Returns a storage controller object with the given name.
9510 *
9511 * @param aName storage controller name to find
9512 * @param aStorageController where to return the found storage controller
9513 * @param aSetError true to set extended error info on failure
9514 */
9515HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9516 ComObjPtr<StorageController> &aStorageController,
9517 bool aSetError /* = false */)
9518{
9519 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9520
9521 for (StorageControllerList::const_iterator
9522 it = mStorageControllers->begin();
9523 it != mStorageControllers->end();
9524 ++it)
9525 {
9526 if ((*it)->i_getName() == aName)
9527 {
9528 aStorageController = (*it);
9529 return S_OK;
9530 }
9531 }
9532
9533 if (aSetError)
9534 return setError(VBOX_E_OBJECT_NOT_FOUND,
9535 tr("Could not find a storage controller named '%s'"),
9536 aName.c_str());
9537 return VBOX_E_OBJECT_NOT_FOUND;
9538}
9539
9540/**
9541 * Returns a USB controller object with the given name.
9542 *
9543 * @param aName USB controller name to find
9544 * @param aUSBController where to return the found USB controller
9545 * @param aSetError true to set extended error info on failure
9546 */
9547HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9548 ComObjPtr<USBController> &aUSBController,
9549 bool aSetError /* = false */)
9550{
9551 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9552
9553 for (USBControllerList::const_iterator
9554 it = mUSBControllers->begin();
9555 it != mUSBControllers->end();
9556 ++it)
9557 {
9558 if ((*it)->i_getName() == aName)
9559 {
9560 aUSBController = (*it);
9561 return S_OK;
9562 }
9563 }
9564
9565 if (aSetError)
9566 return setError(VBOX_E_OBJECT_NOT_FOUND,
9567 tr("Could not find a storage controller named '%s'"),
9568 aName.c_str());
9569 return VBOX_E_OBJECT_NOT_FOUND;
9570}
9571
9572/**
9573 * Returns the number of USB controller instance of the given type.
9574 *
9575 * @param enmType USB controller type.
9576 */
9577ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9578{
9579 ULONG cCtrls = 0;
9580
9581 for (USBControllerList::const_iterator
9582 it = mUSBControllers->begin();
9583 it != mUSBControllers->end();
9584 ++it)
9585 {
9586 if ((*it)->i_getControllerType() == enmType)
9587 cCtrls++;
9588 }
9589
9590 return cCtrls;
9591}
9592
9593HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9594 MediumAttachmentList &atts)
9595{
9596 AutoCaller autoCaller(this);
9597 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9598
9599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9600
9601 for (MediumAttachmentList::const_iterator
9602 it = mMediumAttachments->begin();
9603 it != mMediumAttachments->end();
9604 ++it)
9605 {
9606 const ComObjPtr<MediumAttachment> &pAtt = *it;
9607 // should never happen, but deal with NULL pointers in the list.
9608 AssertContinue(!pAtt.isNull());
9609
9610 // getControllerName() needs caller+read lock
9611 AutoCaller autoAttCaller(pAtt);
9612 if (FAILED(autoAttCaller.rc()))
9613 {
9614 atts.clear();
9615 return autoAttCaller.rc();
9616 }
9617 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9618
9619 if (pAtt->i_getControllerName() == aName)
9620 atts.push_back(pAtt);
9621 }
9622
9623 return S_OK;
9624}
9625
9626
9627/**
9628 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9629 * file if the machine name was changed and about creating a new settings file
9630 * if this is a new machine.
9631 *
9632 * @note Must be never called directly but only from #saveSettings().
9633 */
9634HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9635{
9636 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9637
9638 HRESULT rc = S_OK;
9639
9640 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9641
9642 /// @todo need to handle primary group change, too
9643
9644 /* attempt to rename the settings file if machine name is changed */
9645 if ( mUserData->s.fNameSync
9646 && mUserData.isBackedUp()
9647 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9648 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9649 )
9650 {
9651 bool dirRenamed = false;
9652 bool fileRenamed = false;
9653
9654 Utf8Str configFile, newConfigFile;
9655 Utf8Str configFilePrev, newConfigFilePrev;
9656 Utf8Str configDir, newConfigDir;
9657
9658 do
9659 {
9660 int vrc = VINF_SUCCESS;
9661
9662 Utf8Str name = mUserData.backedUpData()->s.strName;
9663 Utf8Str newName = mUserData->s.strName;
9664 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9665 if (group == "/")
9666 group.setNull();
9667 Utf8Str newGroup = mUserData->s.llGroups.front();
9668 if (newGroup == "/")
9669 newGroup.setNull();
9670
9671 configFile = mData->m_strConfigFileFull;
9672
9673 /* first, rename the directory if it matches the group and machine name */
9674 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9675 group.c_str(), RTPATH_DELIMITER, name.c_str());
9676 /** @todo hack, make somehow use of ComposeMachineFilename */
9677 if (mUserData->s.fDirectoryIncludesUUID)
9678 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9679 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9680 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9681 /** @todo hack, make somehow use of ComposeMachineFilename */
9682 if (mUserData->s.fDirectoryIncludesUUID)
9683 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9684 configDir = configFile;
9685 configDir.stripFilename();
9686 newConfigDir = configDir;
9687 if ( configDir.length() >= groupPlusName.length()
9688 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9689 groupPlusName.c_str()))
9690 {
9691 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9692 Utf8Str newConfigBaseDir(newConfigDir);
9693 newConfigDir.append(newGroupPlusName);
9694 /* consistency: use \ if appropriate on the platform */
9695 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9696 /* new dir and old dir cannot be equal here because of 'if'
9697 * above and because name != newName */
9698 Assert(configDir != newConfigDir);
9699 if (!fSettingsFileIsNew)
9700 {
9701 /* perform real rename only if the machine is not new */
9702 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9703 if ( vrc == VERR_FILE_NOT_FOUND
9704 || vrc == VERR_PATH_NOT_FOUND)
9705 {
9706 /* create the parent directory, then retry renaming */
9707 Utf8Str parent(newConfigDir);
9708 parent.stripFilename();
9709 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9710 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9711 }
9712 if (RT_FAILURE(vrc))
9713 {
9714 rc = setErrorBoth(E_FAIL, vrc,
9715 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9716 configDir.c_str(),
9717 newConfigDir.c_str(),
9718 vrc);
9719 break;
9720 }
9721 /* delete subdirectories which are no longer needed */
9722 Utf8Str dir(configDir);
9723 dir.stripFilename();
9724 while (dir != newConfigBaseDir && dir != ".")
9725 {
9726 vrc = RTDirRemove(dir.c_str());
9727 if (RT_FAILURE(vrc))
9728 break;
9729 dir.stripFilename();
9730 }
9731 dirRenamed = true;
9732 }
9733 }
9734
9735 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9736 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9737
9738 /* then try to rename the settings file itself */
9739 if (newConfigFile != configFile)
9740 {
9741 /* get the path to old settings file in renamed directory */
9742 configFile = Utf8StrFmt("%s%c%s",
9743 newConfigDir.c_str(),
9744 RTPATH_DELIMITER,
9745 RTPathFilename(configFile.c_str()));
9746 if (!fSettingsFileIsNew)
9747 {
9748 /* perform real rename only if the machine is not new */
9749 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9750 if (RT_FAILURE(vrc))
9751 {
9752 rc = setErrorBoth(E_FAIL, vrc,
9753 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9754 configFile.c_str(),
9755 newConfigFile.c_str(),
9756 vrc);
9757 break;
9758 }
9759 fileRenamed = true;
9760 configFilePrev = configFile;
9761 configFilePrev += "-prev";
9762 newConfigFilePrev = newConfigFile;
9763 newConfigFilePrev += "-prev";
9764 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9765 }
9766 }
9767
9768 // update m_strConfigFileFull amd mConfigFile
9769 mData->m_strConfigFileFull = newConfigFile;
9770 // compute the relative path too
9771 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9772
9773 // store the old and new so that VirtualBox::i_saveSettings() can update
9774 // the media registry
9775 if ( mData->mRegistered
9776 && (configDir != newConfigDir || configFile != newConfigFile))
9777 {
9778 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9779
9780 if (pfNeedsGlobalSaveSettings)
9781 *pfNeedsGlobalSaveSettings = true;
9782 }
9783
9784 // in the saved state file path, replace the old directory with the new directory
9785 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9786 {
9787 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9788 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9789 }
9790
9791 // and do the same thing for the saved state file paths of all the online snapshots
9792 if (mData->mFirstSnapshot)
9793 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9794 newConfigDir.c_str());
9795 }
9796 while (0);
9797
9798 if (FAILED(rc))
9799 {
9800 /* silently try to rename everything back */
9801 if (fileRenamed)
9802 {
9803 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9804 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9805 }
9806 if (dirRenamed)
9807 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9808 }
9809
9810 if (FAILED(rc)) return rc;
9811 }
9812
9813 if (fSettingsFileIsNew)
9814 {
9815 /* create a virgin config file */
9816 int vrc = VINF_SUCCESS;
9817
9818 /* ensure the settings directory exists */
9819 Utf8Str path(mData->m_strConfigFileFull);
9820 path.stripFilename();
9821 if (!RTDirExists(path.c_str()))
9822 {
9823 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9824 if (RT_FAILURE(vrc))
9825 {
9826 return setErrorBoth(E_FAIL, vrc,
9827 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9828 path.c_str(),
9829 vrc);
9830 }
9831 }
9832
9833 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9834 path = Utf8Str(mData->m_strConfigFileFull);
9835 RTFILE f = NIL_RTFILE;
9836 vrc = RTFileOpen(&f, path.c_str(),
9837 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9838 if (RT_FAILURE(vrc))
9839 return setErrorBoth(E_FAIL, vrc,
9840 tr("Could not create the settings file '%s' (%Rrc)"),
9841 path.c_str(),
9842 vrc);
9843 RTFileClose(f);
9844 }
9845
9846 return rc;
9847}
9848
9849/**
9850 * Saves and commits machine data, user data and hardware data.
9851 *
9852 * Note that on failure, the data remains uncommitted.
9853 *
9854 * @a aFlags may combine the following flags:
9855 *
9856 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9857 * Used when saving settings after an operation that makes them 100%
9858 * correspond to the settings from the current snapshot.
9859 * - SaveS_Force: settings will be saved without doing a deep compare of the
9860 * settings structures. This is used when this is called because snapshots
9861 * have changed to avoid the overhead of the deep compare.
9862 *
9863 * @note Must be called from under this object's write lock. Locks children for
9864 * writing.
9865 *
9866 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9867 * initialized to false and that will be set to true by this function if
9868 * the caller must invoke VirtualBox::i_saveSettings() because the global
9869 * settings have changed. This will happen if a machine rename has been
9870 * saved and the global machine and media registries will therefore need
9871 * updating.
9872 * @param aFlags Flags.
9873 */
9874HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9875 int aFlags /*= 0*/)
9876{
9877 LogFlowThisFuncEnter();
9878
9879 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9880
9881 /* make sure child objects are unable to modify the settings while we are
9882 * saving them */
9883 i_ensureNoStateDependencies();
9884
9885 AssertReturn(!i_isSnapshotMachine(),
9886 E_FAIL);
9887
9888 HRESULT rc = S_OK;
9889 bool fNeedsWrite = false;
9890
9891 /* First, prepare to save settings. It will care about renaming the
9892 * settings directory and file if the machine name was changed and about
9893 * creating a new settings file if this is a new machine. */
9894 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9895 if (FAILED(rc)) return rc;
9896
9897 // keep a pointer to the current settings structures
9898 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9899 settings::MachineConfigFile *pNewConfig = NULL;
9900
9901 try
9902 {
9903 // make a fresh one to have everyone write stuff into
9904 pNewConfig = new settings::MachineConfigFile(NULL);
9905 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9906
9907 // now go and copy all the settings data from COM to the settings structures
9908 // (this calls i_saveSettings() on all the COM objects in the machine)
9909 i_copyMachineDataToSettings(*pNewConfig);
9910
9911 if (aFlags & SaveS_ResetCurStateModified)
9912 {
9913 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9914 mData->mCurrentStateModified = FALSE;
9915 fNeedsWrite = true; // always, no need to compare
9916 }
9917 else if (aFlags & SaveS_Force)
9918 {
9919 fNeedsWrite = true; // always, no need to compare
9920 }
9921 else
9922 {
9923 if (!mData->mCurrentStateModified)
9924 {
9925 // do a deep compare of the settings that we just saved with the settings
9926 // previously stored in the config file; this invokes MachineConfigFile::operator==
9927 // which does a deep compare of all the settings, which is expensive but less expensive
9928 // than writing out XML in vain
9929 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9930
9931 // could still be modified if any settings changed
9932 mData->mCurrentStateModified = fAnySettingsChanged;
9933
9934 fNeedsWrite = fAnySettingsChanged;
9935 }
9936 else
9937 fNeedsWrite = true;
9938 }
9939
9940 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9941
9942 if (fNeedsWrite)
9943 // now spit it all out!
9944 pNewConfig->write(mData->m_strConfigFileFull);
9945
9946 mData->pMachineConfigFile = pNewConfig;
9947 delete pOldConfig;
9948 i_commit();
9949
9950 // after saving settings, we are no longer different from the XML on disk
9951 mData->flModifications = 0;
9952 }
9953 catch (HRESULT err)
9954 {
9955 // we assume that error info is set by the thrower
9956 rc = err;
9957
9958 // restore old config
9959 delete pNewConfig;
9960 mData->pMachineConfigFile = pOldConfig;
9961 }
9962 catch (...)
9963 {
9964 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9965 }
9966
9967 if (fNeedsWrite)
9968 {
9969 /* Fire the data change event, even on failure (since we've already
9970 * committed all data). This is done only for SessionMachines because
9971 * mutable Machine instances are always not registered (i.e. private
9972 * to the client process that creates them) and thus don't need to
9973 * inform callbacks. */
9974 if (i_isSessionMachine())
9975 mParent->i_onMachineDataChange(mData->mUuid);
9976 }
9977
9978 LogFlowThisFunc(("rc=%08X\n", rc));
9979 LogFlowThisFuncLeave();
9980 return rc;
9981}
9982
9983/**
9984 * Implementation for saving the machine settings into the given
9985 * settings::MachineConfigFile instance. This copies machine extradata
9986 * from the previous machine config file in the instance data, if any.
9987 *
9988 * This gets called from two locations:
9989 *
9990 * -- Machine::i_saveSettings(), during the regular XML writing;
9991 *
9992 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9993 * exported to OVF and we write the VirtualBox proprietary XML
9994 * into a <vbox:Machine> tag.
9995 *
9996 * This routine fills all the fields in there, including snapshots, *except*
9997 * for the following:
9998 *
9999 * -- fCurrentStateModified. There is some special logic associated with that.
10000 *
10001 * The caller can then call MachineConfigFile::write() or do something else
10002 * with it.
10003 *
10004 * Caller must hold the machine lock!
10005 *
10006 * This throws XML errors and HRESULT, so the caller must have a catch block!
10007 */
10008void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10009{
10010 // deep copy extradata, being extra careful with self assignment (the STL
10011 // map assignment on Mac OS X clang based Xcode isn't checking)
10012 if (&config != mData->pMachineConfigFile)
10013 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10014
10015 config.uuid = mData->mUuid;
10016
10017 // copy name, description, OS type, teleport, UTC etc.
10018 config.machineUserData = mUserData->s;
10019
10020 if ( mData->mMachineState == MachineState_Saved
10021 || mData->mMachineState == MachineState_Restoring
10022 // when doing certain snapshot operations we may or may not have
10023 // a saved state in the current state, so keep everything as is
10024 || ( ( mData->mMachineState == MachineState_Snapshotting
10025 || mData->mMachineState == MachineState_DeletingSnapshot
10026 || mData->mMachineState == MachineState_RestoringSnapshot)
10027 && (!mSSData->strStateFilePath.isEmpty())
10028 )
10029 )
10030 {
10031 Assert(!mSSData->strStateFilePath.isEmpty());
10032 /* try to make the file name relative to the settings file dir */
10033 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10034 }
10035 else
10036 {
10037 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10038 config.strStateFile.setNull();
10039 }
10040
10041 if (mData->mCurrentSnapshot)
10042 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10043 else
10044 config.uuidCurrentSnapshot.clear();
10045
10046 config.timeLastStateChange = mData->mLastStateChange;
10047 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10048 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10049
10050 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10051 if (FAILED(rc)) throw rc;
10052
10053 // save machine's media registry if this is VirtualBox 4.0 or later
10054 if (config.canHaveOwnMediaRegistry())
10055 {
10056 // determine machine folder
10057 Utf8Str strMachineFolder = i_getSettingsFileFull();
10058 strMachineFolder.stripFilename();
10059 mParent->i_saveMediaRegistry(config.mediaRegistry,
10060 i_getId(), // only media with registry ID == machine UUID
10061 strMachineFolder);
10062 // this throws HRESULT
10063 }
10064
10065 // save snapshots
10066 rc = i_saveAllSnapshots(config);
10067 if (FAILED(rc)) throw rc;
10068}
10069
10070/**
10071 * Saves all snapshots of the machine into the given machine config file. Called
10072 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10073 * @param config
10074 * @return
10075 */
10076HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10077{
10078 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10079
10080 HRESULT rc = S_OK;
10081
10082 try
10083 {
10084 config.llFirstSnapshot.clear();
10085
10086 if (mData->mFirstSnapshot)
10087 {
10088 // the settings use a list for "the first snapshot"
10089 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10090
10091 // get reference to the snapshot on the list and work on that
10092 // element straight in the list to avoid excessive copying later
10093 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10094 if (FAILED(rc)) throw rc;
10095 }
10096
10097// if (mType == IsSessionMachine)
10098// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10099
10100 }
10101 catch (HRESULT err)
10102 {
10103 /* we assume that error info is set by the thrower */
10104 rc = err;
10105 }
10106 catch (...)
10107 {
10108 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10109 }
10110
10111 return rc;
10112}
10113
10114/**
10115 * Saves the VM hardware configuration. It is assumed that the
10116 * given node is empty.
10117 *
10118 * @param data Reference to the settings object for the hardware config.
10119 * @param pDbg Pointer to the settings object for the debugging config
10120 * which happens to live in mHWData.
10121 * @param pAutostart Pointer to the settings object for the autostart config
10122 * which happens to live in mHWData.
10123 */
10124HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10125 settings::Autostart *pAutostart)
10126{
10127 HRESULT rc = S_OK;
10128
10129 try
10130 {
10131 /* The hardware version attribute (optional).
10132 Automatically upgrade from 1 to current default hardware version
10133 when there is no saved state. (ugly!) */
10134 if ( mHWData->mHWVersion == "1"
10135 && mSSData->strStateFilePath.isEmpty()
10136 )
10137 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10138
10139 data.strVersion = mHWData->mHWVersion;
10140 data.uuid = mHWData->mHardwareUUID;
10141
10142 // CPU
10143 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10144 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10145 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10146 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10147 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10148 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10149 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10150 data.fPAE = !!mHWData->mPAEEnabled;
10151 data.enmLongMode = mHWData->mLongMode;
10152 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10153 data.fAPIC = !!mHWData->mAPIC;
10154 data.fX2APIC = !!mHWData->mX2APIC;
10155 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10156 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10157 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10158 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10159 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10160 data.cCPUs = mHWData->mCPUCount;
10161 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10162 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10163 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10164 data.strCpuProfile = mHWData->mCpuProfile;
10165
10166 data.llCpus.clear();
10167 if (data.fCpuHotPlug)
10168 {
10169 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10170 {
10171 if (mHWData->mCPUAttached[idx])
10172 {
10173 settings::Cpu cpu;
10174 cpu.ulId = idx;
10175 data.llCpus.push_back(cpu);
10176 }
10177 }
10178 }
10179
10180 /* Standard and Extended CPUID leafs. */
10181 data.llCpuIdLeafs.clear();
10182 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10183
10184 // memory
10185 data.ulMemorySizeMB = mHWData->mMemorySize;
10186 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10187
10188 // firmware
10189 data.firmwareType = mHWData->mFirmwareType;
10190
10191 // HID
10192 data.pointingHIDType = mHWData->mPointingHIDType;
10193 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10194
10195 // chipset
10196 data.chipsetType = mHWData->mChipsetType;
10197
10198 // paravirt
10199 data.paravirtProvider = mHWData->mParavirtProvider;
10200 data.strParavirtDebug = mHWData->mParavirtDebug;
10201
10202 // emulated USB card reader
10203 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10204
10205 // HPET
10206 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10207
10208 // boot order
10209 data.mapBootOrder.clear();
10210 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10211 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10212
10213 // display
10214 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10215 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10216 data.cMonitors = mHWData->mMonitorCount;
10217 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10218 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10219
10220 /* VRDEServer settings (optional) */
10221 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10222 if (FAILED(rc)) throw rc;
10223
10224 /* BIOS settings (required) */
10225 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10226 if (FAILED(rc)) throw rc;
10227
10228 /* Recording settings (required) */
10229 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10230 if (FAILED(rc)) throw rc;
10231
10232 /* USB Controller (required) */
10233 data.usbSettings.llUSBControllers.clear();
10234 for (USBControllerList::const_iterator
10235 it = mUSBControllers->begin();
10236 it != mUSBControllers->end();
10237 ++it)
10238 {
10239 ComObjPtr<USBController> ctrl = *it;
10240 settings::USBController settingsCtrl;
10241
10242 settingsCtrl.strName = ctrl->i_getName();
10243 settingsCtrl.enmType = ctrl->i_getControllerType();
10244
10245 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10246 }
10247
10248 /* USB device filters (required) */
10249 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10250 if (FAILED(rc)) throw rc;
10251
10252 /* Network adapters (required) */
10253 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10254 data.llNetworkAdapters.clear();
10255 /* Write out only the nominal number of network adapters for this
10256 * chipset type. Since Machine::commit() hasn't been called there
10257 * may be extra NIC settings in the vector. */
10258 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10259 {
10260 settings::NetworkAdapter nic;
10261 nic.ulSlot = (uint32_t)slot;
10262 /* paranoia check... must not be NULL, but must not crash either. */
10263 if (mNetworkAdapters[slot])
10264 {
10265 if (mNetworkAdapters[slot]->i_hasDefaults())
10266 continue;
10267
10268 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10269 if (FAILED(rc)) throw rc;
10270
10271 data.llNetworkAdapters.push_back(nic);
10272 }
10273 }
10274
10275 /* Serial ports */
10276 data.llSerialPorts.clear();
10277 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10278 {
10279 if (mSerialPorts[slot]->i_hasDefaults())
10280 continue;
10281
10282 settings::SerialPort s;
10283 s.ulSlot = slot;
10284 rc = mSerialPorts[slot]->i_saveSettings(s);
10285 if (FAILED(rc)) return rc;
10286
10287 data.llSerialPorts.push_back(s);
10288 }
10289
10290 /* Parallel ports */
10291 data.llParallelPorts.clear();
10292 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10293 {
10294 if (mParallelPorts[slot]->i_hasDefaults())
10295 continue;
10296
10297 settings::ParallelPort p;
10298 p.ulSlot = slot;
10299 rc = mParallelPorts[slot]->i_saveSettings(p);
10300 if (FAILED(rc)) return rc;
10301
10302 data.llParallelPorts.push_back(p);
10303 }
10304
10305 /* Audio adapter */
10306 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10307 if (FAILED(rc)) return rc;
10308
10309 rc = i_saveStorageControllers(data.storage);
10310 if (FAILED(rc)) return rc;
10311
10312 /* Shared folders */
10313 data.llSharedFolders.clear();
10314 for (HWData::SharedFolderList::const_iterator
10315 it = mHWData->mSharedFolders.begin();
10316 it != mHWData->mSharedFolders.end();
10317 ++it)
10318 {
10319 SharedFolder *pSF = *it;
10320 AutoCaller sfCaller(pSF);
10321 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10322 settings::SharedFolder sf;
10323 sf.strName = pSF->i_getName();
10324 sf.strHostPath = pSF->i_getHostPath();
10325 sf.fWritable = !!pSF->i_isWritable();
10326 sf.fAutoMount = !!pSF->i_isAutoMounted();
10327 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10328
10329 data.llSharedFolders.push_back(sf);
10330 }
10331
10332 // clipboard
10333 data.clipboardMode = mHWData->mClipboardMode;
10334
10335 // drag'n'drop
10336 data.dndMode = mHWData->mDnDMode;
10337
10338 /* Guest */
10339 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10340
10341 // IO settings
10342 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10343 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10344
10345 /* BandwidthControl (required) */
10346 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10347 if (FAILED(rc)) throw rc;
10348
10349 /* Host PCI devices */
10350 data.pciAttachments.clear();
10351 for (HWData::PCIDeviceAssignmentList::const_iterator
10352 it = mHWData->mPCIDeviceAssignments.begin();
10353 it != mHWData->mPCIDeviceAssignments.end();
10354 ++it)
10355 {
10356 ComObjPtr<PCIDeviceAttachment> pda = *it;
10357 settings::HostPCIDeviceAttachment hpda;
10358
10359 rc = pda->i_saveSettings(hpda);
10360 if (FAILED(rc)) throw rc;
10361
10362 data.pciAttachments.push_back(hpda);
10363 }
10364
10365 // guest properties
10366 data.llGuestProperties.clear();
10367#ifdef VBOX_WITH_GUEST_PROPS
10368 for (HWData::GuestPropertyMap::const_iterator
10369 it = mHWData->mGuestProperties.begin();
10370 it != mHWData->mGuestProperties.end();
10371 ++it)
10372 {
10373 HWData::GuestProperty property = it->second;
10374
10375 /* Remove transient guest properties at shutdown unless we
10376 * are saving state. Note that restoring snapshot intentionally
10377 * keeps them, they will be removed if appropriate once the final
10378 * machine state is set (as crashes etc. need to work). */
10379 if ( ( mData->mMachineState == MachineState_PoweredOff
10380 || mData->mMachineState == MachineState_Aborted
10381 || mData->mMachineState == MachineState_Teleported)
10382 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10383 continue;
10384 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10385 prop.strName = it->first;
10386 prop.strValue = property.strValue;
10387 prop.timestamp = property.mTimestamp;
10388 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10389 GuestPropWriteFlags(property.mFlags, szFlags);
10390 prop.strFlags = szFlags;
10391
10392 data.llGuestProperties.push_back(prop);
10393 }
10394
10395 /* I presume this doesn't require a backup(). */
10396 mData->mGuestPropertiesModified = FALSE;
10397#endif /* VBOX_WITH_GUEST_PROPS defined */
10398
10399 *pDbg = mHWData->mDebugging;
10400 *pAutostart = mHWData->mAutostart;
10401
10402 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10403 }
10404 catch (std::bad_alloc &)
10405 {
10406 return E_OUTOFMEMORY;
10407 }
10408
10409 AssertComRC(rc);
10410 return rc;
10411}
10412
10413/**
10414 * Saves the storage controller configuration.
10415 *
10416 * @param data storage settings.
10417 */
10418HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10419{
10420 data.llStorageControllers.clear();
10421
10422 for (StorageControllerList::const_iterator
10423 it = mStorageControllers->begin();
10424 it != mStorageControllers->end();
10425 ++it)
10426 {
10427 HRESULT rc;
10428 ComObjPtr<StorageController> pCtl = *it;
10429
10430 settings::StorageController ctl;
10431 ctl.strName = pCtl->i_getName();
10432 ctl.controllerType = pCtl->i_getControllerType();
10433 ctl.storageBus = pCtl->i_getStorageBus();
10434 ctl.ulInstance = pCtl->i_getInstance();
10435 ctl.fBootable = pCtl->i_getBootable();
10436
10437 /* Save the port count. */
10438 ULONG portCount;
10439 rc = pCtl->COMGETTER(PortCount)(&portCount);
10440 ComAssertComRCRet(rc, rc);
10441 ctl.ulPortCount = portCount;
10442
10443 /* Save fUseHostIOCache */
10444 BOOL fUseHostIOCache;
10445 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10446 ComAssertComRCRet(rc, rc);
10447 ctl.fUseHostIOCache = !!fUseHostIOCache;
10448
10449 /* save the devices now. */
10450 rc = i_saveStorageDevices(pCtl, ctl);
10451 ComAssertComRCRet(rc, rc);
10452
10453 data.llStorageControllers.push_back(ctl);
10454 }
10455
10456 return S_OK;
10457}
10458
10459/**
10460 * Saves the hard disk configuration.
10461 */
10462HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10463 settings::StorageController &data)
10464{
10465 MediumAttachmentList atts;
10466
10467 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10468 if (FAILED(rc)) return rc;
10469
10470 data.llAttachedDevices.clear();
10471 for (MediumAttachmentList::const_iterator
10472 it = atts.begin();
10473 it != atts.end();
10474 ++it)
10475 {
10476 settings::AttachedDevice dev;
10477 IMediumAttachment *iA = *it;
10478 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10479 Medium *pMedium = pAttach->i_getMedium();
10480
10481 dev.deviceType = pAttach->i_getType();
10482 dev.lPort = pAttach->i_getPort();
10483 dev.lDevice = pAttach->i_getDevice();
10484 dev.fPassThrough = pAttach->i_getPassthrough();
10485 dev.fHotPluggable = pAttach->i_getHotPluggable();
10486 if (pMedium)
10487 {
10488 if (pMedium->i_isHostDrive())
10489 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10490 else
10491 dev.uuid = pMedium->i_getId();
10492 dev.fTempEject = pAttach->i_getTempEject();
10493 dev.fNonRotational = pAttach->i_getNonRotational();
10494 dev.fDiscard = pAttach->i_getDiscard();
10495 }
10496
10497 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10498
10499 data.llAttachedDevices.push_back(dev);
10500 }
10501
10502 return S_OK;
10503}
10504
10505/**
10506 * Saves machine state settings as defined by aFlags
10507 * (SaveSTS_* values).
10508 *
10509 * @param aFlags Combination of SaveSTS_* flags.
10510 *
10511 * @note Locks objects for writing.
10512 */
10513HRESULT Machine::i_saveStateSettings(int aFlags)
10514{
10515 if (aFlags == 0)
10516 return S_OK;
10517
10518 AutoCaller autoCaller(this);
10519 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10520
10521 /* This object's write lock is also necessary to serialize file access
10522 * (prevent concurrent reads and writes) */
10523 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10524
10525 HRESULT rc = S_OK;
10526
10527 Assert(mData->pMachineConfigFile);
10528
10529 try
10530 {
10531 if (aFlags & SaveSTS_CurStateModified)
10532 mData->pMachineConfigFile->fCurrentStateModified = true;
10533
10534 if (aFlags & SaveSTS_StateFilePath)
10535 {
10536 if (!mSSData->strStateFilePath.isEmpty())
10537 /* try to make the file name relative to the settings file dir */
10538 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10539 else
10540 mData->pMachineConfigFile->strStateFile.setNull();
10541 }
10542
10543 if (aFlags & SaveSTS_StateTimeStamp)
10544 {
10545 Assert( mData->mMachineState != MachineState_Aborted
10546 || mSSData->strStateFilePath.isEmpty());
10547
10548 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10549
10550 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10551/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10552 }
10553
10554 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10555 }
10556 catch (...)
10557 {
10558 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10559 }
10560
10561 return rc;
10562}
10563
10564/**
10565 * Ensures that the given medium is added to a media registry. If this machine
10566 * was created with 4.0 or later, then the machine registry is used. Otherwise
10567 * the global VirtualBox media registry is used.
10568 *
10569 * Caller must NOT hold machine lock, media tree or any medium locks!
10570 *
10571 * @param pMedium
10572 */
10573void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10574{
10575 /* Paranoia checks: do not hold machine or media tree locks. */
10576 AssertReturnVoid(!isWriteLockOnCurrentThread());
10577 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10578
10579 ComObjPtr<Medium> pBase;
10580 {
10581 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10582 pBase = pMedium->i_getBase();
10583 }
10584
10585 /* Paranoia checks: do not hold medium locks. */
10586 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10587 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10588
10589 // decide which medium registry to use now that the medium is attached:
10590 Guid uuid;
10591 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10592 if (fCanHaveOwnMediaRegistry)
10593 // machine XML is VirtualBox 4.0 or higher:
10594 uuid = i_getId(); // machine UUID
10595 else
10596 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10597
10598 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10599 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10600 if (pMedium->i_addRegistry(uuid))
10601 mParent->i_markRegistryModified(uuid);
10602
10603 /* For more complex hard disk structures it can happen that the base
10604 * medium isn't yet associated with any medium registry. Do that now. */
10605 if (pMedium != pBase)
10606 {
10607 /* Tree lock needed by Medium::addRegistry when recursing. */
10608 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10609 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10610 {
10611 treeLock.release();
10612 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10613 treeLock.acquire();
10614 }
10615 if (pBase->i_addRegistryRecursive(uuid))
10616 {
10617 treeLock.release();
10618 mParent->i_markRegistryModified(uuid);
10619 }
10620 }
10621}
10622
10623/**
10624 * Creates differencing hard disks for all normal hard disks attached to this
10625 * machine and a new set of attachments to refer to created disks.
10626 *
10627 * Used when taking a snapshot or when deleting the current state. Gets called
10628 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10629 *
10630 * This method assumes that mMediumAttachments contains the original hard disk
10631 * attachments it needs to create diffs for. On success, these attachments will
10632 * be replaced with the created diffs.
10633 *
10634 * Attachments with non-normal hard disks are left as is.
10635 *
10636 * If @a aOnline is @c false then the original hard disks that require implicit
10637 * diffs will be locked for reading. Otherwise it is assumed that they are
10638 * already locked for writing (when the VM was started). Note that in the latter
10639 * case it is responsibility of the caller to lock the newly created diffs for
10640 * writing if this method succeeds.
10641 *
10642 * @param aProgress Progress object to run (must contain at least as
10643 * many operations left as the number of hard disks
10644 * attached).
10645 * @param aWeight Weight of this operation.
10646 * @param aOnline Whether the VM was online prior to this operation.
10647 *
10648 * @note The progress object is not marked as completed, neither on success nor
10649 * on failure. This is a responsibility of the caller.
10650 *
10651 * @note Locks this object and the media tree for writing.
10652 */
10653HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10654 ULONG aWeight,
10655 bool aOnline)
10656{
10657 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10658
10659 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10660 AssertReturn(!!pProgressControl, E_INVALIDARG);
10661
10662 AutoCaller autoCaller(this);
10663 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10664
10665 AutoMultiWriteLock2 alock(this->lockHandle(),
10666 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10667
10668 /* must be in a protective state because we release the lock below */
10669 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10670 || mData->mMachineState == MachineState_OnlineSnapshotting
10671 || mData->mMachineState == MachineState_LiveSnapshotting
10672 || mData->mMachineState == MachineState_RestoringSnapshot
10673 || mData->mMachineState == MachineState_DeletingSnapshot
10674 , E_FAIL);
10675
10676 HRESULT rc = S_OK;
10677
10678 // use appropriate locked media map (online or offline)
10679 MediumLockListMap lockedMediaOffline;
10680 MediumLockListMap *lockedMediaMap;
10681 if (aOnline)
10682 lockedMediaMap = &mData->mSession.mLockedMedia;
10683 else
10684 lockedMediaMap = &lockedMediaOffline;
10685
10686 try
10687 {
10688 if (!aOnline)
10689 {
10690 /* lock all attached hard disks early to detect "in use"
10691 * situations before creating actual diffs */
10692 for (MediumAttachmentList::const_iterator
10693 it = mMediumAttachments->begin();
10694 it != mMediumAttachments->end();
10695 ++it)
10696 {
10697 MediumAttachment *pAtt = *it;
10698 if (pAtt->i_getType() == DeviceType_HardDisk)
10699 {
10700 Medium *pMedium = pAtt->i_getMedium();
10701 Assert(pMedium);
10702
10703 MediumLockList *pMediumLockList(new MediumLockList());
10704 alock.release();
10705 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10706 NULL /* pToLockWrite */,
10707 false /* fMediumLockWriteAll */,
10708 NULL,
10709 *pMediumLockList);
10710 alock.acquire();
10711 if (FAILED(rc))
10712 {
10713 delete pMediumLockList;
10714 throw rc;
10715 }
10716 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10717 if (FAILED(rc))
10718 {
10719 throw setError(rc,
10720 tr("Collecting locking information for all attached media failed"));
10721 }
10722 }
10723 }
10724
10725 /* Now lock all media. If this fails, nothing is locked. */
10726 alock.release();
10727 rc = lockedMediaMap->Lock();
10728 alock.acquire();
10729 if (FAILED(rc))
10730 {
10731 throw setError(rc,
10732 tr("Locking of attached media failed"));
10733 }
10734 }
10735
10736 /* remember the current list (note that we don't use backup() since
10737 * mMediumAttachments may be already backed up) */
10738 MediumAttachmentList atts = *mMediumAttachments.data();
10739
10740 /* start from scratch */
10741 mMediumAttachments->clear();
10742
10743 /* go through remembered attachments and create diffs for normal hard
10744 * disks and attach them */
10745 for (MediumAttachmentList::const_iterator
10746 it = atts.begin();
10747 it != atts.end();
10748 ++it)
10749 {
10750 MediumAttachment *pAtt = *it;
10751
10752 DeviceType_T devType = pAtt->i_getType();
10753 Medium *pMedium = pAtt->i_getMedium();
10754
10755 if ( devType != DeviceType_HardDisk
10756 || pMedium == NULL
10757 || pMedium->i_getType() != MediumType_Normal)
10758 {
10759 /* copy the attachment as is */
10760
10761 /** @todo the progress object created in SessionMachine::TakeSnaphot
10762 * only expects operations for hard disks. Later other
10763 * device types need to show up in the progress as well. */
10764 if (devType == DeviceType_HardDisk)
10765 {
10766 if (pMedium == NULL)
10767 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10768 aWeight); // weight
10769 else
10770 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10771 pMedium->i_getBase()->i_getName().c_str()).raw(),
10772 aWeight); // weight
10773 }
10774
10775 mMediumAttachments->push_back(pAtt);
10776 continue;
10777 }
10778
10779 /* need a diff */
10780 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10781 pMedium->i_getBase()->i_getName().c_str()).raw(),
10782 aWeight); // weight
10783
10784 Utf8Str strFullSnapshotFolder;
10785 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10786
10787 ComObjPtr<Medium> diff;
10788 diff.createObject();
10789 // store the diff in the same registry as the parent
10790 // (this cannot fail here because we can't create implicit diffs for
10791 // unregistered images)
10792 Guid uuidRegistryParent;
10793 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10794 Assert(fInRegistry); NOREF(fInRegistry);
10795 rc = diff->init(mParent,
10796 pMedium->i_getPreferredDiffFormat(),
10797 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10798 uuidRegistryParent,
10799 DeviceType_HardDisk);
10800 if (FAILED(rc)) throw rc;
10801
10802 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10803 * the push_back? Looks like we're going to release medium with the
10804 * wrong kind of lock (general issue with if we fail anywhere at all)
10805 * and an orphaned VDI in the snapshots folder. */
10806
10807 /* update the appropriate lock list */
10808 MediumLockList *pMediumLockList;
10809 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10810 AssertComRCThrowRC(rc);
10811 if (aOnline)
10812 {
10813 alock.release();
10814 /* The currently attached medium will be read-only, change
10815 * the lock type to read. */
10816 rc = pMediumLockList->Update(pMedium, false);
10817 alock.acquire();
10818 AssertComRCThrowRC(rc);
10819 }
10820
10821 /* release the locks before the potentially lengthy operation */
10822 alock.release();
10823 rc = pMedium->i_createDiffStorage(diff,
10824 pMedium->i_getPreferredDiffVariant(),
10825 pMediumLockList,
10826 NULL /* aProgress */,
10827 true /* aWait */,
10828 false /* aNotify */);
10829 alock.acquire();
10830 if (FAILED(rc)) throw rc;
10831
10832 /* actual lock list update is done in Machine::i_commitMedia */
10833
10834 rc = diff->i_addBackReference(mData->mUuid);
10835 AssertComRCThrowRC(rc);
10836
10837 /* add a new attachment */
10838 ComObjPtr<MediumAttachment> attachment;
10839 attachment.createObject();
10840 rc = attachment->init(this,
10841 diff,
10842 pAtt->i_getControllerName(),
10843 pAtt->i_getPort(),
10844 pAtt->i_getDevice(),
10845 DeviceType_HardDisk,
10846 true /* aImplicit */,
10847 false /* aPassthrough */,
10848 false /* aTempEject */,
10849 pAtt->i_getNonRotational(),
10850 pAtt->i_getDiscard(),
10851 pAtt->i_getHotPluggable(),
10852 pAtt->i_getBandwidthGroup());
10853 if (FAILED(rc)) throw rc;
10854
10855 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10856 AssertComRCThrowRC(rc);
10857 mMediumAttachments->push_back(attachment);
10858 }
10859 }
10860 catch (HRESULT aRC) { rc = aRC; }
10861
10862 /* unlock all hard disks we locked when there is no VM */
10863 if (!aOnline)
10864 {
10865 ErrorInfoKeeper eik;
10866
10867 HRESULT rc1 = lockedMediaMap->Clear();
10868 AssertComRC(rc1);
10869 }
10870
10871 return rc;
10872}
10873
10874/**
10875 * Deletes implicit differencing hard disks created either by
10876 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10877 * mMediumAttachments.
10878 *
10879 * Note that to delete hard disks created by #attachDevice() this method is
10880 * called from #i_rollbackMedia() when the changes are rolled back.
10881 *
10882 * @note Locks this object and the media tree for writing.
10883 */
10884HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10885{
10886 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10887
10888 AutoCaller autoCaller(this);
10889 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10890
10891 AutoMultiWriteLock2 alock(this->lockHandle(),
10892 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10893
10894 /* We absolutely must have backed up state. */
10895 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10896
10897 /* Check if there are any implicitly created diff images. */
10898 bool fImplicitDiffs = false;
10899 for (MediumAttachmentList::const_iterator
10900 it = mMediumAttachments->begin();
10901 it != mMediumAttachments->end();
10902 ++it)
10903 {
10904 const ComObjPtr<MediumAttachment> &pAtt = *it;
10905 if (pAtt->i_isImplicit())
10906 {
10907 fImplicitDiffs = true;
10908 break;
10909 }
10910 }
10911 /* If there is nothing to do, leave early. This saves lots of image locking
10912 * effort. It also avoids a MachineStateChanged event without real reason.
10913 * This is important e.g. when loading a VM config, because there should be
10914 * no events. Otherwise API clients can become thoroughly confused for
10915 * inaccessible VMs (the code for loading VM configs uses this method for
10916 * cleanup if the config makes no sense), as they take such events as an
10917 * indication that the VM is alive, and they would force the VM config to
10918 * be reread, leading to an endless loop. */
10919 if (!fImplicitDiffs)
10920 return S_OK;
10921
10922 HRESULT rc = S_OK;
10923 MachineState_T oldState = mData->mMachineState;
10924
10925 /* will release the lock before the potentially lengthy operation,
10926 * so protect with the special state (unless already protected) */
10927 if ( oldState != MachineState_Snapshotting
10928 && oldState != MachineState_OnlineSnapshotting
10929 && oldState != MachineState_LiveSnapshotting
10930 && oldState != MachineState_RestoringSnapshot
10931 && oldState != MachineState_DeletingSnapshot
10932 && oldState != MachineState_DeletingSnapshotOnline
10933 && oldState != MachineState_DeletingSnapshotPaused
10934 )
10935 i_setMachineState(MachineState_SettingUp);
10936
10937 // use appropriate locked media map (online or offline)
10938 MediumLockListMap lockedMediaOffline;
10939 MediumLockListMap *lockedMediaMap;
10940 if (aOnline)
10941 lockedMediaMap = &mData->mSession.mLockedMedia;
10942 else
10943 lockedMediaMap = &lockedMediaOffline;
10944
10945 try
10946 {
10947 if (!aOnline)
10948 {
10949 /* lock all attached hard disks early to detect "in use"
10950 * situations before deleting actual diffs */
10951 for (MediumAttachmentList::const_iterator
10952 it = mMediumAttachments->begin();
10953 it != mMediumAttachments->end();
10954 ++it)
10955 {
10956 MediumAttachment *pAtt = *it;
10957 if (pAtt->i_getType() == DeviceType_HardDisk)
10958 {
10959 Medium *pMedium = pAtt->i_getMedium();
10960 Assert(pMedium);
10961
10962 MediumLockList *pMediumLockList(new MediumLockList());
10963 alock.release();
10964 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10965 NULL /* pToLockWrite */,
10966 false /* fMediumLockWriteAll */,
10967 NULL,
10968 *pMediumLockList);
10969 alock.acquire();
10970
10971 if (FAILED(rc))
10972 {
10973 delete pMediumLockList;
10974 throw rc;
10975 }
10976
10977 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10978 if (FAILED(rc))
10979 throw rc;
10980 }
10981 }
10982
10983 if (FAILED(rc))
10984 throw rc;
10985 } // end of offline
10986
10987 /* Lock lists are now up to date and include implicitly created media */
10988
10989 /* Go through remembered attachments and delete all implicitly created
10990 * diffs and fix up the attachment information */
10991 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10992 MediumAttachmentList implicitAtts;
10993 for (MediumAttachmentList::const_iterator
10994 it = mMediumAttachments->begin();
10995 it != mMediumAttachments->end();
10996 ++it)
10997 {
10998 ComObjPtr<MediumAttachment> pAtt = *it;
10999 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11000 if (pMedium.isNull())
11001 continue;
11002
11003 // Implicit attachments go on the list for deletion and back references are removed.
11004 if (pAtt->i_isImplicit())
11005 {
11006 /* Deassociate and mark for deletion */
11007 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11008 rc = pMedium->i_removeBackReference(mData->mUuid);
11009 if (FAILED(rc))
11010 throw rc;
11011 implicitAtts.push_back(pAtt);
11012 continue;
11013 }
11014
11015 /* Was this medium attached before? */
11016 if (!i_findAttachment(oldAtts, pMedium))
11017 {
11018 /* no: de-associate */
11019 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11020 rc = pMedium->i_removeBackReference(mData->mUuid);
11021 if (FAILED(rc))
11022 throw rc;
11023 continue;
11024 }
11025 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11026 }
11027
11028 /* If there are implicit attachments to delete, throw away the lock
11029 * map contents (which will unlock all media) since the medium
11030 * attachments will be rolled back. Below we need to completely
11031 * recreate the lock map anyway since it is infinitely complex to
11032 * do this incrementally (would need reconstructing each attachment
11033 * change, which would be extremely hairy). */
11034 if (implicitAtts.size() != 0)
11035 {
11036 ErrorInfoKeeper eik;
11037
11038 HRESULT rc1 = lockedMediaMap->Clear();
11039 AssertComRC(rc1);
11040 }
11041
11042 /* rollback hard disk changes */
11043 mMediumAttachments.rollback();
11044
11045 MultiResult mrc(S_OK);
11046
11047 // Delete unused implicit diffs.
11048 if (implicitAtts.size() != 0)
11049 {
11050 alock.release();
11051
11052 for (MediumAttachmentList::const_iterator
11053 it = implicitAtts.begin();
11054 it != implicitAtts.end();
11055 ++it)
11056 {
11057 // Remove medium associated with this attachment.
11058 ComObjPtr<MediumAttachment> pAtt = *it;
11059 Assert(pAtt);
11060 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11061 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11062 Assert(pMedium);
11063
11064 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11065 // continue on delete failure, just collect error messages
11066 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11067 pMedium->i_getLocationFull().c_str() ));
11068 mrc = rc;
11069 }
11070 // Clear the list of deleted implicit attachments now, while not
11071 // holding the lock, as it will ultimately trigger Medium::uninit()
11072 // calls which assume that the media tree lock isn't held.
11073 implicitAtts.clear();
11074
11075 alock.acquire();
11076
11077 /* if there is a VM recreate media lock map as mentioned above,
11078 * otherwise it is a waste of time and we leave things unlocked */
11079 if (aOnline)
11080 {
11081 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11082 /* must never be NULL, but better safe than sorry */
11083 if (!pMachine.isNull())
11084 {
11085 alock.release();
11086 rc = mData->mSession.mMachine->i_lockMedia();
11087 alock.acquire();
11088 if (FAILED(rc))
11089 throw rc;
11090 }
11091 }
11092 }
11093 }
11094 catch (HRESULT aRC) {rc = aRC;}
11095
11096 if (mData->mMachineState == MachineState_SettingUp)
11097 i_setMachineState(oldState);
11098
11099 /* unlock all hard disks we locked when there is no VM */
11100 if (!aOnline)
11101 {
11102 ErrorInfoKeeper eik;
11103
11104 HRESULT rc1 = lockedMediaMap->Clear();
11105 AssertComRC(rc1);
11106 }
11107
11108 return rc;
11109}
11110
11111
11112/**
11113 * Looks through the given list of media attachments for one with the given parameters
11114 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11115 * can be searched as well if needed.
11116 *
11117 * @param ll
11118 * @param aControllerName
11119 * @param aControllerPort
11120 * @param aDevice
11121 * @return
11122 */
11123MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11124 const Utf8Str &aControllerName,
11125 LONG aControllerPort,
11126 LONG aDevice)
11127{
11128 for (MediumAttachmentList::const_iterator
11129 it = ll.begin();
11130 it != ll.end();
11131 ++it)
11132 {
11133 MediumAttachment *pAttach = *it;
11134 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11135 return pAttach;
11136 }
11137
11138 return NULL;
11139}
11140
11141/**
11142 * Looks through the given list of media attachments for one with the given parameters
11143 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11144 * can be searched as well if needed.
11145 *
11146 * @param ll
11147 * @param pMedium
11148 * @return
11149 */
11150MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11151 ComObjPtr<Medium> pMedium)
11152{
11153 for (MediumAttachmentList::const_iterator
11154 it = ll.begin();
11155 it != ll.end();
11156 ++it)
11157 {
11158 MediumAttachment *pAttach = *it;
11159 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11160 if (pMediumThis == pMedium)
11161 return pAttach;
11162 }
11163
11164 return NULL;
11165}
11166
11167/**
11168 * Looks through the given list of media attachments for one with the given parameters
11169 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11170 * can be searched as well if needed.
11171 *
11172 * @param ll
11173 * @param id
11174 * @return
11175 */
11176MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11177 Guid &id)
11178{
11179 for (MediumAttachmentList::const_iterator
11180 it = ll.begin();
11181 it != ll.end();
11182 ++it)
11183 {
11184 MediumAttachment *pAttach = *it;
11185 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11186 if (pMediumThis->i_getId() == id)
11187 return pAttach;
11188 }
11189
11190 return NULL;
11191}
11192
11193/**
11194 * Main implementation for Machine::DetachDevice. This also gets called
11195 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11196 *
11197 * @param pAttach Medium attachment to detach.
11198 * @param writeLock Machine write lock which the caller must have locked once.
11199 * This may be released temporarily in here.
11200 * @param pSnapshot If NULL, then the detachment is for the current machine.
11201 * Otherwise this is for a SnapshotMachine, and this must be
11202 * its snapshot.
11203 * @return
11204 */
11205HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11206 AutoWriteLock &writeLock,
11207 Snapshot *pSnapshot)
11208{
11209 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11210 DeviceType_T mediumType = pAttach->i_getType();
11211
11212 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11213
11214 if (pAttach->i_isImplicit())
11215 {
11216 /* attempt to implicitly delete the implicitly created diff */
11217
11218 /// @todo move the implicit flag from MediumAttachment to Medium
11219 /// and forbid any hard disk operation when it is implicit. Or maybe
11220 /// a special media state for it to make it even more simple.
11221
11222 Assert(mMediumAttachments.isBackedUp());
11223
11224 /* will release the lock before the potentially lengthy operation, so
11225 * protect with the special state */
11226 MachineState_T oldState = mData->mMachineState;
11227 i_setMachineState(MachineState_SettingUp);
11228
11229 writeLock.release();
11230
11231 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11232 true /*aWait*/,
11233 false /*aNotify*/);
11234
11235 writeLock.acquire();
11236
11237 i_setMachineState(oldState);
11238
11239 if (FAILED(rc)) return rc;
11240 }
11241
11242 i_setModified(IsModified_Storage);
11243 mMediumAttachments.backup();
11244 mMediumAttachments->remove(pAttach);
11245
11246 if (!oldmedium.isNull())
11247 {
11248 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11249 if (pSnapshot)
11250 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11251 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11252 else if (mediumType != DeviceType_HardDisk)
11253 oldmedium->i_removeBackReference(mData->mUuid);
11254 }
11255
11256 return S_OK;
11257}
11258
11259/**
11260 * Goes thru all media of the given list and
11261 *
11262 * 1) calls i_detachDevice() on each of them for this machine and
11263 * 2) adds all Medium objects found in the process to the given list,
11264 * depending on cleanupMode.
11265 *
11266 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11267 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11268 * media to the list.
11269 *
11270 * This gets called from Machine::Unregister, both for the actual Machine and
11271 * the SnapshotMachine objects that might be found in the snapshots.
11272 *
11273 * Requires caller and locking. The machine lock must be passed in because it
11274 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11275 *
11276 * @param writeLock Machine lock from top-level caller; this gets passed to
11277 * i_detachDevice.
11278 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11279 * object if called for a SnapshotMachine.
11280 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11281 * added to llMedia; if Full, then all media get added;
11282 * otherwise no media get added.
11283 * @param llMedia Caller's list to receive Medium objects which got detached so
11284 * caller can close() them, depending on cleanupMode.
11285 * @return
11286 */
11287HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11288 Snapshot *pSnapshot,
11289 CleanupMode_T cleanupMode,
11290 MediaList &llMedia)
11291{
11292 Assert(isWriteLockOnCurrentThread());
11293
11294 HRESULT rc;
11295
11296 // make a temporary list because i_detachDevice invalidates iterators into
11297 // mMediumAttachments
11298 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11299
11300 for (MediumAttachmentList::iterator
11301 it = llAttachments2.begin();
11302 it != llAttachments2.end();
11303 ++it)
11304 {
11305 ComObjPtr<MediumAttachment> &pAttach = *it;
11306 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11307
11308 if (!pMedium.isNull())
11309 {
11310 AutoCaller mac(pMedium);
11311 if (FAILED(mac.rc())) return mac.rc();
11312 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11313 DeviceType_T devType = pMedium->i_getDeviceType();
11314 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11315 && devType == DeviceType_HardDisk)
11316 || (cleanupMode == CleanupMode_Full)
11317 )
11318 {
11319 llMedia.push_back(pMedium);
11320 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11321 /* Not allowed to keep this lock as below we need the parent
11322 * medium lock, and the lock order is parent to child. */
11323 lock.release();
11324 /*
11325 * Search for medias which are not attached to any machine, but
11326 * in the chain to an attached disk. Mediums are only consided
11327 * if they are:
11328 * - have only one child
11329 * - no references to any machines
11330 * - are of normal medium type
11331 */
11332 while (!pParent.isNull())
11333 {
11334 AutoCaller mac1(pParent);
11335 if (FAILED(mac1.rc())) return mac1.rc();
11336 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11337 if (pParent->i_getChildren().size() == 1)
11338 {
11339 if ( pParent->i_getMachineBackRefCount() == 0
11340 && pParent->i_getType() == MediumType_Normal
11341 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11342 llMedia.push_back(pParent);
11343 }
11344 else
11345 break;
11346 pParent = pParent->i_getParent();
11347 }
11348 }
11349 }
11350
11351 // real machine: then we need to use the proper method
11352 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11353
11354 if (FAILED(rc))
11355 return rc;
11356 }
11357
11358 return S_OK;
11359}
11360
11361/**
11362 * Perform deferred hard disk detachments.
11363 *
11364 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11365 * changed (not backed up).
11366 *
11367 * If @a aOnline is @c true then this method will also unlock the old hard
11368 * disks for which the new implicit diffs were created and will lock these new
11369 * diffs for writing.
11370 *
11371 * @param aOnline Whether the VM was online prior to this operation.
11372 *
11373 * @note Locks this object for writing!
11374 */
11375void Machine::i_commitMedia(bool aOnline /*= false*/)
11376{
11377 AutoCaller autoCaller(this);
11378 AssertComRCReturnVoid(autoCaller.rc());
11379
11380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11381
11382 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11383
11384 HRESULT rc = S_OK;
11385
11386 /* no attach/detach operations -- nothing to do */
11387 if (!mMediumAttachments.isBackedUp())
11388 return;
11389
11390 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11391 bool fMediaNeedsLocking = false;
11392
11393 /* enumerate new attachments */
11394 for (MediumAttachmentList::const_iterator
11395 it = mMediumAttachments->begin();
11396 it != mMediumAttachments->end();
11397 ++it)
11398 {
11399 MediumAttachment *pAttach = *it;
11400
11401 pAttach->i_commit();
11402
11403 Medium *pMedium = pAttach->i_getMedium();
11404 bool fImplicit = pAttach->i_isImplicit();
11405
11406 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11407 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11408 fImplicit));
11409
11410 /** @todo convert all this Machine-based voodoo to MediumAttachment
11411 * based commit logic. */
11412 if (fImplicit)
11413 {
11414 /* convert implicit attachment to normal */
11415 pAttach->i_setImplicit(false);
11416
11417 if ( aOnline
11418 && pMedium
11419 && pAttach->i_getType() == DeviceType_HardDisk
11420 )
11421 {
11422 /* update the appropriate lock list */
11423 MediumLockList *pMediumLockList;
11424 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11425 AssertComRC(rc);
11426 if (pMediumLockList)
11427 {
11428 /* unlock if there's a need to change the locking */
11429 if (!fMediaNeedsLocking)
11430 {
11431 rc = mData->mSession.mLockedMedia.Unlock();
11432 AssertComRC(rc);
11433 fMediaNeedsLocking = true;
11434 }
11435 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11436 AssertComRC(rc);
11437 rc = pMediumLockList->Append(pMedium, true);
11438 AssertComRC(rc);
11439 }
11440 }
11441
11442 continue;
11443 }
11444
11445 if (pMedium)
11446 {
11447 /* was this medium attached before? */
11448 for (MediumAttachmentList::iterator
11449 oldIt = oldAtts.begin();
11450 oldIt != oldAtts.end();
11451 ++oldIt)
11452 {
11453 MediumAttachment *pOldAttach = *oldIt;
11454 if (pOldAttach->i_getMedium() == pMedium)
11455 {
11456 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11457
11458 /* yes: remove from old to avoid de-association */
11459 oldAtts.erase(oldIt);
11460 break;
11461 }
11462 }
11463 }
11464 }
11465
11466 /* enumerate remaining old attachments and de-associate from the
11467 * current machine state */
11468 for (MediumAttachmentList::const_iterator
11469 it = oldAtts.begin();
11470 it != oldAtts.end();
11471 ++it)
11472 {
11473 MediumAttachment *pAttach = *it;
11474 Medium *pMedium = pAttach->i_getMedium();
11475
11476 /* Detach only hard disks, since DVD/floppy media is detached
11477 * instantly in MountMedium. */
11478 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11479 {
11480 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11481
11482 /* now de-associate from the current machine state */
11483 rc = pMedium->i_removeBackReference(mData->mUuid);
11484 AssertComRC(rc);
11485
11486 if (aOnline)
11487 {
11488 /* unlock since medium is not used anymore */
11489 MediumLockList *pMediumLockList;
11490 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11491 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11492 {
11493 /* this happens for online snapshots, there the attachment
11494 * is changing, but only to a diff image created under
11495 * the old one, so there is no separate lock list */
11496 Assert(!pMediumLockList);
11497 }
11498 else
11499 {
11500 AssertComRC(rc);
11501 if (pMediumLockList)
11502 {
11503 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11504 AssertComRC(rc);
11505 }
11506 }
11507 }
11508 }
11509 }
11510
11511 /* take media locks again so that the locking state is consistent */
11512 if (fMediaNeedsLocking)
11513 {
11514 Assert(aOnline);
11515 rc = mData->mSession.mLockedMedia.Lock();
11516 AssertComRC(rc);
11517 }
11518
11519 /* commit the hard disk changes */
11520 mMediumAttachments.commit();
11521
11522 if (i_isSessionMachine())
11523 {
11524 /*
11525 * Update the parent machine to point to the new owner.
11526 * This is necessary because the stored parent will point to the
11527 * session machine otherwise and cause crashes or errors later
11528 * when the session machine gets invalid.
11529 */
11530 /** @todo Change the MediumAttachment class to behave like any other
11531 * class in this regard by creating peer MediumAttachment
11532 * objects for session machines and share the data with the peer
11533 * machine.
11534 */
11535 for (MediumAttachmentList::const_iterator
11536 it = mMediumAttachments->begin();
11537 it != mMediumAttachments->end();
11538 ++it)
11539 (*it)->i_updateParentMachine(mPeer);
11540
11541 /* attach new data to the primary machine and reshare it */
11542 mPeer->mMediumAttachments.attach(mMediumAttachments);
11543 }
11544
11545 return;
11546}
11547
11548/**
11549 * Perform deferred deletion of implicitly created diffs.
11550 *
11551 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11552 * changed (not backed up).
11553 *
11554 * @note Locks this object for writing!
11555 */
11556void Machine::i_rollbackMedia()
11557{
11558 AutoCaller autoCaller(this);
11559 AssertComRCReturnVoid(autoCaller.rc());
11560
11561 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11562 LogFlowThisFunc(("Entering rollbackMedia\n"));
11563
11564 HRESULT rc = S_OK;
11565
11566 /* no attach/detach operations -- nothing to do */
11567 if (!mMediumAttachments.isBackedUp())
11568 return;
11569
11570 /* enumerate new attachments */
11571 for (MediumAttachmentList::const_iterator
11572 it = mMediumAttachments->begin();
11573 it != mMediumAttachments->end();
11574 ++it)
11575 {
11576 MediumAttachment *pAttach = *it;
11577 /* Fix up the backrefs for DVD/floppy media. */
11578 if (pAttach->i_getType() != DeviceType_HardDisk)
11579 {
11580 Medium *pMedium = pAttach->i_getMedium();
11581 if (pMedium)
11582 {
11583 rc = pMedium->i_removeBackReference(mData->mUuid);
11584 AssertComRC(rc);
11585 }
11586 }
11587
11588 (*it)->i_rollback();
11589
11590 pAttach = *it;
11591 /* Fix up the backrefs for DVD/floppy media. */
11592 if (pAttach->i_getType() != DeviceType_HardDisk)
11593 {
11594 Medium *pMedium = pAttach->i_getMedium();
11595 if (pMedium)
11596 {
11597 rc = pMedium->i_addBackReference(mData->mUuid);
11598 AssertComRC(rc);
11599 }
11600 }
11601 }
11602
11603 /** @todo convert all this Machine-based voodoo to MediumAttachment
11604 * based rollback logic. */
11605 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11606
11607 return;
11608}
11609
11610/**
11611 * Returns true if the settings file is located in the directory named exactly
11612 * as the machine; this means, among other things, that the machine directory
11613 * should be auto-renamed.
11614 *
11615 * @param aSettingsDir if not NULL, the full machine settings file directory
11616 * name will be assigned there.
11617 *
11618 * @note Doesn't lock anything.
11619 * @note Not thread safe (must be called from this object's lock).
11620 */
11621bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11622{
11623 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11624 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11625 if (aSettingsDir)
11626 *aSettingsDir = strMachineDirName;
11627 strMachineDirName.stripPath(); // vmname
11628 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11629 strConfigFileOnly.stripPath() // vmname.vbox
11630 .stripSuffix(); // vmname
11631 /** @todo hack, make somehow use of ComposeMachineFilename */
11632 if (mUserData->s.fDirectoryIncludesUUID)
11633 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11634
11635 AssertReturn(!strMachineDirName.isEmpty(), false);
11636 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11637
11638 return strMachineDirName == strConfigFileOnly;
11639}
11640
11641/**
11642 * Discards all changes to machine settings.
11643 *
11644 * @param aNotify Whether to notify the direct session about changes or not.
11645 *
11646 * @note Locks objects for writing!
11647 */
11648void Machine::i_rollback(bool aNotify)
11649{
11650 AutoCaller autoCaller(this);
11651 AssertComRCReturn(autoCaller.rc(), (void)0);
11652
11653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11654
11655 if (!mStorageControllers.isNull())
11656 {
11657 if (mStorageControllers.isBackedUp())
11658 {
11659 /* unitialize all new devices (absent in the backed up list). */
11660 StorageControllerList *backedList = mStorageControllers.backedUpData();
11661 for (StorageControllerList::const_iterator
11662 it = mStorageControllers->begin();
11663 it != mStorageControllers->end();
11664 ++it)
11665 {
11666 if ( std::find(backedList->begin(), backedList->end(), *it)
11667 == backedList->end()
11668 )
11669 {
11670 (*it)->uninit();
11671 }
11672 }
11673
11674 /* restore the list */
11675 mStorageControllers.rollback();
11676 }
11677
11678 /* rollback any changes to devices after restoring the list */
11679 if (mData->flModifications & IsModified_Storage)
11680 {
11681 for (StorageControllerList::const_iterator
11682 it = mStorageControllers->begin();
11683 it != mStorageControllers->end();
11684 ++it)
11685 {
11686 (*it)->i_rollback();
11687 }
11688 }
11689 }
11690
11691 if (!mUSBControllers.isNull())
11692 {
11693 if (mUSBControllers.isBackedUp())
11694 {
11695 /* unitialize all new devices (absent in the backed up list). */
11696 USBControllerList *backedList = mUSBControllers.backedUpData();
11697 for (USBControllerList::const_iterator
11698 it = mUSBControllers->begin();
11699 it != mUSBControllers->end();
11700 ++it)
11701 {
11702 if ( std::find(backedList->begin(), backedList->end(), *it)
11703 == backedList->end()
11704 )
11705 {
11706 (*it)->uninit();
11707 }
11708 }
11709
11710 /* restore the list */
11711 mUSBControllers.rollback();
11712 }
11713
11714 /* rollback any changes to devices after restoring the list */
11715 if (mData->flModifications & IsModified_USB)
11716 {
11717 for (USBControllerList::const_iterator
11718 it = mUSBControllers->begin();
11719 it != mUSBControllers->end();
11720 ++it)
11721 {
11722 (*it)->i_rollback();
11723 }
11724 }
11725 }
11726
11727 mUserData.rollback();
11728
11729 mHWData.rollback();
11730
11731 if (mData->flModifications & IsModified_Storage)
11732 i_rollbackMedia();
11733
11734 if (mBIOSSettings)
11735 mBIOSSettings->i_rollback();
11736
11737 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11738 mRecordingSettings->i_rollback();
11739
11740 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11741 mVRDEServer->i_rollback();
11742
11743 if (mAudioAdapter)
11744 mAudioAdapter->i_rollback();
11745
11746 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11747 mUSBDeviceFilters->i_rollback();
11748
11749 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11750 mBandwidthControl->i_rollback();
11751
11752 if (!mHWData.isNull())
11753 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11754 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11755 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11756 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11757
11758 if (mData->flModifications & IsModified_NetworkAdapters)
11759 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11760 if ( mNetworkAdapters[slot]
11761 && mNetworkAdapters[slot]->i_isModified())
11762 {
11763 mNetworkAdapters[slot]->i_rollback();
11764 networkAdapters[slot] = mNetworkAdapters[slot];
11765 }
11766
11767 if (mData->flModifications & IsModified_SerialPorts)
11768 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11769 if ( mSerialPorts[slot]
11770 && mSerialPorts[slot]->i_isModified())
11771 {
11772 mSerialPorts[slot]->i_rollback();
11773 serialPorts[slot] = mSerialPorts[slot];
11774 }
11775
11776 if (mData->flModifications & IsModified_ParallelPorts)
11777 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11778 if ( mParallelPorts[slot]
11779 && mParallelPorts[slot]->i_isModified())
11780 {
11781 mParallelPorts[slot]->i_rollback();
11782 parallelPorts[slot] = mParallelPorts[slot];
11783 }
11784
11785 if (aNotify)
11786 {
11787 /* inform the direct session about changes */
11788
11789 ComObjPtr<Machine> that = this;
11790 uint32_t flModifications = mData->flModifications;
11791 alock.release();
11792
11793 if (flModifications & IsModified_SharedFolders)
11794 that->i_onSharedFolderChange();
11795
11796 if (flModifications & IsModified_VRDEServer)
11797 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11798 if (flModifications & IsModified_USB)
11799 that->i_onUSBControllerChange();
11800
11801 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11802 if (networkAdapters[slot])
11803 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11804 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11805 if (serialPorts[slot])
11806 that->i_onSerialPortChange(serialPorts[slot]);
11807 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11808 if (parallelPorts[slot])
11809 that->i_onParallelPortChange(parallelPorts[slot]);
11810
11811 if (flModifications & IsModified_Storage)
11812 that->i_onStorageControllerChange();
11813
11814#if 0
11815 if (flModifications & IsModified_BandwidthControl)
11816 that->onBandwidthControlChange();
11817#endif
11818 }
11819}
11820
11821/**
11822 * Commits all the changes to machine settings.
11823 *
11824 * Note that this operation is supposed to never fail.
11825 *
11826 * @note Locks this object and children for writing.
11827 */
11828void Machine::i_commit()
11829{
11830 AutoCaller autoCaller(this);
11831 AssertComRCReturnVoid(autoCaller.rc());
11832
11833 AutoCaller peerCaller(mPeer);
11834 AssertComRCReturnVoid(peerCaller.rc());
11835
11836 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11837
11838 /*
11839 * use safe commit to ensure Snapshot machines (that share mUserData)
11840 * will still refer to a valid memory location
11841 */
11842 mUserData.commitCopy();
11843
11844 mHWData.commit();
11845
11846 if (mMediumAttachments.isBackedUp())
11847 i_commitMedia(Global::IsOnline(mData->mMachineState));
11848
11849 mBIOSSettings->i_commit();
11850 mRecordingSettings->i_commit();
11851 mVRDEServer->i_commit();
11852 mAudioAdapter->i_commit();
11853 mUSBDeviceFilters->i_commit();
11854 mBandwidthControl->i_commit();
11855
11856 /* Since mNetworkAdapters is a list which might have been changed (resized)
11857 * without using the Backupable<> template we need to handle the copying
11858 * of the list entries manually, including the creation of peers for the
11859 * new objects. */
11860 bool commitNetworkAdapters = false;
11861 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11862 if (mPeer)
11863 {
11864 /* commit everything, even the ones which will go away */
11865 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11866 mNetworkAdapters[slot]->i_commit();
11867 /* copy over the new entries, creating a peer and uninit the original */
11868 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11869 for (size_t slot = 0; slot < newSize; slot++)
11870 {
11871 /* look if this adapter has a peer device */
11872 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11873 if (!peer)
11874 {
11875 /* no peer means the adapter is a newly created one;
11876 * create a peer owning data this data share it with */
11877 peer.createObject();
11878 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11879 }
11880 mPeer->mNetworkAdapters[slot] = peer;
11881 }
11882 /* uninit any no longer needed network adapters */
11883 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11884 mNetworkAdapters[slot]->uninit();
11885 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11886 {
11887 if (mPeer->mNetworkAdapters[slot])
11888 mPeer->mNetworkAdapters[slot]->uninit();
11889 }
11890 /* Keep the original network adapter count until this point, so that
11891 * discarding a chipset type change will not lose settings. */
11892 mNetworkAdapters.resize(newSize);
11893 mPeer->mNetworkAdapters.resize(newSize);
11894 }
11895 else
11896 {
11897 /* we have no peer (our parent is the newly created machine);
11898 * just commit changes to the network adapters */
11899 commitNetworkAdapters = true;
11900 }
11901 if (commitNetworkAdapters)
11902 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11903 mNetworkAdapters[slot]->i_commit();
11904
11905 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11906 mSerialPorts[slot]->i_commit();
11907 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11908 mParallelPorts[slot]->i_commit();
11909
11910 bool commitStorageControllers = false;
11911
11912 if (mStorageControllers.isBackedUp())
11913 {
11914 mStorageControllers.commit();
11915
11916 if (mPeer)
11917 {
11918 /* Commit all changes to new controllers (this will reshare data with
11919 * peers for those who have peers) */
11920 StorageControllerList *newList = new StorageControllerList();
11921 for (StorageControllerList::const_iterator
11922 it = mStorageControllers->begin();
11923 it != mStorageControllers->end();
11924 ++it)
11925 {
11926 (*it)->i_commit();
11927
11928 /* look if this controller has a peer device */
11929 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11930 if (!peer)
11931 {
11932 /* no peer means the device is a newly created one;
11933 * create a peer owning data this device share it with */
11934 peer.createObject();
11935 peer->init(mPeer, *it, true /* aReshare */);
11936 }
11937 else
11938 {
11939 /* remove peer from the old list */
11940 mPeer->mStorageControllers->remove(peer);
11941 }
11942 /* and add it to the new list */
11943 newList->push_back(peer);
11944 }
11945
11946 /* uninit old peer's controllers that are left */
11947 for (StorageControllerList::const_iterator
11948 it = mPeer->mStorageControllers->begin();
11949 it != mPeer->mStorageControllers->end();
11950 ++it)
11951 {
11952 (*it)->uninit();
11953 }
11954
11955 /* attach new list of controllers to our peer */
11956 mPeer->mStorageControllers.attach(newList);
11957 }
11958 else
11959 {
11960 /* we have no peer (our parent is the newly created machine);
11961 * just commit changes to devices */
11962 commitStorageControllers = true;
11963 }
11964 }
11965 else
11966 {
11967 /* the list of controllers itself is not changed,
11968 * just commit changes to controllers themselves */
11969 commitStorageControllers = true;
11970 }
11971
11972 if (commitStorageControllers)
11973 {
11974 for (StorageControllerList::const_iterator
11975 it = mStorageControllers->begin();
11976 it != mStorageControllers->end();
11977 ++it)
11978 {
11979 (*it)->i_commit();
11980 }
11981 }
11982
11983 bool commitUSBControllers = false;
11984
11985 if (mUSBControllers.isBackedUp())
11986 {
11987 mUSBControllers.commit();
11988
11989 if (mPeer)
11990 {
11991 /* Commit all changes to new controllers (this will reshare data with
11992 * peers for those who have peers) */
11993 USBControllerList *newList = new USBControllerList();
11994 for (USBControllerList::const_iterator
11995 it = mUSBControllers->begin();
11996 it != mUSBControllers->end();
11997 ++it)
11998 {
11999 (*it)->i_commit();
12000
12001 /* look if this controller has a peer device */
12002 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12003 if (!peer)
12004 {
12005 /* no peer means the device is a newly created one;
12006 * create a peer owning data this device share it with */
12007 peer.createObject();
12008 peer->init(mPeer, *it, true /* aReshare */);
12009 }
12010 else
12011 {
12012 /* remove peer from the old list */
12013 mPeer->mUSBControllers->remove(peer);
12014 }
12015 /* and add it to the new list */
12016 newList->push_back(peer);
12017 }
12018
12019 /* uninit old peer's controllers that are left */
12020 for (USBControllerList::const_iterator
12021 it = mPeer->mUSBControllers->begin();
12022 it != mPeer->mUSBControllers->end();
12023 ++it)
12024 {
12025 (*it)->uninit();
12026 }
12027
12028 /* attach new list of controllers to our peer */
12029 mPeer->mUSBControllers.attach(newList);
12030 }
12031 else
12032 {
12033 /* we have no peer (our parent is the newly created machine);
12034 * just commit changes to devices */
12035 commitUSBControllers = true;
12036 }
12037 }
12038 else
12039 {
12040 /* the list of controllers itself is not changed,
12041 * just commit changes to controllers themselves */
12042 commitUSBControllers = true;
12043 }
12044
12045 if (commitUSBControllers)
12046 {
12047 for (USBControllerList::const_iterator
12048 it = mUSBControllers->begin();
12049 it != mUSBControllers->end();
12050 ++it)
12051 {
12052 (*it)->i_commit();
12053 }
12054 }
12055
12056 if (i_isSessionMachine())
12057 {
12058 /* attach new data to the primary machine and reshare it */
12059 mPeer->mUserData.attach(mUserData);
12060 mPeer->mHWData.attach(mHWData);
12061 /* mmMediumAttachments is reshared by fixupMedia */
12062 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12063 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12064 }
12065}
12066
12067/**
12068 * Copies all the hardware data from the given machine.
12069 *
12070 * Currently, only called when the VM is being restored from a snapshot. In
12071 * particular, this implies that the VM is not running during this method's
12072 * call.
12073 *
12074 * @note This method must be called from under this object's lock.
12075 *
12076 * @note This method doesn't call #i_commit(), so all data remains backed up and
12077 * unsaved.
12078 */
12079void Machine::i_copyFrom(Machine *aThat)
12080{
12081 AssertReturnVoid(!i_isSnapshotMachine());
12082 AssertReturnVoid(aThat->i_isSnapshotMachine());
12083
12084 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12085
12086 mHWData.assignCopy(aThat->mHWData);
12087
12088 // create copies of all shared folders (mHWData after attaching a copy
12089 // contains just references to original objects)
12090 for (HWData::SharedFolderList::iterator
12091 it = mHWData->mSharedFolders.begin();
12092 it != mHWData->mSharedFolders.end();
12093 ++it)
12094 {
12095 ComObjPtr<SharedFolder> folder;
12096 folder.createObject();
12097 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12098 AssertComRC(rc);
12099 *it = folder;
12100 }
12101
12102 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12103 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12104 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12105 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12106 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12107 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12108
12109 /* create private copies of all controllers */
12110 mStorageControllers.backup();
12111 mStorageControllers->clear();
12112 for (StorageControllerList::const_iterator
12113 it = aThat->mStorageControllers->begin();
12114 it != aThat->mStorageControllers->end();
12115 ++it)
12116 {
12117 ComObjPtr<StorageController> ctrl;
12118 ctrl.createObject();
12119 ctrl->initCopy(this, *it);
12120 mStorageControllers->push_back(ctrl);
12121 }
12122
12123 /* create private copies of all USB controllers */
12124 mUSBControllers.backup();
12125 mUSBControllers->clear();
12126 for (USBControllerList::const_iterator
12127 it = aThat->mUSBControllers->begin();
12128 it != aThat->mUSBControllers->end();
12129 ++it)
12130 {
12131 ComObjPtr<USBController> ctrl;
12132 ctrl.createObject();
12133 ctrl->initCopy(this, *it);
12134 mUSBControllers->push_back(ctrl);
12135 }
12136
12137 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12138 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12139 {
12140 if (mNetworkAdapters[slot].isNotNull())
12141 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12142 else
12143 {
12144 unconst(mNetworkAdapters[slot]).createObject();
12145 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12146 }
12147 }
12148 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12149 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12150 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12151 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12152}
12153
12154/**
12155 * Returns whether the given storage controller is hotplug capable.
12156 *
12157 * @returns true if the controller supports hotplugging
12158 * false otherwise.
12159 * @param enmCtrlType The controller type to check for.
12160 */
12161bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12162{
12163 ComPtr<ISystemProperties> systemProperties;
12164 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12165 if (FAILED(rc))
12166 return false;
12167
12168 BOOL aHotplugCapable = FALSE;
12169 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12170
12171 return RT_BOOL(aHotplugCapable);
12172}
12173
12174#ifdef VBOX_WITH_RESOURCE_USAGE_API
12175
12176void Machine::i_getDiskList(MediaList &list)
12177{
12178 for (MediumAttachmentList::const_iterator
12179 it = mMediumAttachments->begin();
12180 it != mMediumAttachments->end();
12181 ++it)
12182 {
12183 MediumAttachment *pAttach = *it;
12184 /* just in case */
12185 AssertContinue(pAttach);
12186
12187 AutoCaller localAutoCallerA(pAttach);
12188 if (FAILED(localAutoCallerA.rc())) continue;
12189
12190 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12191
12192 if (pAttach->i_getType() == DeviceType_HardDisk)
12193 list.push_back(pAttach->i_getMedium());
12194 }
12195}
12196
12197void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12198{
12199 AssertReturnVoid(isWriteLockOnCurrentThread());
12200 AssertPtrReturnVoid(aCollector);
12201
12202 pm::CollectorHAL *hal = aCollector->getHAL();
12203 /* Create sub metrics */
12204 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12205 "Percentage of processor time spent in user mode by the VM process.");
12206 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12207 "Percentage of processor time spent in kernel mode by the VM process.");
12208 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12209 "Size of resident portion of VM process in memory.");
12210 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12211 "Actual size of all VM disks combined.");
12212 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12213 "Network receive rate.");
12214 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12215 "Network transmit rate.");
12216 /* Create and register base metrics */
12217 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12218 cpuLoadUser, cpuLoadKernel);
12219 aCollector->registerBaseMetric(cpuLoad);
12220 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12221 ramUsageUsed);
12222 aCollector->registerBaseMetric(ramUsage);
12223 MediaList disks;
12224 i_getDiskList(disks);
12225 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12226 diskUsageUsed);
12227 aCollector->registerBaseMetric(diskUsage);
12228
12229 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12230 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12231 new pm::AggregateAvg()));
12232 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12233 new pm::AggregateMin()));
12234 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12235 new pm::AggregateMax()));
12236 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12237 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12238 new pm::AggregateAvg()));
12239 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12240 new pm::AggregateMin()));
12241 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12242 new pm::AggregateMax()));
12243
12244 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12245 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12246 new pm::AggregateAvg()));
12247 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12248 new pm::AggregateMin()));
12249 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12250 new pm::AggregateMax()));
12251
12252 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12253 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12254 new pm::AggregateAvg()));
12255 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12256 new pm::AggregateMin()));
12257 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12258 new pm::AggregateMax()));
12259
12260
12261 /* Guest metrics collector */
12262 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12263 aCollector->registerGuest(mCollectorGuest);
12264 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12265
12266 /* Create sub metrics */
12267 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12268 "Percentage of processor time spent in user mode as seen by the guest.");
12269 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12270 "Percentage of processor time spent in kernel mode as seen by the guest.");
12271 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12272 "Percentage of processor time spent idling as seen by the guest.");
12273
12274 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12275 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12276 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12277 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12278 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12279 pm::SubMetric *guestMemCache = new pm::SubMetric(
12280 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12281
12282 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12283 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12284
12285 /* Create and register base metrics */
12286 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12287 machineNetRx, machineNetTx);
12288 aCollector->registerBaseMetric(machineNetRate);
12289
12290 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12291 guestLoadUser, guestLoadKernel, guestLoadIdle);
12292 aCollector->registerBaseMetric(guestCpuLoad);
12293
12294 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12295 guestMemTotal, guestMemFree,
12296 guestMemBalloon, guestMemShared,
12297 guestMemCache, guestPagedTotal);
12298 aCollector->registerBaseMetric(guestCpuMem);
12299
12300 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12301 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12304
12305 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12306 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12307 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12308 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12309
12310 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12311 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12312 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12313 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12314
12315 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12316 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12317 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12318 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12319
12320 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12321 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12322 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12323 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12324
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12329
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12332 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12333 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12334
12335 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12336 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12338 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12339
12340 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12344
12345 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12346 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12347 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12348 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12349
12350 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12351 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12352 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12353 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12354}
12355
12356void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12357{
12358 AssertReturnVoid(isWriteLockOnCurrentThread());
12359
12360 if (aCollector)
12361 {
12362 aCollector->unregisterMetricsFor(aMachine);
12363 aCollector->unregisterBaseMetricsFor(aMachine);
12364 }
12365}
12366
12367#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12368
12369
12370////////////////////////////////////////////////////////////////////////////////
12371
12372DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12373
12374HRESULT SessionMachine::FinalConstruct()
12375{
12376 LogFlowThisFunc(("\n"));
12377
12378 mClientToken = NULL;
12379
12380 return BaseFinalConstruct();
12381}
12382
12383void SessionMachine::FinalRelease()
12384{
12385 LogFlowThisFunc(("\n"));
12386
12387 Assert(!mClientToken);
12388 /* paranoia, should not hang around any more */
12389 if (mClientToken)
12390 {
12391 delete mClientToken;
12392 mClientToken = NULL;
12393 }
12394
12395 uninit(Uninit::Unexpected);
12396
12397 BaseFinalRelease();
12398}
12399
12400/**
12401 * @note Must be called only by Machine::LockMachine() from its own write lock.
12402 */
12403HRESULT SessionMachine::init(Machine *aMachine)
12404{
12405 LogFlowThisFuncEnter();
12406 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12407
12408 AssertReturn(aMachine, E_INVALIDARG);
12409
12410 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12411
12412 /* Enclose the state transition NotReady->InInit->Ready */
12413 AutoInitSpan autoInitSpan(this);
12414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12415
12416 HRESULT rc = S_OK;
12417
12418 RT_ZERO(mAuthLibCtx);
12419
12420 /* create the machine client token */
12421 try
12422 {
12423 mClientToken = new ClientToken(aMachine, this);
12424 if (!mClientToken->isReady())
12425 {
12426 delete mClientToken;
12427 mClientToken = NULL;
12428 rc = E_FAIL;
12429 }
12430 }
12431 catch (std::bad_alloc &)
12432 {
12433 rc = E_OUTOFMEMORY;
12434 }
12435 if (FAILED(rc))
12436 return rc;
12437
12438 /* memorize the peer Machine */
12439 unconst(mPeer) = aMachine;
12440 /* share the parent pointer */
12441 unconst(mParent) = aMachine->mParent;
12442
12443 /* take the pointers to data to share */
12444 mData.share(aMachine->mData);
12445 mSSData.share(aMachine->mSSData);
12446
12447 mUserData.share(aMachine->mUserData);
12448 mHWData.share(aMachine->mHWData);
12449 mMediumAttachments.share(aMachine->mMediumAttachments);
12450
12451 mStorageControllers.allocate();
12452 for (StorageControllerList::const_iterator
12453 it = aMachine->mStorageControllers->begin();
12454 it != aMachine->mStorageControllers->end();
12455 ++it)
12456 {
12457 ComObjPtr<StorageController> ctl;
12458 ctl.createObject();
12459 ctl->init(this, *it);
12460 mStorageControllers->push_back(ctl);
12461 }
12462
12463 mUSBControllers.allocate();
12464 for (USBControllerList::const_iterator
12465 it = aMachine->mUSBControllers->begin();
12466 it != aMachine->mUSBControllers->end();
12467 ++it)
12468 {
12469 ComObjPtr<USBController> ctl;
12470 ctl.createObject();
12471 ctl->init(this, *it);
12472 mUSBControllers->push_back(ctl);
12473 }
12474
12475 unconst(mBIOSSettings).createObject();
12476 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12477 unconst(mRecordingSettings).createObject();
12478 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12479 /* create another VRDEServer object that will be mutable */
12480 unconst(mVRDEServer).createObject();
12481 mVRDEServer->init(this, aMachine->mVRDEServer);
12482 /* create another audio adapter object that will be mutable */
12483 unconst(mAudioAdapter).createObject();
12484 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12485 /* create a list of serial ports that will be mutable */
12486 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12487 {
12488 unconst(mSerialPorts[slot]).createObject();
12489 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12490 }
12491 /* create a list of parallel ports that will be mutable */
12492 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12493 {
12494 unconst(mParallelPorts[slot]).createObject();
12495 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12496 }
12497
12498 /* create another USB device filters object that will be mutable */
12499 unconst(mUSBDeviceFilters).createObject();
12500 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12501
12502 /* create a list of network adapters that will be mutable */
12503 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12504 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12505 {
12506 unconst(mNetworkAdapters[slot]).createObject();
12507 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12508 }
12509
12510 /* create another bandwidth control object that will be mutable */
12511 unconst(mBandwidthControl).createObject();
12512 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12513
12514 /* default is to delete saved state on Saved -> PoweredOff transition */
12515 mRemoveSavedState = true;
12516
12517 /* Confirm a successful initialization when it's the case */
12518 autoInitSpan.setSucceeded();
12519
12520 miNATNetworksStarted = 0;
12521
12522 LogFlowThisFuncLeave();
12523 return rc;
12524}
12525
12526/**
12527 * Uninitializes this session object. If the reason is other than
12528 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12529 * or the client watcher code.
12530 *
12531 * @param aReason uninitialization reason
12532 *
12533 * @note Locks mParent + this object for writing.
12534 */
12535void SessionMachine::uninit(Uninit::Reason aReason)
12536{
12537 LogFlowThisFuncEnter();
12538 LogFlowThisFunc(("reason=%d\n", aReason));
12539
12540 /*
12541 * Strongly reference ourselves to prevent this object deletion after
12542 * mData->mSession.mMachine.setNull() below (which can release the last
12543 * reference and call the destructor). Important: this must be done before
12544 * accessing any members (and before AutoUninitSpan that does it as well).
12545 * This self reference will be released as the very last step on return.
12546 */
12547 ComObjPtr<SessionMachine> selfRef;
12548 if (aReason != Uninit::Unexpected)
12549 selfRef = this;
12550
12551 /* Enclose the state transition Ready->InUninit->NotReady */
12552 AutoUninitSpan autoUninitSpan(this);
12553 if (autoUninitSpan.uninitDone())
12554 {
12555 LogFlowThisFunc(("Already uninitialized\n"));
12556 LogFlowThisFuncLeave();
12557 return;
12558 }
12559
12560 if (autoUninitSpan.initFailed())
12561 {
12562 /* We've been called by init() because it's failed. It's not really
12563 * necessary (nor it's safe) to perform the regular uninit sequence
12564 * below, the following is enough.
12565 */
12566 LogFlowThisFunc(("Initialization failed.\n"));
12567 /* destroy the machine client token */
12568 if (mClientToken)
12569 {
12570 delete mClientToken;
12571 mClientToken = NULL;
12572 }
12573 uninitDataAndChildObjects();
12574 mData.free();
12575 unconst(mParent) = NULL;
12576 unconst(mPeer) = NULL;
12577 LogFlowThisFuncLeave();
12578 return;
12579 }
12580
12581 MachineState_T lastState;
12582 {
12583 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12584 lastState = mData->mMachineState;
12585 }
12586 NOREF(lastState);
12587
12588#ifdef VBOX_WITH_USB
12589 // release all captured USB devices, but do this before requesting the locks below
12590 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12591 {
12592 /* Console::captureUSBDevices() is called in the VM process only after
12593 * setting the machine state to Starting or Restoring.
12594 * Console::detachAllUSBDevices() will be called upon successful
12595 * termination. So, we need to release USB devices only if there was
12596 * an abnormal termination of a running VM.
12597 *
12598 * This is identical to SessionMachine::DetachAllUSBDevices except
12599 * for the aAbnormal argument. */
12600 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12601 AssertComRC(rc);
12602 NOREF(rc);
12603
12604 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12605 if (service)
12606 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12607 }
12608#endif /* VBOX_WITH_USB */
12609
12610 // we need to lock this object in uninit() because the lock is shared
12611 // with mPeer (as well as data we modify below). mParent lock is needed
12612 // by several calls to it.
12613 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12614
12615#ifdef VBOX_WITH_RESOURCE_USAGE_API
12616 /*
12617 * It is safe to call Machine::i_unregisterMetrics() here because
12618 * PerformanceCollector::samplerCallback no longer accesses guest methods
12619 * holding the lock.
12620 */
12621 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12622 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12623 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12624 if (mCollectorGuest)
12625 {
12626 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12627 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12628 mCollectorGuest = NULL;
12629 }
12630#endif
12631
12632 if (aReason == Uninit::Abnormal)
12633 {
12634 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12635
12636 /* reset the state to Aborted */
12637 if (mData->mMachineState != MachineState_Aborted)
12638 i_setMachineState(MachineState_Aborted);
12639 }
12640
12641 // any machine settings modified?
12642 if (mData->flModifications)
12643 {
12644 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12645 i_rollback(false /* aNotify */);
12646 }
12647
12648 mData->mSession.mPID = NIL_RTPROCESS;
12649
12650 if (aReason == Uninit::Unexpected)
12651 {
12652 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12653 * client watcher thread to update the set of machines that have open
12654 * sessions. */
12655 mParent->i_updateClientWatcher();
12656 }
12657
12658 /* uninitialize all remote controls */
12659 if (mData->mSession.mRemoteControls.size())
12660 {
12661 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12662 mData->mSession.mRemoteControls.size()));
12663
12664 /* Always restart a the beginning, since the iterator is invalidated
12665 * by using erase(). */
12666 for (Data::Session::RemoteControlList::iterator
12667 it = mData->mSession.mRemoteControls.begin();
12668 it != mData->mSession.mRemoteControls.end();
12669 it = mData->mSession.mRemoteControls.begin())
12670 {
12671 ComPtr<IInternalSessionControl> pControl = *it;
12672 mData->mSession.mRemoteControls.erase(it);
12673 multilock.release();
12674 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12675 HRESULT rc = pControl->Uninitialize();
12676 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12677 if (FAILED(rc))
12678 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12679 multilock.acquire();
12680 }
12681 mData->mSession.mRemoteControls.clear();
12682 }
12683
12684 /* Remove all references to the NAT network service. The service will stop
12685 * if all references (also from other VMs) are removed. */
12686 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12687 {
12688 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12689 {
12690 BOOL enabled;
12691 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12692 if ( FAILED(hrc)
12693 || !enabled)
12694 continue;
12695
12696 NetworkAttachmentType_T type;
12697 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12698 if ( SUCCEEDED(hrc)
12699 && type == NetworkAttachmentType_NATNetwork)
12700 {
12701 Bstr name;
12702 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12703 if (SUCCEEDED(hrc))
12704 {
12705 multilock.release();
12706 Utf8Str strName(name);
12707 LogRel(("VM '%s' stops using NAT network '%s'\n",
12708 mUserData->s.strName.c_str(), strName.c_str()));
12709 mParent->i_natNetworkRefDec(strName);
12710 multilock.acquire();
12711 }
12712 }
12713 }
12714 }
12715
12716 /*
12717 * An expected uninitialization can come only from #i_checkForDeath().
12718 * Otherwise it means that something's gone really wrong (for example,
12719 * the Session implementation has released the VirtualBox reference
12720 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12721 * etc). However, it's also possible, that the client releases the IPC
12722 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12723 * but the VirtualBox release event comes first to the server process.
12724 * This case is practically possible, so we should not assert on an
12725 * unexpected uninit, just log a warning.
12726 */
12727
12728 if (aReason == Uninit::Unexpected)
12729 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12730
12731 if (aReason != Uninit::Normal)
12732 {
12733 mData->mSession.mDirectControl.setNull();
12734 }
12735 else
12736 {
12737 /* this must be null here (see #OnSessionEnd()) */
12738 Assert(mData->mSession.mDirectControl.isNull());
12739 Assert(mData->mSession.mState == SessionState_Unlocking);
12740 Assert(!mData->mSession.mProgress.isNull());
12741 }
12742 if (mData->mSession.mProgress)
12743 {
12744 if (aReason == Uninit::Normal)
12745 mData->mSession.mProgress->i_notifyComplete(S_OK);
12746 else
12747 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12748 COM_IIDOF(ISession),
12749 getComponentName(),
12750 tr("The VM session was aborted"));
12751 mData->mSession.mProgress.setNull();
12752 }
12753
12754 if (mConsoleTaskData.mProgress)
12755 {
12756 Assert(aReason == Uninit::Abnormal);
12757 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12758 COM_IIDOF(ISession),
12759 getComponentName(),
12760 tr("The VM session was aborted"));
12761 mConsoleTaskData.mProgress.setNull();
12762 }
12763
12764 /* remove the association between the peer machine and this session machine */
12765 Assert( (SessionMachine*)mData->mSession.mMachine == this
12766 || aReason == Uninit::Unexpected);
12767
12768 /* reset the rest of session data */
12769 mData->mSession.mLockType = LockType_Null;
12770 mData->mSession.mMachine.setNull();
12771 mData->mSession.mState = SessionState_Unlocked;
12772 mData->mSession.mName.setNull();
12773
12774 /* destroy the machine client token before leaving the exclusive lock */
12775 if (mClientToken)
12776 {
12777 delete mClientToken;
12778 mClientToken = NULL;
12779 }
12780
12781 /* fire an event */
12782 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12783
12784 uninitDataAndChildObjects();
12785
12786 /* free the essential data structure last */
12787 mData.free();
12788
12789 /* release the exclusive lock before setting the below two to NULL */
12790 multilock.release();
12791
12792 unconst(mParent) = NULL;
12793 unconst(mPeer) = NULL;
12794
12795 AuthLibUnload(&mAuthLibCtx);
12796
12797 LogFlowThisFuncLeave();
12798}
12799
12800// util::Lockable interface
12801////////////////////////////////////////////////////////////////////////////////
12802
12803/**
12804 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12805 * with the primary Machine instance (mPeer).
12806 */
12807RWLockHandle *SessionMachine::lockHandle() const
12808{
12809 AssertReturn(mPeer != NULL, NULL);
12810 return mPeer->lockHandle();
12811}
12812
12813// IInternalMachineControl methods
12814////////////////////////////////////////////////////////////////////////////////
12815
12816/**
12817 * Passes collected guest statistics to performance collector object
12818 */
12819HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12820 ULONG aCpuKernel, ULONG aCpuIdle,
12821 ULONG aMemTotal, ULONG aMemFree,
12822 ULONG aMemBalloon, ULONG aMemShared,
12823 ULONG aMemCache, ULONG aPageTotal,
12824 ULONG aAllocVMM, ULONG aFreeVMM,
12825 ULONG aBalloonedVMM, ULONG aSharedVMM,
12826 ULONG aVmNetRx, ULONG aVmNetTx)
12827{
12828#ifdef VBOX_WITH_RESOURCE_USAGE_API
12829 if (mCollectorGuest)
12830 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12831 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12832 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12833 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12834
12835 return S_OK;
12836#else
12837 NOREF(aValidStats);
12838 NOREF(aCpuUser);
12839 NOREF(aCpuKernel);
12840 NOREF(aCpuIdle);
12841 NOREF(aMemTotal);
12842 NOREF(aMemFree);
12843 NOREF(aMemBalloon);
12844 NOREF(aMemShared);
12845 NOREF(aMemCache);
12846 NOREF(aPageTotal);
12847 NOREF(aAllocVMM);
12848 NOREF(aFreeVMM);
12849 NOREF(aBalloonedVMM);
12850 NOREF(aSharedVMM);
12851 NOREF(aVmNetRx);
12852 NOREF(aVmNetTx);
12853 return E_NOTIMPL;
12854#endif
12855}
12856
12857////////////////////////////////////////////////////////////////////////////////
12858//
12859// SessionMachine task records
12860//
12861////////////////////////////////////////////////////////////////////////////////
12862
12863/**
12864 * Task record for saving the machine state.
12865 */
12866class SessionMachine::SaveStateTask
12867 : public Machine::Task
12868{
12869public:
12870 SaveStateTask(SessionMachine *m,
12871 Progress *p,
12872 const Utf8Str &t,
12873 Reason_T enmReason,
12874 const Utf8Str &strStateFilePath)
12875 : Task(m, p, t),
12876 m_enmReason(enmReason),
12877 m_strStateFilePath(strStateFilePath)
12878 {}
12879
12880private:
12881 void handler()
12882 {
12883 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12884 }
12885
12886 Reason_T m_enmReason;
12887 Utf8Str m_strStateFilePath;
12888
12889 friend class SessionMachine;
12890};
12891
12892/**
12893 * Task thread implementation for SessionMachine::SaveState(), called from
12894 * SessionMachine::taskHandler().
12895 *
12896 * @note Locks this object for writing.
12897 *
12898 * @param task
12899 * @return
12900 */
12901void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12902{
12903 LogFlowThisFuncEnter();
12904
12905 AutoCaller autoCaller(this);
12906 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12907 if (FAILED(autoCaller.rc()))
12908 {
12909 /* we might have been uninitialized because the session was accidentally
12910 * closed by the client, so don't assert */
12911 HRESULT rc = setError(E_FAIL,
12912 tr("The session has been accidentally closed"));
12913 task.m_pProgress->i_notifyComplete(rc);
12914 LogFlowThisFuncLeave();
12915 return;
12916 }
12917
12918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12919
12920 HRESULT rc = S_OK;
12921
12922 try
12923 {
12924 ComPtr<IInternalSessionControl> directControl;
12925 if (mData->mSession.mLockType == LockType_VM)
12926 directControl = mData->mSession.mDirectControl;
12927 if (directControl.isNull())
12928 throw setError(VBOX_E_INVALID_VM_STATE,
12929 tr("Trying to save state without a running VM"));
12930 alock.release();
12931 BOOL fSuspendedBySave;
12932 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12933 Assert(!fSuspendedBySave);
12934 alock.acquire();
12935
12936 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12937 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12938 throw E_FAIL);
12939
12940 if (SUCCEEDED(rc))
12941 {
12942 mSSData->strStateFilePath = task.m_strStateFilePath;
12943
12944 /* save all VM settings */
12945 rc = i_saveSettings(NULL);
12946 // no need to check whether VirtualBox.xml needs saving also since
12947 // we can't have a name change pending at this point
12948 }
12949 else
12950 {
12951 // On failure, set the state to the state we had at the beginning.
12952 i_setMachineState(task.m_machineStateBackup);
12953 i_updateMachineStateOnClient();
12954
12955 // Delete the saved state file (might have been already created).
12956 // No need to check whether this is shared with a snapshot here
12957 // because we certainly created a fresh saved state file here.
12958 RTFileDelete(task.m_strStateFilePath.c_str());
12959 }
12960 }
12961 catch (HRESULT aRC) { rc = aRC; }
12962
12963 task.m_pProgress->i_notifyComplete(rc);
12964
12965 LogFlowThisFuncLeave();
12966}
12967
12968/**
12969 * @note Locks this object for writing.
12970 */
12971HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12972{
12973 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12974}
12975
12976HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12977{
12978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12979
12980 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12981 if (FAILED(rc)) return rc;
12982
12983 if ( mData->mMachineState != MachineState_Running
12984 && mData->mMachineState != MachineState_Paused
12985 )
12986 return setError(VBOX_E_INVALID_VM_STATE,
12987 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12988 Global::stringifyMachineState(mData->mMachineState));
12989
12990 ComObjPtr<Progress> pProgress;
12991 pProgress.createObject();
12992 rc = pProgress->init(i_getVirtualBox(),
12993 static_cast<IMachine *>(this) /* aInitiator */,
12994 tr("Saving the execution state of the virtual machine"),
12995 FALSE /* aCancelable */);
12996 if (FAILED(rc))
12997 return rc;
12998
12999 Utf8Str strStateFilePath;
13000 i_composeSavedStateFilename(strStateFilePath);
13001
13002 /* create and start the task on a separate thread (note that it will not
13003 * start working until we release alock) */
13004 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13005 rc = pTask->createThread();
13006 if (FAILED(rc))
13007 return rc;
13008
13009 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13010 i_setMachineState(MachineState_Saving);
13011 i_updateMachineStateOnClient();
13012
13013 pProgress.queryInterfaceTo(aProgress.asOutParam());
13014
13015 return S_OK;
13016}
13017
13018/**
13019 * @note Locks this object for writing.
13020 */
13021HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13022{
13023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13024
13025 HRESULT rc = i_checkStateDependency(MutableStateDep);
13026 if (FAILED(rc)) return rc;
13027
13028 if ( mData->mMachineState != MachineState_PoweredOff
13029 && mData->mMachineState != MachineState_Teleported
13030 && mData->mMachineState != MachineState_Aborted
13031 )
13032 return setError(VBOX_E_INVALID_VM_STATE,
13033 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13034 Global::stringifyMachineState(mData->mMachineState));
13035
13036 com::Utf8Str stateFilePathFull;
13037 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13038 if (RT_FAILURE(vrc))
13039 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13040 tr("Invalid saved state file path '%s' (%Rrc)"),
13041 aSavedStateFile.c_str(),
13042 vrc);
13043
13044 mSSData->strStateFilePath = stateFilePathFull;
13045
13046 /* The below i_setMachineState() will detect the state transition and will
13047 * update the settings file */
13048
13049 return i_setMachineState(MachineState_Saved);
13050}
13051
13052/**
13053 * @note Locks this object for writing.
13054 */
13055HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13056{
13057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13058
13059 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13060 if (FAILED(rc)) return rc;
13061
13062 if (mData->mMachineState != MachineState_Saved)
13063 return setError(VBOX_E_INVALID_VM_STATE,
13064 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13065 Global::stringifyMachineState(mData->mMachineState));
13066
13067 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13068
13069 /*
13070 * Saved -> PoweredOff transition will be detected in the SessionMachine
13071 * and properly handled.
13072 */
13073 rc = i_setMachineState(MachineState_PoweredOff);
13074 return rc;
13075}
13076
13077
13078/**
13079 * @note Locks the same as #i_setMachineState() does.
13080 */
13081HRESULT SessionMachine::updateState(MachineState_T aState)
13082{
13083 return i_setMachineState(aState);
13084}
13085
13086/**
13087 * @note Locks this object for writing.
13088 */
13089HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13090{
13091 IProgress *pProgress(aProgress);
13092
13093 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13094
13095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13096
13097 if (mData->mSession.mState != SessionState_Locked)
13098 return VBOX_E_INVALID_OBJECT_STATE;
13099
13100 if (!mData->mSession.mProgress.isNull())
13101 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13102
13103 /* If we didn't reference the NAT network service yet, add a reference to
13104 * force a start */
13105 if (miNATNetworksStarted < 1)
13106 {
13107 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13108 {
13109 BOOL enabled;
13110 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13111 if ( FAILED(hrc)
13112 || !enabled)
13113 continue;
13114
13115 NetworkAttachmentType_T type;
13116 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13117 if ( SUCCEEDED(hrc)
13118 && type == NetworkAttachmentType_NATNetwork)
13119 {
13120 Bstr name;
13121 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13122 if (SUCCEEDED(hrc))
13123 {
13124 Utf8Str strName(name);
13125 LogRel(("VM '%s' starts using NAT network '%s'\n",
13126 mUserData->s.strName.c_str(), strName.c_str()));
13127 mPeer->lockHandle()->unlockWrite();
13128 mParent->i_natNetworkRefInc(strName);
13129#ifdef RT_LOCK_STRICT
13130 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13131#else
13132 mPeer->lockHandle()->lockWrite();
13133#endif
13134 }
13135 }
13136 }
13137 miNATNetworksStarted++;
13138 }
13139
13140 LogFlowThisFunc(("returns S_OK.\n"));
13141 return S_OK;
13142}
13143
13144/**
13145 * @note Locks this object for writing.
13146 */
13147HRESULT SessionMachine::endPowerUp(LONG aResult)
13148{
13149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13150
13151 if (mData->mSession.mState != SessionState_Locked)
13152 return VBOX_E_INVALID_OBJECT_STATE;
13153
13154 /* Finalize the LaunchVMProcess progress object. */
13155 if (mData->mSession.mProgress)
13156 {
13157 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13158 mData->mSession.mProgress.setNull();
13159 }
13160
13161 if (SUCCEEDED((HRESULT)aResult))
13162 {
13163#ifdef VBOX_WITH_RESOURCE_USAGE_API
13164 /* The VM has been powered up successfully, so it makes sense
13165 * now to offer the performance metrics for a running machine
13166 * object. Doing it earlier wouldn't be safe. */
13167 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13168 mData->mSession.mPID);
13169#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13170 }
13171
13172 return S_OK;
13173}
13174
13175/**
13176 * @note Locks this object for writing.
13177 */
13178HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13179{
13180 LogFlowThisFuncEnter();
13181
13182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13183
13184 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13185 E_FAIL);
13186
13187 /* create a progress object to track operation completion */
13188 ComObjPtr<Progress> pProgress;
13189 pProgress.createObject();
13190 pProgress->init(i_getVirtualBox(),
13191 static_cast<IMachine *>(this) /* aInitiator */,
13192 tr("Stopping the virtual machine"),
13193 FALSE /* aCancelable */);
13194
13195 /* fill in the console task data */
13196 mConsoleTaskData.mLastState = mData->mMachineState;
13197 mConsoleTaskData.mProgress = pProgress;
13198
13199 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13200 i_setMachineState(MachineState_Stopping);
13201
13202 pProgress.queryInterfaceTo(aProgress.asOutParam());
13203
13204 return S_OK;
13205}
13206
13207/**
13208 * @note Locks this object for writing.
13209 */
13210HRESULT SessionMachine::endPoweringDown(LONG aResult,
13211 const com::Utf8Str &aErrMsg)
13212{
13213 LogFlowThisFuncEnter();
13214
13215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13216
13217 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13218 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13219 && mConsoleTaskData.mLastState != MachineState_Null,
13220 E_FAIL);
13221
13222 /*
13223 * On failure, set the state to the state we had when BeginPoweringDown()
13224 * was called (this is expected by Console::PowerDown() and the associated
13225 * task). On success the VM process already changed the state to
13226 * MachineState_PoweredOff, so no need to do anything.
13227 */
13228 if (FAILED(aResult))
13229 i_setMachineState(mConsoleTaskData.mLastState);
13230
13231 /* notify the progress object about operation completion */
13232 Assert(mConsoleTaskData.mProgress);
13233 if (SUCCEEDED(aResult))
13234 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13235 else
13236 {
13237 if (aErrMsg.length())
13238 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13239 COM_IIDOF(ISession),
13240 getComponentName(),
13241 aErrMsg.c_str());
13242 else
13243 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13244 }
13245
13246 /* clear out the temporary saved state data */
13247 mConsoleTaskData.mLastState = MachineState_Null;
13248 mConsoleTaskData.mProgress.setNull();
13249
13250 LogFlowThisFuncLeave();
13251 return S_OK;
13252}
13253
13254
13255/**
13256 * Goes through the USB filters of the given machine to see if the given
13257 * device matches any filter or not.
13258 *
13259 * @note Locks the same as USBController::hasMatchingFilter() does.
13260 */
13261HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13262 BOOL *aMatched,
13263 ULONG *aMaskedInterfaces)
13264{
13265 LogFlowThisFunc(("\n"));
13266
13267#ifdef VBOX_WITH_USB
13268 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13269#else
13270 NOREF(aDevice);
13271 NOREF(aMaskedInterfaces);
13272 *aMatched = FALSE;
13273#endif
13274
13275 return S_OK;
13276}
13277
13278/**
13279 * @note Locks the same as Host::captureUSBDevice() does.
13280 */
13281HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13282{
13283 LogFlowThisFunc(("\n"));
13284
13285#ifdef VBOX_WITH_USB
13286 /* if captureDeviceForVM() fails, it must have set extended error info */
13287 clearError();
13288 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13289 if (FAILED(rc)) return rc;
13290
13291 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13292 AssertReturn(service, E_FAIL);
13293 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13294#else
13295 NOREF(aId);
13296 return E_NOTIMPL;
13297#endif
13298}
13299
13300/**
13301 * @note Locks the same as Host::detachUSBDevice() does.
13302 */
13303HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13304 BOOL aDone)
13305{
13306 LogFlowThisFunc(("\n"));
13307
13308#ifdef VBOX_WITH_USB
13309 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13310 AssertReturn(service, E_FAIL);
13311 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13312#else
13313 NOREF(aId);
13314 NOREF(aDone);
13315 return E_NOTIMPL;
13316#endif
13317}
13318
13319/**
13320 * Inserts all machine filters to the USB proxy service and then calls
13321 * Host::autoCaptureUSBDevices().
13322 *
13323 * Called by Console from the VM process upon VM startup.
13324 *
13325 * @note Locks what called methods lock.
13326 */
13327HRESULT SessionMachine::autoCaptureUSBDevices()
13328{
13329 LogFlowThisFunc(("\n"));
13330
13331#ifdef VBOX_WITH_USB
13332 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13333 AssertComRC(rc);
13334 NOREF(rc);
13335
13336 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13337 AssertReturn(service, E_FAIL);
13338 return service->autoCaptureDevicesForVM(this);
13339#else
13340 return S_OK;
13341#endif
13342}
13343
13344/**
13345 * Removes all machine filters from the USB proxy service and then calls
13346 * Host::detachAllUSBDevices().
13347 *
13348 * Called by Console from the VM process upon normal VM termination or by
13349 * SessionMachine::uninit() upon abnormal VM termination (from under the
13350 * Machine/SessionMachine lock).
13351 *
13352 * @note Locks what called methods lock.
13353 */
13354HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13355{
13356 LogFlowThisFunc(("\n"));
13357
13358#ifdef VBOX_WITH_USB
13359 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13360 AssertComRC(rc);
13361 NOREF(rc);
13362
13363 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13364 AssertReturn(service, E_FAIL);
13365 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13366#else
13367 NOREF(aDone);
13368 return S_OK;
13369#endif
13370}
13371
13372/**
13373 * @note Locks this object for writing.
13374 */
13375HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13376 ComPtr<IProgress> &aProgress)
13377{
13378 LogFlowThisFuncEnter();
13379
13380 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13381 /*
13382 * We don't assert below because it might happen that a non-direct session
13383 * informs us it is closed right after we've been uninitialized -- it's ok.
13384 */
13385
13386 /* get IInternalSessionControl interface */
13387 ComPtr<IInternalSessionControl> control(aSession);
13388
13389 ComAssertRet(!control.isNull(), E_INVALIDARG);
13390
13391 /* Creating a Progress object requires the VirtualBox lock, and
13392 * thus locking it here is required by the lock order rules. */
13393 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13394
13395 if (control == mData->mSession.mDirectControl)
13396 {
13397 /* The direct session is being normally closed by the client process
13398 * ----------------------------------------------------------------- */
13399
13400 /* go to the closing state (essential for all open*Session() calls and
13401 * for #i_checkForDeath()) */
13402 Assert(mData->mSession.mState == SessionState_Locked);
13403 mData->mSession.mState = SessionState_Unlocking;
13404
13405 /* set direct control to NULL to release the remote instance */
13406 mData->mSession.mDirectControl.setNull();
13407 LogFlowThisFunc(("Direct control is set to NULL\n"));
13408
13409 if (mData->mSession.mProgress)
13410 {
13411 /* finalize the progress, someone might wait if a frontend
13412 * closes the session before powering on the VM. */
13413 mData->mSession.mProgress->notifyComplete(E_FAIL,
13414 COM_IIDOF(ISession),
13415 getComponentName(),
13416 tr("The VM session was closed before any attempt to power it on"));
13417 mData->mSession.mProgress.setNull();
13418 }
13419
13420 /* Create the progress object the client will use to wait until
13421 * #i_checkForDeath() is called to uninitialize this session object after
13422 * it releases the IPC semaphore.
13423 * Note! Because we're "reusing" mProgress here, this must be a proxy
13424 * object just like for LaunchVMProcess. */
13425 Assert(mData->mSession.mProgress.isNull());
13426 ComObjPtr<ProgressProxy> progress;
13427 progress.createObject();
13428 ComPtr<IUnknown> pPeer(mPeer);
13429 progress->init(mParent, pPeer,
13430 Bstr(tr("Closing session")).raw(),
13431 FALSE /* aCancelable */);
13432 progress.queryInterfaceTo(aProgress.asOutParam());
13433 mData->mSession.mProgress = progress;
13434 }
13435 else
13436 {
13437 /* the remote session is being normally closed */
13438 bool found = false;
13439 for (Data::Session::RemoteControlList::iterator
13440 it = mData->mSession.mRemoteControls.begin();
13441 it != mData->mSession.mRemoteControls.end();
13442 ++it)
13443 {
13444 if (control == *it)
13445 {
13446 found = true;
13447 // This MUST be erase(it), not remove(*it) as the latter
13448 // triggers a very nasty use after free due to the place where
13449 // the value "lives".
13450 mData->mSession.mRemoteControls.erase(it);
13451 break;
13452 }
13453 }
13454 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13455 E_INVALIDARG);
13456 }
13457
13458 /* signal the client watcher thread, because the client is going away */
13459 mParent->i_updateClientWatcher();
13460
13461 LogFlowThisFuncLeave();
13462 return S_OK;
13463}
13464
13465HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13466 std::vector<com::Utf8Str> &aValues,
13467 std::vector<LONG64> &aTimestamps,
13468 std::vector<com::Utf8Str> &aFlags)
13469{
13470 LogFlowThisFunc(("\n"));
13471
13472#ifdef VBOX_WITH_GUEST_PROPS
13473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13474
13475 size_t cEntries = mHWData->mGuestProperties.size();
13476 aNames.resize(cEntries);
13477 aValues.resize(cEntries);
13478 aTimestamps.resize(cEntries);
13479 aFlags.resize(cEntries);
13480
13481 size_t i = 0;
13482 for (HWData::GuestPropertyMap::const_iterator
13483 it = mHWData->mGuestProperties.begin();
13484 it != mHWData->mGuestProperties.end();
13485 ++it, ++i)
13486 {
13487 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13488 aNames[i] = it->first;
13489 aValues[i] = it->second.strValue;
13490 aTimestamps[i] = it->second.mTimestamp;
13491
13492 /* If it is NULL, keep it NULL. */
13493 if (it->second.mFlags)
13494 {
13495 GuestPropWriteFlags(it->second.mFlags, szFlags);
13496 aFlags[i] = szFlags;
13497 }
13498 else
13499 aFlags[i] = "";
13500 }
13501 return S_OK;
13502#else
13503 ReturnComNotImplemented();
13504#endif
13505}
13506
13507HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13508 const com::Utf8Str &aValue,
13509 LONG64 aTimestamp,
13510 const com::Utf8Str &aFlags)
13511{
13512 LogFlowThisFunc(("\n"));
13513
13514#ifdef VBOX_WITH_GUEST_PROPS
13515 try
13516 {
13517 /*
13518 * Convert input up front.
13519 */
13520 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13521 if (aFlags.length())
13522 {
13523 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13524 AssertRCReturn(vrc, E_INVALIDARG);
13525 }
13526
13527 /*
13528 * Now grab the object lock, validate the state and do the update.
13529 */
13530
13531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13532
13533 if (!Global::IsOnline(mData->mMachineState))
13534 {
13535 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13536 VBOX_E_INVALID_VM_STATE);
13537 }
13538
13539 i_setModified(IsModified_MachineData);
13540 mHWData.backup();
13541
13542 bool fDelete = !aValue.length();
13543 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13544 if (it != mHWData->mGuestProperties.end())
13545 {
13546 if (!fDelete)
13547 {
13548 it->second.strValue = aValue;
13549 it->second.mTimestamp = aTimestamp;
13550 it->second.mFlags = fFlags;
13551 }
13552 else
13553 mHWData->mGuestProperties.erase(it);
13554
13555 mData->mGuestPropertiesModified = TRUE;
13556 }
13557 else if (!fDelete)
13558 {
13559 HWData::GuestProperty prop;
13560 prop.strValue = aValue;
13561 prop.mTimestamp = aTimestamp;
13562 prop.mFlags = fFlags;
13563
13564 mHWData->mGuestProperties[aName] = prop;
13565 mData->mGuestPropertiesModified = TRUE;
13566 }
13567
13568 alock.release();
13569
13570 mParent->i_onGuestPropertyChange(mData->mUuid,
13571 Bstr(aName).raw(),
13572 Bstr(aValue).raw(),
13573 Bstr(aFlags).raw());
13574 }
13575 catch (...)
13576 {
13577 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13578 }
13579 return S_OK;
13580#else
13581 ReturnComNotImplemented();
13582#endif
13583}
13584
13585
13586HRESULT SessionMachine::lockMedia()
13587{
13588 AutoMultiWriteLock2 alock(this->lockHandle(),
13589 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13590
13591 AssertReturn( mData->mMachineState == MachineState_Starting
13592 || mData->mMachineState == MachineState_Restoring
13593 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13594
13595 clearError();
13596 alock.release();
13597 return i_lockMedia();
13598}
13599
13600HRESULT SessionMachine::unlockMedia()
13601{
13602 HRESULT hrc = i_unlockMedia();
13603 return hrc;
13604}
13605
13606HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13607 ComPtr<IMediumAttachment> &aNewAttachment)
13608{
13609 // request the host lock first, since might be calling Host methods for getting host drives;
13610 // next, protect the media tree all the while we're in here, as well as our member variables
13611 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13612 this->lockHandle(),
13613 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13614
13615 IMediumAttachment *iAttach = aAttachment;
13616 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13617
13618 Utf8Str ctrlName;
13619 LONG lPort;
13620 LONG lDevice;
13621 bool fTempEject;
13622 {
13623 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13624
13625 /* Need to query the details first, as the IMediumAttachment reference
13626 * might be to the original settings, which we are going to change. */
13627 ctrlName = pAttach->i_getControllerName();
13628 lPort = pAttach->i_getPort();
13629 lDevice = pAttach->i_getDevice();
13630 fTempEject = pAttach->i_getTempEject();
13631 }
13632
13633 if (!fTempEject)
13634 {
13635 /* Remember previously mounted medium. The medium before taking the
13636 * backup is not necessarily the same thing. */
13637 ComObjPtr<Medium> oldmedium;
13638 oldmedium = pAttach->i_getMedium();
13639
13640 i_setModified(IsModified_Storage);
13641 mMediumAttachments.backup();
13642
13643 // The backup operation makes the pAttach reference point to the
13644 // old settings. Re-get the correct reference.
13645 pAttach = i_findAttachment(*mMediumAttachments.data(),
13646 ctrlName,
13647 lPort,
13648 lDevice);
13649
13650 {
13651 AutoCaller autoAttachCaller(this);
13652 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13653
13654 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13655 if (!oldmedium.isNull())
13656 oldmedium->i_removeBackReference(mData->mUuid);
13657
13658 pAttach->i_updateMedium(NULL);
13659 pAttach->i_updateEjected();
13660 }
13661
13662 i_setModified(IsModified_Storage);
13663 }
13664 else
13665 {
13666 {
13667 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13668 pAttach->i_updateEjected();
13669 }
13670 }
13671
13672 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13673
13674 return S_OK;
13675}
13676
13677HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13678 com::Utf8Str &aResult)
13679{
13680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13681
13682 HRESULT hr = S_OK;
13683
13684 if (!mAuthLibCtx.hAuthLibrary)
13685 {
13686 /* Load the external authentication library. */
13687 Bstr authLibrary;
13688 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13689
13690 Utf8Str filename = authLibrary;
13691
13692 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13693 if (RT_FAILURE(vrc))
13694 hr = setErrorBoth(E_FAIL, vrc,
13695 tr("Could not load the external authentication library '%s' (%Rrc)"),
13696 filename.c_str(), vrc);
13697 }
13698
13699 /* The auth library might need the machine lock. */
13700 alock.release();
13701
13702 if (FAILED(hr))
13703 return hr;
13704
13705 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13706 {
13707 enum VRDEAuthParams
13708 {
13709 parmUuid = 1,
13710 parmGuestJudgement,
13711 parmUser,
13712 parmPassword,
13713 parmDomain,
13714 parmClientId
13715 };
13716
13717 AuthResult result = AuthResultAccessDenied;
13718
13719 Guid uuid(aAuthParams[parmUuid]);
13720 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13721 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13722
13723 result = AuthLibAuthenticate(&mAuthLibCtx,
13724 uuid.raw(), guestJudgement,
13725 aAuthParams[parmUser].c_str(),
13726 aAuthParams[parmPassword].c_str(),
13727 aAuthParams[parmDomain].c_str(),
13728 u32ClientId);
13729
13730 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13731 size_t cbPassword = aAuthParams[parmPassword].length();
13732 if (cbPassword)
13733 {
13734 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13735 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13736 }
13737
13738 if (result == AuthResultAccessGranted)
13739 aResult = "granted";
13740 else
13741 aResult = "denied";
13742
13743 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13744 aAuthParams[parmUser].c_str(), aResult.c_str()));
13745 }
13746 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13747 {
13748 enum VRDEAuthDisconnectParams
13749 {
13750 parmUuid = 1,
13751 parmClientId
13752 };
13753
13754 Guid uuid(aAuthParams[parmUuid]);
13755 uint32_t u32ClientId = 0;
13756 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13757 }
13758 else
13759 {
13760 hr = E_INVALIDARG;
13761 }
13762
13763 return hr;
13764}
13765
13766// public methods only for internal purposes
13767/////////////////////////////////////////////////////////////////////////////
13768
13769#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13770/**
13771 * Called from the client watcher thread to check for expected or unexpected
13772 * death of the client process that has a direct session to this machine.
13773 *
13774 * On Win32 and on OS/2, this method is called only when we've got the
13775 * mutex (i.e. the client has either died or terminated normally) so it always
13776 * returns @c true (the client is terminated, the session machine is
13777 * uninitialized).
13778 *
13779 * On other platforms, the method returns @c true if the client process has
13780 * terminated normally or abnormally and the session machine was uninitialized,
13781 * and @c false if the client process is still alive.
13782 *
13783 * @note Locks this object for writing.
13784 */
13785bool SessionMachine::i_checkForDeath()
13786{
13787 Uninit::Reason reason;
13788 bool terminated = false;
13789
13790 /* Enclose autoCaller with a block because calling uninit() from under it
13791 * will deadlock. */
13792 {
13793 AutoCaller autoCaller(this);
13794 if (!autoCaller.isOk())
13795 {
13796 /* return true if not ready, to cause the client watcher to exclude
13797 * the corresponding session from watching */
13798 LogFlowThisFunc(("Already uninitialized!\n"));
13799 return true;
13800 }
13801
13802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13803
13804 /* Determine the reason of death: if the session state is Closing here,
13805 * everything is fine. Otherwise it means that the client did not call
13806 * OnSessionEnd() before it released the IPC semaphore. This may happen
13807 * either because the client process has abnormally terminated, or
13808 * because it simply forgot to call ISession::Close() before exiting. We
13809 * threat the latter also as an abnormal termination (see
13810 * Session::uninit() for details). */
13811 reason = mData->mSession.mState == SessionState_Unlocking ?
13812 Uninit::Normal :
13813 Uninit::Abnormal;
13814
13815 if (mClientToken)
13816 terminated = mClientToken->release();
13817 } /* AutoCaller block */
13818
13819 if (terminated)
13820 uninit(reason);
13821
13822 return terminated;
13823}
13824
13825void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829 strTokenId.setNull();
13830
13831 AutoCaller autoCaller(this);
13832 AssertComRCReturnVoid(autoCaller.rc());
13833
13834 Assert(mClientToken);
13835 if (mClientToken)
13836 mClientToken->getId(strTokenId);
13837}
13838#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13839IToken *SessionMachine::i_getToken()
13840{
13841 LogFlowThisFunc(("\n"));
13842
13843 AutoCaller autoCaller(this);
13844 AssertComRCReturn(autoCaller.rc(), NULL);
13845
13846 Assert(mClientToken);
13847 if (mClientToken)
13848 return mClientToken->getToken();
13849 else
13850 return NULL;
13851}
13852#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13853
13854Machine::ClientToken *SessionMachine::i_getClientToken()
13855{
13856 LogFlowThisFunc(("\n"));
13857
13858 AutoCaller autoCaller(this);
13859 AssertComRCReturn(autoCaller.rc(), NULL);
13860
13861 return mClientToken;
13862}
13863
13864
13865/**
13866 * @note Locks this object for reading.
13867 */
13868HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13869{
13870 LogFlowThisFunc(("\n"));
13871
13872 AutoCaller autoCaller(this);
13873 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13874
13875 ComPtr<IInternalSessionControl> directControl;
13876 {
13877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13878 if (mData->mSession.mLockType == LockType_VM)
13879 directControl = mData->mSession.mDirectControl;
13880 }
13881
13882 /* ignore notifications sent after #OnSessionEnd() is called */
13883 if (!directControl)
13884 return S_OK;
13885
13886 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13887}
13888
13889/**
13890 * @note Locks this object for reading.
13891 */
13892HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13893 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13894 IN_BSTR aGuestIp, LONG aGuestPort)
13895{
13896 LogFlowThisFunc(("\n"));
13897
13898 AutoCaller autoCaller(this);
13899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13900
13901 ComPtr<IInternalSessionControl> directControl;
13902 {
13903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13904 if (mData->mSession.mLockType == LockType_VM)
13905 directControl = mData->mSession.mDirectControl;
13906 }
13907
13908 /* ignore notifications sent after #OnSessionEnd() is called */
13909 if (!directControl)
13910 return S_OK;
13911 /*
13912 * instead acting like callback we ask IVirtualBox deliver corresponding event
13913 */
13914
13915 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13916 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13917 return S_OK;
13918}
13919
13920/**
13921 * @note Locks this object for reading.
13922 */
13923HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13924{
13925 LogFlowThisFunc(("\n"));
13926
13927 AutoCaller autoCaller(this);
13928 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13929
13930 ComPtr<IInternalSessionControl> directControl;
13931 {
13932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13933 if (mData->mSession.mLockType == LockType_VM)
13934 directControl = mData->mSession.mDirectControl;
13935 }
13936
13937 /* ignore notifications sent after #OnSessionEnd() is called */
13938 if (!directControl)
13939 return S_OK;
13940
13941 return directControl->OnAudioAdapterChange(audioAdapter);
13942}
13943
13944/**
13945 * @note Locks this object for reading.
13946 */
13947HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13948{
13949 LogFlowThisFunc(("\n"));
13950
13951 AutoCaller autoCaller(this);
13952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13953
13954 ComPtr<IInternalSessionControl> directControl;
13955 {
13956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13957 if (mData->mSession.mLockType == LockType_VM)
13958 directControl = mData->mSession.mDirectControl;
13959 }
13960
13961 /* ignore notifications sent after #OnSessionEnd() is called */
13962 if (!directControl)
13963 return S_OK;
13964
13965 return directControl->OnSerialPortChange(serialPort);
13966}
13967
13968/**
13969 * @note Locks this object for reading.
13970 */
13971HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13972{
13973 LogFlowThisFunc(("\n"));
13974
13975 AutoCaller autoCaller(this);
13976 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13977
13978 ComPtr<IInternalSessionControl> directControl;
13979 {
13980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13981 if (mData->mSession.mLockType == LockType_VM)
13982 directControl = mData->mSession.mDirectControl;
13983 }
13984
13985 /* ignore notifications sent after #OnSessionEnd() is called */
13986 if (!directControl)
13987 return S_OK;
13988
13989 return directControl->OnParallelPortChange(parallelPort);
13990}
13991
13992/**
13993 * @note Locks this object for reading.
13994 */
13995HRESULT SessionMachine::i_onStorageControllerChange()
13996{
13997 LogFlowThisFunc(("\n"));
13998
13999 AutoCaller autoCaller(this);
14000 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14001
14002 ComPtr<IInternalSessionControl> directControl;
14003 {
14004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14005 if (mData->mSession.mLockType == LockType_VM)
14006 directControl = mData->mSession.mDirectControl;
14007 }
14008
14009 /* ignore notifications sent after #OnSessionEnd() is called */
14010 if (!directControl)
14011 return S_OK;
14012
14013 return directControl->OnStorageControllerChange();
14014}
14015
14016/**
14017 * @note Locks this object for reading.
14018 */
14019HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14020{
14021 LogFlowThisFunc(("\n"));
14022
14023 AutoCaller autoCaller(this);
14024 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14025
14026 ComPtr<IInternalSessionControl> directControl;
14027 {
14028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14029 if (mData->mSession.mLockType == LockType_VM)
14030 directControl = mData->mSession.mDirectControl;
14031 }
14032
14033 mParent->i_onMediumChanged(aAttachment);
14034
14035 /* ignore notifications sent after #OnSessionEnd() is called */
14036 if (!directControl)
14037 return S_OK;
14038
14039 return directControl->OnMediumChange(aAttachment, aForce);
14040}
14041
14042/**
14043 * @note Locks this object for reading.
14044 */
14045HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14046{
14047 LogFlowThisFunc(("\n"));
14048
14049 AutoCaller autoCaller(this);
14050 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14051
14052 ComPtr<IInternalSessionControl> directControl;
14053 {
14054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14055 if (mData->mSession.mLockType == LockType_VM)
14056 directControl = mData->mSession.mDirectControl;
14057 }
14058
14059 /* ignore notifications sent after #OnSessionEnd() is called */
14060 if (!directControl)
14061 return S_OK;
14062
14063 return directControl->OnCPUChange(aCPU, aRemove);
14064}
14065
14066HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14067{
14068 LogFlowThisFunc(("\n"));
14069
14070 AutoCaller autoCaller(this);
14071 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14072
14073 ComPtr<IInternalSessionControl> directControl;
14074 {
14075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14076 if (mData->mSession.mLockType == LockType_VM)
14077 directControl = mData->mSession.mDirectControl;
14078 }
14079
14080 /* ignore notifications sent after #OnSessionEnd() is called */
14081 if (!directControl)
14082 return S_OK;
14083
14084 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14085}
14086
14087/**
14088 * @note Locks this object for reading.
14089 */
14090HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14091{
14092 LogFlowThisFunc(("\n"));
14093
14094 AutoCaller autoCaller(this);
14095 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14096
14097 ComPtr<IInternalSessionControl> directControl;
14098 {
14099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14100 if (mData->mSession.mLockType == LockType_VM)
14101 directControl = mData->mSession.mDirectControl;
14102 }
14103
14104 /* ignore notifications sent after #OnSessionEnd() is called */
14105 if (!directControl)
14106 return S_OK;
14107
14108 return directControl->OnVRDEServerChange(aRestart);
14109}
14110
14111/**
14112 * @note Locks this object for reading.
14113 */
14114HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14120
14121 ComPtr<IInternalSessionControl> directControl;
14122 {
14123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14124 if (mData->mSession.mLockType == LockType_VM)
14125 directControl = mData->mSession.mDirectControl;
14126 }
14127
14128 /* ignore notifications sent after #OnSessionEnd() is called */
14129 if (!directControl)
14130 return S_OK;
14131
14132 return directControl->OnRecordingChange(aEnable);
14133}
14134
14135/**
14136 * @note Locks this object for reading.
14137 */
14138HRESULT SessionMachine::i_onUSBControllerChange()
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14144
14145 ComPtr<IInternalSessionControl> directControl;
14146 {
14147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14148 if (mData->mSession.mLockType == LockType_VM)
14149 directControl = mData->mSession.mDirectControl;
14150 }
14151
14152 /* ignore notifications sent after #OnSessionEnd() is called */
14153 if (!directControl)
14154 return S_OK;
14155
14156 return directControl->OnUSBControllerChange();
14157}
14158
14159/**
14160 * @note Locks this object for reading.
14161 */
14162HRESULT SessionMachine::i_onSharedFolderChange()
14163{
14164 LogFlowThisFunc(("\n"));
14165
14166 AutoCaller autoCaller(this);
14167 AssertComRCReturnRC(autoCaller.rc());
14168
14169 ComPtr<IInternalSessionControl> directControl;
14170 {
14171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14172 if (mData->mSession.mLockType == LockType_VM)
14173 directControl = mData->mSession.mDirectControl;
14174 }
14175
14176 /* ignore notifications sent after #OnSessionEnd() is called */
14177 if (!directControl)
14178 return S_OK;
14179
14180 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14181}
14182
14183/**
14184 * @note Locks this object for reading.
14185 */
14186HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14187{
14188 LogFlowThisFunc(("\n"));
14189
14190 AutoCaller autoCaller(this);
14191 AssertComRCReturnRC(autoCaller.rc());
14192
14193 ComPtr<IInternalSessionControl> directControl;
14194 {
14195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14196 if (mData->mSession.mLockType == LockType_VM)
14197 directControl = mData->mSession.mDirectControl;
14198 }
14199
14200 /* ignore notifications sent after #OnSessionEnd() is called */
14201 if (!directControl)
14202 return S_OK;
14203
14204 return directControl->OnClipboardModeChange(aClipboardMode);
14205}
14206
14207/**
14208 * @note Locks this object for reading.
14209 */
14210HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14211{
14212 LogFlowThisFunc(("\n"));
14213
14214 AutoCaller autoCaller(this);
14215 AssertComRCReturnRC(autoCaller.rc());
14216
14217 ComPtr<IInternalSessionControl> directControl;
14218 {
14219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14220 if (mData->mSession.mLockType == LockType_VM)
14221 directControl = mData->mSession.mDirectControl;
14222 }
14223
14224 /* ignore notifications sent after #OnSessionEnd() is called */
14225 if (!directControl)
14226 return S_OK;
14227
14228 return directControl->OnDnDModeChange(aDnDMode);
14229}
14230
14231/**
14232 * @note Locks this object for reading.
14233 */
14234HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14235{
14236 LogFlowThisFunc(("\n"));
14237
14238 AutoCaller autoCaller(this);
14239 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14240
14241 ComPtr<IInternalSessionControl> directControl;
14242 {
14243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14244 if (mData->mSession.mLockType == LockType_VM)
14245 directControl = mData->mSession.mDirectControl;
14246 }
14247
14248 /* ignore notifications sent after #OnSessionEnd() is called */
14249 if (!directControl)
14250 return S_OK;
14251
14252 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14253}
14254
14255/**
14256 * @note Locks this object for reading.
14257 */
14258HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14259{
14260 LogFlowThisFunc(("\n"));
14261
14262 AutoCaller autoCaller(this);
14263 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14264
14265 ComPtr<IInternalSessionControl> directControl;
14266 {
14267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14268 if (mData->mSession.mLockType == LockType_VM)
14269 directControl = mData->mSession.mDirectControl;
14270 }
14271
14272 mParent->i_onStorageDeviceChanged(aAttachment, aRemove, aSilent);
14273
14274 /* ignore notifications sent after #OnSessionEnd() is called */
14275 if (!directControl)
14276 return S_OK;
14277
14278 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14279}
14280
14281/**
14282 * Returns @c true if this machine's USB controller reports it has a matching
14283 * filter for the given USB device and @c false otherwise.
14284 *
14285 * @note locks this object for reading.
14286 */
14287bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14288{
14289 AutoCaller autoCaller(this);
14290 /* silently return if not ready -- this method may be called after the
14291 * direct machine session has been called */
14292 if (!autoCaller.isOk())
14293 return false;
14294
14295#ifdef VBOX_WITH_USB
14296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14297
14298 switch (mData->mMachineState)
14299 {
14300 case MachineState_Starting:
14301 case MachineState_Restoring:
14302 case MachineState_TeleportingIn:
14303 case MachineState_Paused:
14304 case MachineState_Running:
14305 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14306 * elsewhere... */
14307 alock.release();
14308 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14309 default: break;
14310 }
14311#else
14312 NOREF(aDevice);
14313 NOREF(aMaskedIfs);
14314#endif
14315 return false;
14316}
14317
14318/**
14319 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14320 */
14321HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14322 IVirtualBoxErrorInfo *aError,
14323 ULONG aMaskedIfs,
14324 const com::Utf8Str &aCaptureFilename)
14325{
14326 LogFlowThisFunc(("\n"));
14327
14328 AutoCaller autoCaller(this);
14329
14330 /* This notification may happen after the machine object has been
14331 * uninitialized (the session was closed), so don't assert. */
14332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14333
14334 ComPtr<IInternalSessionControl> directControl;
14335 {
14336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14337 if (mData->mSession.mLockType == LockType_VM)
14338 directControl = mData->mSession.mDirectControl;
14339 }
14340
14341 /* fail on notifications sent after #OnSessionEnd() is called, it is
14342 * expected by the caller */
14343 if (!directControl)
14344 return E_FAIL;
14345
14346 /* No locks should be held at this point. */
14347 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14348 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14349
14350 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14351}
14352
14353/**
14354 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14355 */
14356HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14357 IVirtualBoxErrorInfo *aError)
14358{
14359 LogFlowThisFunc(("\n"));
14360
14361 AutoCaller autoCaller(this);
14362
14363 /* This notification may happen after the machine object has been
14364 * uninitialized (the session was closed), so don't assert. */
14365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14366
14367 ComPtr<IInternalSessionControl> directControl;
14368 {
14369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14370 if (mData->mSession.mLockType == LockType_VM)
14371 directControl = mData->mSession.mDirectControl;
14372 }
14373
14374 /* fail on notifications sent after #OnSessionEnd() is called, it is
14375 * expected by the caller */
14376 if (!directControl)
14377 return E_FAIL;
14378
14379 /* No locks should be held at this point. */
14380 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14381 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14382
14383 return directControl->OnUSBDeviceDetach(aId, aError);
14384}
14385
14386// protected methods
14387/////////////////////////////////////////////////////////////////////////////
14388
14389/**
14390 * Deletes the given file if it is no longer in use by either the current machine state
14391 * (if the machine is "saved") or any of the machine's snapshots.
14392 *
14393 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14394 * but is different for each SnapshotMachine. When calling this, the order of calling this
14395 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14396 * is therefore critical. I know, it's all rather messy.
14397 *
14398 * @param strStateFile
14399 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14400 * the test for whether the saved state file is in use.
14401 */
14402void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14403 Snapshot *pSnapshotToIgnore)
14404{
14405 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14406 if ( (strStateFile.isNotEmpty())
14407 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14408 )
14409 // ... and it must also not be shared with other snapshots
14410 if ( !mData->mFirstSnapshot
14411 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14412 // this checks the SnapshotMachine's state file paths
14413 )
14414 RTFileDelete(strStateFile.c_str());
14415}
14416
14417/**
14418 * Locks the attached media.
14419 *
14420 * All attached hard disks are locked for writing and DVD/floppy are locked for
14421 * reading. Parents of attached hard disks (if any) are locked for reading.
14422 *
14423 * This method also performs accessibility check of all media it locks: if some
14424 * media is inaccessible, the method will return a failure and a bunch of
14425 * extended error info objects per each inaccessible medium.
14426 *
14427 * Note that this method is atomic: if it returns a success, all media are
14428 * locked as described above; on failure no media is locked at all (all
14429 * succeeded individual locks will be undone).
14430 *
14431 * The caller is responsible for doing the necessary state sanity checks.
14432 *
14433 * The locks made by this method must be undone by calling #unlockMedia() when
14434 * no more needed.
14435 */
14436HRESULT SessionMachine::i_lockMedia()
14437{
14438 AutoCaller autoCaller(this);
14439 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14440
14441 AutoMultiWriteLock2 alock(this->lockHandle(),
14442 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14443
14444 /* bail out if trying to lock things with already set up locking */
14445 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14446
14447 MultiResult mrc(S_OK);
14448
14449 /* Collect locking information for all medium objects attached to the VM. */
14450 for (MediumAttachmentList::const_iterator
14451 it = mMediumAttachments->begin();
14452 it != mMediumAttachments->end();
14453 ++it)
14454 {
14455 MediumAttachment *pAtt = *it;
14456 DeviceType_T devType = pAtt->i_getType();
14457 Medium *pMedium = pAtt->i_getMedium();
14458
14459 MediumLockList *pMediumLockList(new MediumLockList());
14460 // There can be attachments without a medium (floppy/dvd), and thus
14461 // it's impossible to create a medium lock list. It still makes sense
14462 // to have the empty medium lock list in the map in case a medium is
14463 // attached later.
14464 if (pMedium != NULL)
14465 {
14466 MediumType_T mediumType = pMedium->i_getType();
14467 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14468 || mediumType == MediumType_Shareable;
14469 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14470
14471 alock.release();
14472 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14473 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14474 false /* fMediumLockWriteAll */,
14475 NULL,
14476 *pMediumLockList);
14477 alock.acquire();
14478 if (FAILED(mrc))
14479 {
14480 delete pMediumLockList;
14481 mData->mSession.mLockedMedia.Clear();
14482 break;
14483 }
14484 }
14485
14486 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14487 if (FAILED(rc))
14488 {
14489 mData->mSession.mLockedMedia.Clear();
14490 mrc = setError(rc,
14491 tr("Collecting locking information for all attached media failed"));
14492 break;
14493 }
14494 }
14495
14496 if (SUCCEEDED(mrc))
14497 {
14498 /* Now lock all media. If this fails, nothing is locked. */
14499 alock.release();
14500 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14501 alock.acquire();
14502 if (FAILED(rc))
14503 {
14504 mrc = setError(rc,
14505 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14506 }
14507 }
14508
14509 return mrc;
14510}
14511
14512/**
14513 * Undoes the locks made by by #lockMedia().
14514 */
14515HRESULT SessionMachine::i_unlockMedia()
14516{
14517 AutoCaller autoCaller(this);
14518 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14519
14520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14521
14522 /* we may be holding important error info on the current thread;
14523 * preserve it */
14524 ErrorInfoKeeper eik;
14525
14526 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14527 AssertComRC(rc);
14528 return rc;
14529}
14530
14531/**
14532 * Helper to change the machine state (reimplementation).
14533 *
14534 * @note Locks this object for writing.
14535 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14536 * it can cause crashes in random places due to unexpectedly committing
14537 * the current settings. The caller is responsible for that. The call
14538 * to saveStateSettings is fine, because this method does not commit.
14539 */
14540HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14541{
14542 LogFlowThisFuncEnter();
14543 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14544
14545 AutoCaller autoCaller(this);
14546 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14547
14548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14549
14550 MachineState_T oldMachineState = mData->mMachineState;
14551
14552 AssertMsgReturn(oldMachineState != aMachineState,
14553 ("oldMachineState=%s, aMachineState=%s\n",
14554 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14555 E_FAIL);
14556
14557 HRESULT rc = S_OK;
14558
14559 int stsFlags = 0;
14560 bool deleteSavedState = false;
14561
14562 /* detect some state transitions */
14563
14564 if ( ( oldMachineState == MachineState_Saved
14565 && aMachineState == MachineState_Restoring)
14566 || ( ( oldMachineState == MachineState_PoweredOff
14567 || oldMachineState == MachineState_Teleported
14568 || oldMachineState == MachineState_Aborted
14569 )
14570 && ( aMachineState == MachineState_TeleportingIn
14571 || aMachineState == MachineState_Starting
14572 )
14573 )
14574 )
14575 {
14576 /* The EMT thread is about to start */
14577
14578 /* Nothing to do here for now... */
14579
14580 /// @todo NEWMEDIA don't let mDVDDrive and other children
14581 /// change anything when in the Starting/Restoring state
14582 }
14583 else if ( ( oldMachineState == MachineState_Running
14584 || oldMachineState == MachineState_Paused
14585 || oldMachineState == MachineState_Teleporting
14586 || oldMachineState == MachineState_OnlineSnapshotting
14587 || oldMachineState == MachineState_LiveSnapshotting
14588 || oldMachineState == MachineState_Stuck
14589 || oldMachineState == MachineState_Starting
14590 || oldMachineState == MachineState_Stopping
14591 || oldMachineState == MachineState_Saving
14592 || oldMachineState == MachineState_Restoring
14593 || oldMachineState == MachineState_TeleportingPausedVM
14594 || oldMachineState == MachineState_TeleportingIn
14595 )
14596 && ( aMachineState == MachineState_PoweredOff
14597 || aMachineState == MachineState_Saved
14598 || aMachineState == MachineState_Teleported
14599 || aMachineState == MachineState_Aborted
14600 )
14601 )
14602 {
14603 /* The EMT thread has just stopped, unlock attached media. Note that as
14604 * opposed to locking that is done from Console, we do unlocking here
14605 * because the VM process may have aborted before having a chance to
14606 * properly unlock all media it locked. */
14607
14608 unlockMedia();
14609 }
14610
14611 if (oldMachineState == MachineState_Restoring)
14612 {
14613 if (aMachineState != MachineState_Saved)
14614 {
14615 /*
14616 * delete the saved state file once the machine has finished
14617 * restoring from it (note that Console sets the state from
14618 * Restoring to Saved if the VM couldn't restore successfully,
14619 * to give the user an ability to fix an error and retry --
14620 * we keep the saved state file in this case)
14621 */
14622 deleteSavedState = true;
14623 }
14624 }
14625 else if ( oldMachineState == MachineState_Saved
14626 && ( aMachineState == MachineState_PoweredOff
14627 || aMachineState == MachineState_Aborted
14628 || aMachineState == MachineState_Teleported
14629 )
14630 )
14631 {
14632 /*
14633 * delete the saved state after SessionMachine::ForgetSavedState() is called
14634 * or if the VM process (owning a direct VM session) crashed while the
14635 * VM was Saved
14636 */
14637
14638 /// @todo (dmik)
14639 // Not sure that deleting the saved state file just because of the
14640 // client death before it attempted to restore the VM is a good
14641 // thing. But when it crashes we need to go to the Aborted state
14642 // which cannot have the saved state file associated... The only
14643 // way to fix this is to make the Aborted condition not a VM state
14644 // but a bool flag: i.e., when a crash occurs, set it to true and
14645 // change the state to PoweredOff or Saved depending on the
14646 // saved state presence.
14647
14648 deleteSavedState = true;
14649 mData->mCurrentStateModified = TRUE;
14650 stsFlags |= SaveSTS_CurStateModified;
14651 }
14652
14653 if ( aMachineState == MachineState_Starting
14654 || aMachineState == MachineState_Restoring
14655 || aMachineState == MachineState_TeleportingIn
14656 )
14657 {
14658 /* set the current state modified flag to indicate that the current
14659 * state is no more identical to the state in the
14660 * current snapshot */
14661 if (!mData->mCurrentSnapshot.isNull())
14662 {
14663 mData->mCurrentStateModified = TRUE;
14664 stsFlags |= SaveSTS_CurStateModified;
14665 }
14666 }
14667
14668 if (deleteSavedState)
14669 {
14670 if (mRemoveSavedState)
14671 {
14672 Assert(!mSSData->strStateFilePath.isEmpty());
14673
14674 // it is safe to delete the saved state file if ...
14675 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14676 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14677 // ... none of the snapshots share the saved state file
14678 )
14679 RTFileDelete(mSSData->strStateFilePath.c_str());
14680 }
14681
14682 mSSData->strStateFilePath.setNull();
14683 stsFlags |= SaveSTS_StateFilePath;
14684 }
14685
14686 /* redirect to the underlying peer machine */
14687 mPeer->i_setMachineState(aMachineState);
14688
14689 if ( oldMachineState != MachineState_RestoringSnapshot
14690 && ( aMachineState == MachineState_PoweredOff
14691 || aMachineState == MachineState_Teleported
14692 || aMachineState == MachineState_Aborted
14693 || aMachineState == MachineState_Saved))
14694 {
14695 /* the machine has stopped execution
14696 * (or the saved state file was adopted) */
14697 stsFlags |= SaveSTS_StateTimeStamp;
14698 }
14699
14700 if ( ( oldMachineState == MachineState_PoweredOff
14701 || oldMachineState == MachineState_Aborted
14702 || oldMachineState == MachineState_Teleported
14703 )
14704 && aMachineState == MachineState_Saved)
14705 {
14706 /* the saved state file was adopted */
14707 Assert(!mSSData->strStateFilePath.isEmpty());
14708 stsFlags |= SaveSTS_StateFilePath;
14709 }
14710
14711#ifdef VBOX_WITH_GUEST_PROPS
14712 if ( aMachineState == MachineState_PoweredOff
14713 || aMachineState == MachineState_Aborted
14714 || aMachineState == MachineState_Teleported)
14715 {
14716 /* Make sure any transient guest properties get removed from the
14717 * property store on shutdown. */
14718 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14719
14720 /* remove it from the settings representation */
14721 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14722 for (settings::GuestPropertiesList::iterator
14723 it = llGuestProperties.begin();
14724 it != llGuestProperties.end();
14725 /*nothing*/)
14726 {
14727 const settings::GuestProperty &prop = *it;
14728 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14729 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14730 {
14731 it = llGuestProperties.erase(it);
14732 fNeedsSaving = true;
14733 }
14734 else
14735 {
14736 ++it;
14737 }
14738 }
14739
14740 /* Additionally remove it from the HWData representation. Required to
14741 * keep everything in sync, as this is what the API keeps using. */
14742 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14743 for (HWData::GuestPropertyMap::iterator
14744 it = llHWGuestProperties.begin();
14745 it != llHWGuestProperties.end();
14746 /*nothing*/)
14747 {
14748 uint32_t fFlags = it->second.mFlags;
14749 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14750 {
14751 /* iterator where we need to continue after the erase call
14752 * (C++03 is a fact still, and it doesn't return the iterator
14753 * which would allow continuing) */
14754 HWData::GuestPropertyMap::iterator it2 = it;
14755 ++it2;
14756 llHWGuestProperties.erase(it);
14757 it = it2;
14758 fNeedsSaving = true;
14759 }
14760 else
14761 {
14762 ++it;
14763 }
14764 }
14765
14766 if (fNeedsSaving)
14767 {
14768 mData->mCurrentStateModified = TRUE;
14769 stsFlags |= SaveSTS_CurStateModified;
14770 }
14771 }
14772#endif /* VBOX_WITH_GUEST_PROPS */
14773
14774 rc = i_saveStateSettings(stsFlags);
14775
14776 if ( ( oldMachineState != MachineState_PoweredOff
14777 && oldMachineState != MachineState_Aborted
14778 && oldMachineState != MachineState_Teleported
14779 )
14780 && ( aMachineState == MachineState_PoweredOff
14781 || aMachineState == MachineState_Aborted
14782 || aMachineState == MachineState_Teleported
14783 )
14784 )
14785 {
14786 /* we've been shut down for any reason */
14787 /* no special action so far */
14788 }
14789
14790 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14791 LogFlowThisFuncLeave();
14792 return rc;
14793}
14794
14795/**
14796 * Sends the current machine state value to the VM process.
14797 *
14798 * @note Locks this object for reading, then calls a client process.
14799 */
14800HRESULT SessionMachine::i_updateMachineStateOnClient()
14801{
14802 AutoCaller autoCaller(this);
14803 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14804
14805 ComPtr<IInternalSessionControl> directControl;
14806 {
14807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14808 AssertReturn(!!mData, E_FAIL);
14809 if (mData->mSession.mLockType == LockType_VM)
14810 directControl = mData->mSession.mDirectControl;
14811
14812 /* directControl may be already set to NULL here in #OnSessionEnd()
14813 * called too early by the direct session process while there is still
14814 * some operation (like deleting the snapshot) in progress. The client
14815 * process in this case is waiting inside Session::close() for the
14816 * "end session" process object to complete, while #uninit() called by
14817 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14818 * operation to complete. For now, we accept this inconsistent behavior
14819 * and simply do nothing here. */
14820
14821 if (mData->mSession.mState == SessionState_Unlocking)
14822 return S_OK;
14823 }
14824
14825 /* ignore notifications sent after #OnSessionEnd() is called */
14826 if (!directControl)
14827 return S_OK;
14828
14829 return directControl->UpdateMachineState(mData->mMachineState);
14830}
14831
14832
14833/*static*/
14834HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14835{
14836 va_list args;
14837 va_start(args, pcszMsg);
14838 HRESULT rc = setErrorInternal(aResultCode,
14839 getStaticClassIID(),
14840 getStaticComponentName(),
14841 Utf8Str(pcszMsg, args),
14842 false /* aWarning */,
14843 true /* aLogIt */);
14844 va_end(args);
14845 return rc;
14846}
14847
14848
14849HRESULT Machine::updateState(MachineState_T aState)
14850{
14851 NOREF(aState);
14852 ReturnComNotImplemented();
14853}
14854
14855HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14856{
14857 NOREF(aProgress);
14858 ReturnComNotImplemented();
14859}
14860
14861HRESULT Machine::endPowerUp(LONG aResult)
14862{
14863 NOREF(aResult);
14864 ReturnComNotImplemented();
14865}
14866
14867HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14868{
14869 NOREF(aProgress);
14870 ReturnComNotImplemented();
14871}
14872
14873HRESULT Machine::endPoweringDown(LONG aResult,
14874 const com::Utf8Str &aErrMsg)
14875{
14876 NOREF(aResult);
14877 NOREF(aErrMsg);
14878 ReturnComNotImplemented();
14879}
14880
14881HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14882 BOOL *aMatched,
14883 ULONG *aMaskedInterfaces)
14884{
14885 NOREF(aDevice);
14886 NOREF(aMatched);
14887 NOREF(aMaskedInterfaces);
14888 ReturnComNotImplemented();
14889
14890}
14891
14892HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14893{
14894 NOREF(aId); NOREF(aCaptureFilename);
14895 ReturnComNotImplemented();
14896}
14897
14898HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14899 BOOL aDone)
14900{
14901 NOREF(aId);
14902 NOREF(aDone);
14903 ReturnComNotImplemented();
14904}
14905
14906HRESULT Machine::autoCaptureUSBDevices()
14907{
14908 ReturnComNotImplemented();
14909}
14910
14911HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14912{
14913 NOREF(aDone);
14914 ReturnComNotImplemented();
14915}
14916
14917HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14918 ComPtr<IProgress> &aProgress)
14919{
14920 NOREF(aSession);
14921 NOREF(aProgress);
14922 ReturnComNotImplemented();
14923}
14924
14925HRESULT Machine::finishOnlineMergeMedium()
14926{
14927 ReturnComNotImplemented();
14928}
14929
14930HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14931 std::vector<com::Utf8Str> &aValues,
14932 std::vector<LONG64> &aTimestamps,
14933 std::vector<com::Utf8Str> &aFlags)
14934{
14935 NOREF(aNames);
14936 NOREF(aValues);
14937 NOREF(aTimestamps);
14938 NOREF(aFlags);
14939 ReturnComNotImplemented();
14940}
14941
14942HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14943 const com::Utf8Str &aValue,
14944 LONG64 aTimestamp,
14945 const com::Utf8Str &aFlags)
14946{
14947 NOREF(aName);
14948 NOREF(aValue);
14949 NOREF(aTimestamp);
14950 NOREF(aFlags);
14951 ReturnComNotImplemented();
14952}
14953
14954HRESULT Machine::lockMedia()
14955{
14956 ReturnComNotImplemented();
14957}
14958
14959HRESULT Machine::unlockMedia()
14960{
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14965 ComPtr<IMediumAttachment> &aNewAttachment)
14966{
14967 NOREF(aAttachment);
14968 NOREF(aNewAttachment);
14969 ReturnComNotImplemented();
14970}
14971
14972HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14973 ULONG aCpuUser,
14974 ULONG aCpuKernel,
14975 ULONG aCpuIdle,
14976 ULONG aMemTotal,
14977 ULONG aMemFree,
14978 ULONG aMemBalloon,
14979 ULONG aMemShared,
14980 ULONG aMemCache,
14981 ULONG aPagedTotal,
14982 ULONG aMemAllocTotal,
14983 ULONG aMemFreeTotal,
14984 ULONG aMemBalloonTotal,
14985 ULONG aMemSharedTotal,
14986 ULONG aVmNetRx,
14987 ULONG aVmNetTx)
14988{
14989 NOREF(aValidStats);
14990 NOREF(aCpuUser);
14991 NOREF(aCpuKernel);
14992 NOREF(aCpuIdle);
14993 NOREF(aMemTotal);
14994 NOREF(aMemFree);
14995 NOREF(aMemBalloon);
14996 NOREF(aMemShared);
14997 NOREF(aMemCache);
14998 NOREF(aPagedTotal);
14999 NOREF(aMemAllocTotal);
15000 NOREF(aMemFreeTotal);
15001 NOREF(aMemBalloonTotal);
15002 NOREF(aMemSharedTotal);
15003 NOREF(aVmNetRx);
15004 NOREF(aVmNetTx);
15005 ReturnComNotImplemented();
15006}
15007
15008HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15009 com::Utf8Str &aResult)
15010{
15011 NOREF(aAuthParams);
15012 NOREF(aResult);
15013 ReturnComNotImplemented();
15014}
15015
15016HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15017{
15018 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15019
15020 AutoCaller autoCaller(this);
15021 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15022
15023 HRESULT rc = S_OK;
15024
15025 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15026 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15027 rc = getUSBDeviceFilters(usbDeviceFilters);
15028 if (FAILED(rc)) return rc;
15029
15030 NOREF(aFlags);
15031 com::Utf8Str osTypeId;
15032 ComObjPtr<GuestOSType> osType = NULL;
15033
15034 /* Get the guest os type as a string from the VB. */
15035 rc = getOSTypeId(osTypeId);
15036 if (FAILED(rc)) return rc;
15037
15038 /* Get the os type obj that coresponds, can be used to get
15039 * the defaults for this guest OS. */
15040 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15041 if (FAILED(rc)) return rc;
15042
15043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15044
15045 /* Let the OS type select 64-bit ness. */
15046 mHWData->mLongMode = osType->i_is64Bit()
15047 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15048
15049 /* Apply network adapters defaults */
15050 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15051 mNetworkAdapters[slot]->i_applyDefaults(osType);
15052
15053 /* Apply serial port defaults */
15054 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15055 mSerialPorts[slot]->i_applyDefaults(osType);
15056
15057 /* Apply parallel port defaults - not OS dependent*/
15058 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15059 mParallelPorts[slot]->i_applyDefaults();
15060
15061
15062 /* Let the OS type enable the X2APIC */
15063 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15064
15065 /* This one covers IOAPICEnabled. */
15066 mBIOSSettings->i_applyDefaults(osType);
15067
15068 /* Initialize default record settings. */
15069 mRecordingSettings->i_applyDefaults();
15070
15071 /* Initialize default BIOS settings here */
15072 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15073 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15074
15075 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15076 if (FAILED(rc)) return rc;
15077
15078 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15079 if (FAILED(rc)) return rc;
15080
15081 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15082 if (FAILED(rc)) return rc;
15083
15084 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15085 if (FAILED(rc)) return rc;
15086
15087 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15088 if (FAILED(rc)) return rc;
15089
15090 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15091 if (FAILED(rc)) return rc;
15092
15093 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15094 if (FAILED(rc)) return rc;
15095
15096 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15097 if (FAILED(rc)) return rc;
15098
15099 BOOL mRTCUseUTC;
15100 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15101 if (FAILED(rc)) return rc;
15102
15103 setRTCUseUTC(mRTCUseUTC);
15104 if (FAILED(rc)) return rc;
15105
15106 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15107 if (FAILED(rc)) return rc;
15108
15109 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15110 if (FAILED(rc)) return rc;
15111
15112 /* Audio stuff. */
15113 AudioCodecType_T audioCodec;
15114 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15115 if (FAILED(rc)) return rc;
15116
15117 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15118 if (FAILED(rc)) return rc;
15119
15120 AudioControllerType_T audioController;
15121 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15122 if (FAILED(rc)) return rc;
15123
15124 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15125 if (FAILED(rc)) return rc;
15126
15127 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15128 if (FAILED(rc)) return rc;
15129
15130 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15131 if (FAILED(rc)) return rc;
15132
15133 /* Storage Controllers */
15134 StorageControllerType_T hdStorageControllerType;
15135 StorageBus_T hdStorageBusType;
15136 StorageControllerType_T dvdStorageControllerType;
15137 StorageBus_T dvdStorageBusType;
15138 BOOL recommendedFloppy;
15139 ComPtr<IStorageController> floppyController;
15140 ComPtr<IStorageController> hdController;
15141 ComPtr<IStorageController> dvdController;
15142 Utf8Str strFloppyName, strDVDName, strHDName;
15143
15144 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15145 strFloppyName = Bstr("Floppy 1").raw();
15146 strDVDName = Bstr("DVD 1").raw();
15147 strHDName = Bstr("HDD 1").raw();
15148
15149 /* Floppy recommended? add one. */
15150 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15151 if (FAILED(rc)) return rc;
15152 if (recommendedFloppy)
15153 {
15154 rc = addStorageController(strFloppyName,
15155 StorageBus_Floppy,
15156 floppyController);
15157 if (FAILED(rc)) return rc;
15158 }
15159
15160 /* Setup one DVD storage controller. */
15161 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15162 if (FAILED(rc)) return rc;
15163
15164 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15165 if (FAILED(rc)) return rc;
15166
15167 rc = addStorageController(strDVDName,
15168 dvdStorageBusType,
15169 dvdController);
15170 if (FAILED(rc)) return rc;
15171
15172 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15173 if (FAILED(rc)) return rc;
15174
15175 /* Setup one HDD storage controller. */
15176 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15177 if (FAILED(rc)) return rc;
15178
15179 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15180 if (FAILED(rc)) return rc;
15181
15182 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15183 {
15184 rc = addStorageController(strHDName,
15185 hdStorageBusType,
15186 hdController);
15187 if (FAILED(rc)) return rc;
15188
15189 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15190 if (FAILED(rc)) return rc;
15191 }
15192 else
15193 {
15194 /* The HD controller is the same as DVD: */
15195 hdController = dvdController;
15196 strHDName = Bstr("DVD 1").raw();
15197 }
15198
15199 /* Limit the AHCI port count if it's used because windows has trouble with
15200 * too many ports and other guest (OS X in particular) may take extra long
15201 * boot: */
15202
15203 // pParent = static_cast<Medium*>(aP)
15204 IStorageController *temp = hdController;
15205 ComObjPtr<StorageController> storageController;
15206 storageController = static_cast<StorageController *>(temp);
15207
15208 // tempHDController = aHDController;
15209 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15210 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15211 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15212 storageController->COMSETTER(PortCount)(1);
15213
15214 /* USB stuff */
15215
15216 bool ohciEnabled = false;
15217
15218 ComPtr<IUSBController> usbController;
15219 BOOL recommendedUSB3;
15220 BOOL recommendedUSB;
15221 BOOL usbProxyAvailable;
15222
15223 getUSBProxyAvailable(&usbProxyAvailable);
15224 if (FAILED(rc)) return rc;
15225
15226 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15227 if (FAILED(rc)) return rc;
15228 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15229 if (FAILED(rc)) return rc;
15230
15231 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15232 {
15233#ifdef VBOX_WITH_EXTPACK
15234 /* USB 3.0 is only available if the proper ExtPack is installed. */
15235 ExtPackManager *aManager = mParent->i_getExtPackManager();
15236 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15237 {
15238 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15239 if (FAILED(rc)) return rc;
15240
15241 /* xHci includes OHCI */
15242 ohciEnabled = true;
15243 }
15244#endif
15245 }
15246 if ( !ohciEnabled
15247 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15248 {
15249 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15250 if (FAILED(rc)) return rc;
15251 ohciEnabled = true;
15252
15253#ifdef VBOX_WITH_EXTPACK
15254 /* USB 2.0 is only available if the proper ExtPack is installed.
15255 * Note. Configuring EHCI here and providing messages about
15256 * the missing extpack isn't exactly clean, but it is a
15257 * necessary evil to patch over legacy compatability issues
15258 * introduced by the new distribution model. */
15259 ExtPackManager *manager = mParent->i_getExtPackManager();
15260 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15261 {
15262 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15263 if (FAILED(rc)) return rc;
15264 }
15265#endif
15266 }
15267
15268 /* Set recommended human interface device types: */
15269 BOOL recommendedUSBHID;
15270 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15271 if (FAILED(rc)) return rc;
15272
15273 if (recommendedUSBHID)
15274 {
15275 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15276 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15277 if (!ohciEnabled && !usbDeviceFilters.isNull())
15278 {
15279 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15280 if (FAILED(rc)) return rc;
15281 }
15282 }
15283
15284 BOOL recommendedUSBTablet;
15285 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15286 if (FAILED(rc)) return rc;
15287
15288 if (recommendedUSBTablet)
15289 {
15290 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15291 if (!ohciEnabled && !usbDeviceFilters.isNull())
15292 {
15293 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15294 if (FAILED(rc)) return rc;
15295 }
15296 }
15297 return S_OK;
15298}
15299
15300/* This isn't handled entirely by the wrapper generator yet. */
15301#ifdef VBOX_WITH_XPCOM
15302NS_DECL_CLASSINFO(SessionMachine)
15303NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15304
15305NS_DECL_CLASSINFO(SnapshotMachine)
15306NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15307#endif
Note: See TracBrowser for help on using the repository browser.

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