VirtualBox

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

Last change on this file since 75262 was 75251, checked in by vboxsync, 6 years ago

Capturing: Separated capturing settings into new interfaces ICaptureSettings and ICaptureScreenSettings to unload stuff from IMachine; a lot of internal interface / code cleanups. Also see #9286. Work in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 529.3 KB
Line 
1/* $Id: MachineImpl.cpp 75251 2018-11-05 17:55:29Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47#include "MachineImplMoveVM.h"
48#include "ExtPackManagerImpl.h"
49
50// generated header
51#include "VBoxEvents.h"
52
53#ifdef VBOX_WITH_USB
54# include "USBProxyService.h"
55#endif
56
57#include "AutoCaller.h"
58#include "HashedPw.h"
59#include "Performance.h"
60
61#include <iprt/asm.h>
62#include <iprt/path.h>
63#include <iprt/dir.h>
64#include <iprt/env.h>
65#include <iprt/lockvalidator.h>
66#include <iprt/process.h>
67#include <iprt/cpp/utils.h>
68#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
69#include <iprt/sha.h>
70#include <iprt/string.h>
71#include <iprt/ctype.h>
72
73#include <VBox/com/array.h>
74#include <VBox/com/list.h>
75
76#include <VBox/err.h>
77#include <VBox/param.h>
78#include <VBox/settings.h>
79#include <VBox/vmm/ssm.h>
80
81#ifdef VBOX_WITH_GUEST_PROPS
82# include <VBox/HostServices/GuestPropertySvc.h>
83# include <VBox/com/array.h>
84#endif
85
86#include "VBox/com/MultiResult.h"
87
88#include <algorithm>
89
90#ifdef VBOX_WITH_DTRACE_R3_MAIN
91# include "dtrace/VBoxAPI.h"
92#endif
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mLockType = LockType_Null;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mHWVirtExEnabled = true;
171 mHWVirtExNestedPagingEnabled = true;
172#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
173 mHWVirtExLargePagesEnabled = true;
174#else
175 /* Not supported on 32 bits hosts. */
176 mHWVirtExLargePagesEnabled = false;
177#endif
178 mHWVirtExVPIDEnabled = true;
179 mHWVirtExUXEnabled = true;
180 mHWVirtExForceEnabled = false;
181 mHWVirtExUseNativeApi = false;
182#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
183 mPAEEnabled = true;
184#else
185 mPAEEnabled = false;
186#endif
187 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
188 mTripleFaultReset = false;
189 mAPIC = true;
190 mX2APIC = false;
191 mIBPBOnVMExit = false;
192 mIBPBOnVMEntry = false;
193 mSpecCtrl = false;
194 mSpecCtrlByHost = false;
195 mNestedHWVirt = false;
196 mHPETEnabled = false;
197 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
198 mCpuIdPortabilityLevel = 0;
199 mCpuProfile = "host";
200
201 /* default boot order: floppy - DVD - HDD */
202 mBootOrder[0] = DeviceType_Floppy;
203 mBootOrder[1] = DeviceType_DVD;
204 mBootOrder[2] = DeviceType_HardDisk;
205 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
206 mBootOrder[i] = DeviceType_Null;
207
208 mClipboardMode = ClipboardMode_Disabled;
209 mDnDMode = DnDMode_Disabled;
210
211 mFirmwareType = FirmwareType_BIOS;
212 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
213 mPointingHIDType = PointingHIDType_PS2Mouse;
214 mChipsetType = ChipsetType_PIIX3;
215 mParavirtProvider = ParavirtProvider_Default;
216 mEmulatedUSBCardReaderEnabled = FALSE;
217
218 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
219 mCPUAttached[i] = false;
220
221 mIOCacheEnabled = true;
222 mIOCacheSize = 5; /* 5MB */
223}
224
225Machine::HWData::~HWData()
226{
227}
228
229/////////////////////////////////////////////////////////////////////////////
230// Machine class
231/////////////////////////////////////////////////////////////////////////////
232
233// constructor / destructor
234/////////////////////////////////////////////////////////////////////////////
235
236Machine::Machine() :
237#ifdef VBOX_WITH_RESOURCE_USAGE_API
238 mCollectorGuest(NULL),
239#endif
240 mPeer(NULL),
241 mParent(NULL),
242 mSerialPorts(),
243 mParallelPorts(),
244 uRegistryNeedsSaving(0)
245{}
246
247Machine::~Machine()
248{}
249
250HRESULT Machine::FinalConstruct()
251{
252 LogFlowThisFunc(("\n"));
253 return BaseFinalConstruct();
254}
255
256void Machine::FinalRelease()
257{
258 LogFlowThisFunc(("\n"));
259 uninit();
260 BaseFinalRelease();
261}
262
263/**
264 * Initializes a new machine instance; this init() variant creates a new, empty machine.
265 * This gets called from VirtualBox::CreateMachine().
266 *
267 * @param aParent Associated parent object
268 * @param strConfigFile Local file system path to the VM settings file (can
269 * be relative to the VirtualBox config directory).
270 * @param strName name for the machine
271 * @param llGroups list of groups for the machine
272 * @param strOsType OS Type string (stored as is if aOsType is NULL).
273 * @param aOsType OS Type of this machine or NULL.
274 * @param aId UUID for the new machine.
275 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
276 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
277 * scheme (includes the UUID).
278 *
279 * @return Success indicator. if not S_OK, the machine object is invalid
280 */
281HRESULT Machine::init(VirtualBox *aParent,
282 const Utf8Str &strConfigFile,
283 const Utf8Str &strName,
284 const StringsList &llGroups,
285 const Utf8Str &strOsType,
286 GuestOSType *aOsType,
287 const Guid &aId,
288 bool fForceOverwrite,
289 bool fDirectoryIncludesUUID)
290{
291 LogFlowThisFuncEnter();
292 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
293
294 /* Enclose the state transition NotReady->InInit->Ready */
295 AutoInitSpan autoInitSpan(this);
296 AssertReturn(autoInitSpan.isOk(), E_FAIL);
297
298 HRESULT rc = initImpl(aParent, strConfigFile);
299 if (FAILED(rc)) return rc;
300
301 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
302 if (FAILED(rc)) return rc;
303
304 if (SUCCEEDED(rc))
305 {
306 // create an empty machine config
307 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
308
309 rc = initDataAndChildObjects();
310 }
311
312 if (SUCCEEDED(rc))
313 {
314 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
315 mData->mAccessible = TRUE;
316
317 unconst(mData->mUuid) = aId;
318
319 mUserData->s.strName = strName;
320
321 if (llGroups.size())
322 mUserData->s.llGroups = llGroups;
323
324 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
325 // the "name sync" flag determines whether the machine directory gets renamed along
326 // with the machine file; say so if the settings file name is the same as the
327 // settings file parent directory (machine directory)
328 mUserData->s.fNameSync = i_isInOwnDir();
329
330 // initialize the default snapshots folder
331 rc = COMSETTER(SnapshotFolder)(NULL);
332 AssertComRC(rc);
333
334 if (aOsType)
335 {
336 /* Store OS type */
337 mUserData->s.strOsType = aOsType->i_id();
338
339 /* Let the OS type select 64-bit ness. */
340 mHWData->mLongMode = aOsType->i_is64Bit()
341 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
342
343 /* Let the OS type enable the X2APIC */
344 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
345 }
346 else if (!strOsType.isEmpty())
347 {
348 /* Store OS type */
349 mUserData->s.strOsType = strOsType;
350
351 /* No guest OS type object. Pick some plausible defaults which the
352 * host can handle. There's no way to know or validate anything. */
353 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
354 mHWData->mX2APIC = false;
355 }
356
357 /* Apply BIOS defaults. */
358 mBIOSSettings->i_applyDefaults(aOsType);
359
360 /* Apply capture defaults. */
361 mCaptureSettings->i_applyDefaults();
362
363 /* Apply network adapters defaults */
364 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
365 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
366
367 /* Apply serial port defaults */
368 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
369 mSerialPorts[slot]->i_applyDefaults(aOsType);
370
371 /* Apply parallel port defaults */
372 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
373 mParallelPorts[slot]->i_applyDefaults();
374
375 /* At this point the changing of the current state modification
376 * flag is allowed. */
377 i_allowStateModification();
378
379 /* commit all changes made during the initialization */
380 i_commit();
381 }
382
383 /* Confirm a successful initialization when it's the case */
384 if (SUCCEEDED(rc))
385 {
386 if (mData->mAccessible)
387 autoInitSpan.setSucceeded();
388 else
389 autoInitSpan.setLimited();
390 }
391
392 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
393 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
394 mData->mRegistered,
395 mData->mAccessible,
396 rc));
397
398 LogFlowThisFuncLeave();
399
400 return rc;
401}
402
403/**
404 * Initializes a new instance with data from machine XML (formerly Init_Registered).
405 * Gets called in two modes:
406 *
407 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
408 * UUID is specified and we mark the machine as "registered";
409 *
410 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
411 * and the machine remains unregistered until RegisterMachine() is called.
412 *
413 * @param aParent Associated parent object
414 * @param strConfigFile Local file system path to the VM settings file (can
415 * be relative to the VirtualBox config directory).
416 * @param aId UUID of the machine or NULL (see above).
417 *
418 * @return Success indicator. if not S_OK, the machine object is invalid
419 */
420HRESULT Machine::initFromSettings(VirtualBox *aParent,
421 const Utf8Str &strConfigFile,
422 const Guid *aId)
423{
424 LogFlowThisFuncEnter();
425 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
426
427 /* Enclose the state transition NotReady->InInit->Ready */
428 AutoInitSpan autoInitSpan(this);
429 AssertReturn(autoInitSpan.isOk(), E_FAIL);
430
431 HRESULT rc = initImpl(aParent, strConfigFile);
432 if (FAILED(rc)) return rc;
433
434 if (aId)
435 {
436 // loading a registered VM:
437 unconst(mData->mUuid) = *aId;
438 mData->mRegistered = TRUE;
439 // now load the settings from XML:
440 rc = i_registeredInit();
441 // this calls initDataAndChildObjects() and loadSettings()
442 }
443 else
444 {
445 // opening an unregistered VM (VirtualBox::OpenMachine()):
446 rc = initDataAndChildObjects();
447
448 if (SUCCEEDED(rc))
449 {
450 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
451 mData->mAccessible = TRUE;
452
453 try
454 {
455 // load and parse machine XML; this will throw on XML or logic errors
456 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
457
458 // reject VM UUID duplicates, they can happen if someone
459 // tries to register an already known VM config again
460 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
461 true /* fPermitInaccessible */,
462 false /* aDoSetError */,
463 NULL) != VBOX_E_OBJECT_NOT_FOUND)
464 {
465 throw setError(E_FAIL,
466 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
467 mData->m_strConfigFile.c_str());
468 }
469
470 // use UUID from machine config
471 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
472
473 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
474 NULL /* puuidRegistry */);
475 if (FAILED(rc)) throw rc;
476
477 /* At this point the changing of the current state modification
478 * flag is allowed. */
479 i_allowStateModification();
480
481 i_commit();
482 }
483 catch (HRESULT err)
484 {
485 /* we assume that error info is set by the thrower */
486 rc = err;
487 }
488 catch (...)
489 {
490 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
491 }
492 }
493 }
494
495 /* Confirm a successful initialization when it's the case */
496 if (SUCCEEDED(rc))
497 {
498 if (mData->mAccessible)
499 autoInitSpan.setSucceeded();
500 else
501 {
502 autoInitSpan.setLimited();
503
504 // uninit media from this machine's media registry, or else
505 // reloading the settings will fail
506 mParent->i_unregisterMachineMedia(i_getId());
507 }
508 }
509
510 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
511 "rc=%08X\n",
512 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
513 mData->mRegistered, mData->mAccessible, rc));
514
515 LogFlowThisFuncLeave();
516
517 return rc;
518}
519
520/**
521 * Initializes a new instance from a machine config that is already in memory
522 * (import OVF case). Since we are importing, the UUID in the machine
523 * config is ignored and we always generate a fresh one.
524 *
525 * @param aParent Associated parent object.
526 * @param strName Name for the new machine; this overrides what is specified in config.
527 * @param strSettingsFilename File name of .vbox file.
528 * @param config Machine configuration loaded and parsed from XML.
529 *
530 * @return Success indicator. if not S_OK, the machine object is invalid
531 */
532HRESULT Machine::init(VirtualBox *aParent,
533 const Utf8Str &strName,
534 const Utf8Str &strSettingsFilename,
535 const settings::MachineConfigFile &config)
536{
537 LogFlowThisFuncEnter();
538
539 /* Enclose the state transition NotReady->InInit->Ready */
540 AutoInitSpan autoInitSpan(this);
541 AssertReturn(autoInitSpan.isOk(), E_FAIL);
542
543 HRESULT rc = initImpl(aParent, strSettingsFilename);
544 if (FAILED(rc)) return rc;
545
546 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
547 if (FAILED(rc)) return rc;
548
549 rc = initDataAndChildObjects();
550
551 if (SUCCEEDED(rc))
552 {
553 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
554 mData->mAccessible = TRUE;
555
556 // create empty machine config for instance data
557 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
558
559 // generate fresh UUID, ignore machine config
560 unconst(mData->mUuid).create();
561
562 rc = i_loadMachineDataFromSettings(config,
563 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
564
565 // override VM name as well, it may be different
566 mUserData->s.strName = strName;
567
568 if (SUCCEEDED(rc))
569 {
570 /* At this point the changing of the current state modification
571 * flag is allowed. */
572 i_allowStateModification();
573
574 /* commit all changes made during the initialization */
575 i_commit();
576 }
577 }
578
579 /* Confirm a successful initialization when it's the case */
580 if (SUCCEEDED(rc))
581 {
582 if (mData->mAccessible)
583 autoInitSpan.setSucceeded();
584 else
585 {
586 /* Ignore all errors from unregistering, they would destroy
587- * the more interesting error information we already have,
588- * pinpointing the issue with the VM config. */
589 ErrorInfoKeeper eik;
590
591 autoInitSpan.setLimited();
592
593 // uninit media from this machine's media registry, or else
594 // reloading the settings will fail
595 mParent->i_unregisterMachineMedia(i_getId());
596 }
597 }
598
599 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
600 "rc=%08X\n",
601 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
602 mData->mRegistered, mData->mAccessible, rc));
603
604 LogFlowThisFuncLeave();
605
606 return rc;
607}
608
609/**
610 * Shared code between the various init() implementations.
611 * @param aParent The VirtualBox object.
612 * @param strConfigFile Settings file.
613 * @return
614 */
615HRESULT Machine::initImpl(VirtualBox *aParent,
616 const Utf8Str &strConfigFile)
617{
618 LogFlowThisFuncEnter();
619
620 AssertReturn(aParent, E_INVALIDARG);
621 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
622
623 HRESULT rc = S_OK;
624
625 /* share the parent weakly */
626 unconst(mParent) = aParent;
627
628 /* allocate the essential machine data structure (the rest will be
629 * allocated later by initDataAndChildObjects() */
630 mData.allocate();
631
632 /* memorize the config file name (as provided) */
633 mData->m_strConfigFile = strConfigFile;
634
635 /* get the full file name */
636 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
637 if (RT_FAILURE(vrc1))
638 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
639 tr("Invalid machine settings file name '%s' (%Rrc)"),
640 strConfigFile.c_str(),
641 vrc1);
642
643 LogFlowThisFuncLeave();
644
645 return rc;
646}
647
648/**
649 * Tries to create a machine settings file in the path stored in the machine
650 * instance data. Used when a new machine is created to fail gracefully if
651 * the settings file could not be written (e.g. because machine dir is read-only).
652 * @return
653 */
654HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
655{
656 HRESULT rc = S_OK;
657
658 // when we create a new machine, we must be able to create the settings file
659 RTFILE f = NIL_RTFILE;
660 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
661 if ( RT_SUCCESS(vrc)
662 || vrc == VERR_SHARING_VIOLATION
663 )
664 {
665 if (RT_SUCCESS(vrc))
666 RTFileClose(f);
667 if (!fForceOverwrite)
668 rc = setError(VBOX_E_FILE_ERROR,
669 tr("Machine settings file '%s' already exists"),
670 mData->m_strConfigFileFull.c_str());
671 else
672 {
673 /* try to delete the config file, as otherwise the creation
674 * of a new settings file will fail. */
675 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
676 if (RT_FAILURE(vrc2))
677 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
678 tr("Could not delete the existing settings file '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(), vrc2);
680 }
681 }
682 else if ( vrc != VERR_FILE_NOT_FOUND
683 && vrc != VERR_PATH_NOT_FOUND
684 )
685 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
686 tr("Invalid machine settings file name '%s' (%Rrc)"),
687 mData->m_strConfigFileFull.c_str(),
688 vrc);
689 return rc;
690}
691
692/**
693 * Initializes the registered machine by loading the settings file.
694 * This method is separated from #init() in order to make it possible to
695 * retry the operation after VirtualBox startup instead of refusing to
696 * startup the whole VirtualBox server in case if the settings file of some
697 * registered VM is invalid or inaccessible.
698 *
699 * @note Must be always called from this object's write lock
700 * (unless called from #init() that doesn't need any locking).
701 * @note Locks the mUSBController method for writing.
702 * @note Subclasses must not call this method.
703 */
704HRESULT Machine::i_registeredInit()
705{
706 AssertReturn(!i_isSessionMachine(), E_FAIL);
707 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
708 AssertReturn(mData->mUuid.isValid(), E_FAIL);
709 AssertReturn(!mData->mAccessible, E_FAIL);
710
711 HRESULT rc = initDataAndChildObjects();
712
713 if (SUCCEEDED(rc))
714 {
715 /* Temporarily reset the registered flag in order to let setters
716 * potentially called from loadSettings() succeed (isMutable() used in
717 * all setters will return FALSE for a Machine instance if mRegistered
718 * is TRUE). */
719 mData->mRegistered = FALSE;
720
721 try
722 {
723 // load and parse machine XML; this will throw on XML or logic errors
724 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
725
726 if (mData->mUuid != mData->pMachineConfigFile->uuid)
727 throw setError(E_FAIL,
728 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
729 mData->pMachineConfigFile->uuid.raw(),
730 mData->m_strConfigFileFull.c_str(),
731 mData->mUuid.toString().c_str(),
732 mParent->i_settingsFilePath().c_str());
733
734 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
735 NULL /* const Guid *puuidRegistry */);
736 if (FAILED(rc)) throw rc;
737 }
738 catch (HRESULT err)
739 {
740 /* we assume that error info is set by the thrower */
741 rc = err;
742 }
743 catch (...)
744 {
745 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
746 }
747
748 /* Restore the registered flag (even on failure) */
749 mData->mRegistered = TRUE;
750 }
751
752 if (SUCCEEDED(rc))
753 {
754 /* Set mAccessible to TRUE only if we successfully locked and loaded
755 * the settings file */
756 mData->mAccessible = TRUE;
757
758 /* commit all changes made during loading the settings file */
759 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
760 /// @todo r=klaus for some reason the settings loading logic backs up
761 // the settings, and therefore a commit is needed. Should probably be changed.
762 }
763 else
764 {
765 /* If the machine is registered, then, instead of returning a
766 * failure, we mark it as inaccessible and set the result to
767 * success to give it a try later */
768
769 /* fetch the current error info */
770 mData->mAccessError = com::ErrorInfo();
771 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
772
773 /* rollback all changes */
774 i_rollback(false /* aNotify */);
775
776 // uninit media from this machine's media registry, or else
777 // reloading the settings will fail
778 mParent->i_unregisterMachineMedia(i_getId());
779
780 /* uninitialize the common part to make sure all data is reset to
781 * default (null) values */
782 uninitDataAndChildObjects();
783
784 rc = S_OK;
785 }
786
787 return rc;
788}
789
790/**
791 * Uninitializes the instance.
792 * Called either from FinalRelease() or by the parent when it gets destroyed.
793 *
794 * @note The caller of this method must make sure that this object
795 * a) doesn't have active callers on the current thread and b) is not locked
796 * by the current thread; otherwise uninit() will hang either a) due to
797 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
798 * a dead-lock caused by this thread waiting for all callers on the other
799 * threads are done but preventing them from doing so by holding a lock.
800 */
801void Machine::uninit()
802{
803 LogFlowThisFuncEnter();
804
805 Assert(!isWriteLockOnCurrentThread());
806
807 Assert(!uRegistryNeedsSaving);
808 if (uRegistryNeedsSaving)
809 {
810 AutoCaller autoCaller(this);
811 if (SUCCEEDED(autoCaller.rc()))
812 {
813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
814 i_saveSettings(NULL, Machine::SaveS_Force);
815 }
816 }
817
818 /* Enclose the state transition Ready->InUninit->NotReady */
819 AutoUninitSpan autoUninitSpan(this);
820 if (autoUninitSpan.uninitDone())
821 return;
822
823 Assert(!i_isSnapshotMachine());
824 Assert(!i_isSessionMachine());
825 Assert(!!mData);
826
827 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
828 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
829
830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
831
832 if (!mData->mSession.mMachine.isNull())
833 {
834 /* Theoretically, this can only happen if the VirtualBox server has been
835 * terminated while there were clients running that owned open direct
836 * sessions. Since in this case we are definitely called by
837 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
838 * won't happen on the client watcher thread (because it has a
839 * VirtualBox caller for the duration of the
840 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
841 * cannot happen until the VirtualBox caller is released). This is
842 * important, because SessionMachine::uninit() cannot correctly operate
843 * after we return from this method (it expects the Machine instance is
844 * still valid). We'll call it ourselves below.
845 */
846 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
847 (SessionMachine*)mData->mSession.mMachine));
848
849 if (Global::IsOnlineOrTransient(mData->mMachineState))
850 {
851 Log1WarningThisFunc(("Setting state to Aborted!\n"));
852 /* set machine state using SessionMachine reimplementation */
853 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
854 }
855
856 /*
857 * Uninitialize SessionMachine using public uninit() to indicate
858 * an unexpected uninitialization.
859 */
860 mData->mSession.mMachine->uninit();
861 /* SessionMachine::uninit() must set mSession.mMachine to null */
862 Assert(mData->mSession.mMachine.isNull());
863 }
864
865 // uninit media from this machine's media registry, if they're still there
866 Guid uuidMachine(i_getId());
867
868 /* the lock is no more necessary (SessionMachine is uninitialized) */
869 alock.release();
870
871 /* XXX This will fail with
872 * "cannot be closed because it is still attached to 1 virtual machines"
873 * because at this point we did not call uninitDataAndChildObjects() yet
874 * and therefore also removeBackReference() for all these mediums was not called! */
875
876 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
877 mParent->i_unregisterMachineMedia(uuidMachine);
878
879 // has machine been modified?
880 if (mData->flModifications)
881 {
882 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
883 i_rollback(false /* aNotify */);
884 }
885
886 if (mData->mAccessible)
887 uninitDataAndChildObjects();
888
889 /* free the essential data structure last */
890 mData.free();
891
892 LogFlowThisFuncLeave();
893}
894
895// Wrapped IMachine properties
896/////////////////////////////////////////////////////////////////////////////
897HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
898{
899 /* mParent is constant during life time, no need to lock */
900 ComObjPtr<VirtualBox> pVirtualBox(mParent);
901 aParent = pVirtualBox;
902
903 return S_OK;
904}
905
906
907HRESULT Machine::getAccessible(BOOL *aAccessible)
908{
909 /* In some cases (medium registry related), it is necessary to be able to
910 * go through the list of all machines. Happens when an inaccessible VM
911 * has a sensible medium registry. */
912 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
914
915 HRESULT rc = S_OK;
916
917 if (!mData->mAccessible)
918 {
919 /* try to initialize the VM once more if not accessible */
920
921 AutoReinitSpan autoReinitSpan(this);
922 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
923
924#ifdef DEBUG
925 LogFlowThisFunc(("Dumping media backreferences\n"));
926 mParent->i_dumpAllBackRefs();
927#endif
928
929 if (mData->pMachineConfigFile)
930 {
931 // reset the XML file to force loadSettings() (called from i_registeredInit())
932 // to parse it again; the file might have changed
933 delete mData->pMachineConfigFile;
934 mData->pMachineConfigFile = NULL;
935 }
936
937 rc = i_registeredInit();
938
939 if (SUCCEEDED(rc) && mData->mAccessible)
940 {
941 autoReinitSpan.setSucceeded();
942
943 /* make sure interesting parties will notice the accessibility
944 * state change */
945 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
946 mParent->i_onMachineDataChange(mData->mUuid);
947 }
948 }
949
950 if (SUCCEEDED(rc))
951 *aAccessible = mData->mAccessible;
952
953 LogFlowThisFuncLeave();
954
955 return rc;
956}
957
958HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
959{
960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
961
962 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
963 {
964 /* return shortly */
965 aAccessError = NULL;
966 return S_OK;
967 }
968
969 HRESULT rc = S_OK;
970
971 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
972 rc = errorInfo.createObject();
973 if (SUCCEEDED(rc))
974 {
975 errorInfo->init(mData->mAccessError.getResultCode(),
976 mData->mAccessError.getInterfaceID().ref(),
977 Utf8Str(mData->mAccessError.getComponent()).c_str(),
978 Utf8Str(mData->mAccessError.getText()));
979 aAccessError = errorInfo;
980 }
981
982 return rc;
983}
984
985HRESULT Machine::getName(com::Utf8Str &aName)
986{
987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
988
989 aName = mUserData->s.strName;
990
991 return S_OK;
992}
993
994HRESULT Machine::setName(const com::Utf8Str &aName)
995{
996 // prohibit setting a UUID only as the machine name, or else it can
997 // never be found by findMachine()
998 Guid test(aName);
999
1000 if (test.isValid())
1001 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1002
1003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1004
1005 HRESULT rc = i_checkStateDependency(MutableStateDep);
1006 if (FAILED(rc)) return rc;
1007
1008 i_setModified(IsModified_MachineData);
1009 mUserData.backup();
1010 mUserData->s.strName = aName;
1011
1012 return S_OK;
1013}
1014
1015HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1016{
1017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1018
1019 aDescription = mUserData->s.strDescription;
1020
1021 return S_OK;
1022}
1023
1024HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1025{
1026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1027
1028 // this can be done in principle in any state as it doesn't affect the VM
1029 // significantly, but play safe by not messing around while complex
1030 // activities are going on
1031 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1032 if (FAILED(rc)) return rc;
1033
1034 i_setModified(IsModified_MachineData);
1035 mUserData.backup();
1036 mUserData->s.strDescription = aDescription;
1037
1038 return S_OK;
1039}
1040
1041HRESULT Machine::getId(com::Guid &aId)
1042{
1043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1044
1045 aId = mData->mUuid;
1046
1047 return S_OK;
1048}
1049
1050HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1051{
1052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1053 aGroups.resize(mUserData->s.llGroups.size());
1054 size_t i = 0;
1055 for (StringsList::const_iterator
1056 it = mUserData->s.llGroups.begin();
1057 it != mUserData->s.llGroups.end();
1058 ++it, ++i)
1059 aGroups[i] = (*it);
1060
1061 return S_OK;
1062}
1063
1064HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1065{
1066 StringsList llGroups;
1067 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1068 if (FAILED(rc))
1069 return rc;
1070
1071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1072
1073 rc = i_checkStateDependency(MutableOrSavedStateDep);
1074 if (FAILED(rc)) return rc;
1075
1076 i_setModified(IsModified_MachineData);
1077 mUserData.backup();
1078 mUserData->s.llGroups = llGroups;
1079
1080 return S_OK;
1081}
1082
1083HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1084{
1085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1086
1087 aOSTypeId = mUserData->s.strOsType;
1088
1089 return S_OK;
1090}
1091
1092HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1093{
1094 /* look up the object by Id to check it is valid */
1095 ComObjPtr<GuestOSType> pGuestOSType;
1096 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1097
1098 /* when setting, always use the "etalon" value for consistency -- lookup
1099 * by ID is case-insensitive and the input value may have different case */
1100 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1101
1102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 HRESULT rc = i_checkStateDependency(MutableStateDep);
1105 if (FAILED(rc)) return rc;
1106
1107 i_setModified(IsModified_MachineData);
1108 mUserData.backup();
1109 mUserData->s.strOsType = osTypeId;
1110
1111 return S_OK;
1112}
1113
1114HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1115{
1116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1117
1118 *aFirmwareType = mHWData->mFirmwareType;
1119
1120 return S_OK;
1121}
1122
1123HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1124{
1125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1126
1127 HRESULT rc = i_checkStateDependency(MutableStateDep);
1128 if (FAILED(rc)) return rc;
1129
1130 i_setModified(IsModified_MachineData);
1131 mHWData.backup();
1132 mHWData->mFirmwareType = aFirmwareType;
1133
1134 return S_OK;
1135}
1136
1137HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1138{
1139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1140
1141 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1142
1143 return S_OK;
1144}
1145
1146HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1147{
1148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1149
1150 HRESULT rc = i_checkStateDependency(MutableStateDep);
1151 if (FAILED(rc)) return rc;
1152
1153 i_setModified(IsModified_MachineData);
1154 mHWData.backup();
1155 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1156
1157 return S_OK;
1158}
1159
1160HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1161{
1162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 *aPointingHIDType = mHWData->mPointingHIDType;
1165
1166 return S_OK;
1167}
1168
1169HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1170{
1171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 HRESULT rc = i_checkStateDependency(MutableStateDep);
1174 if (FAILED(rc)) return rc;
1175
1176 i_setModified(IsModified_MachineData);
1177 mHWData.backup();
1178 mHWData->mPointingHIDType = aPointingHIDType;
1179
1180 return S_OK;
1181}
1182
1183HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1184{
1185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 *aChipsetType = mHWData->mChipsetType;
1188
1189 return S_OK;
1190}
1191
1192HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1193{
1194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1195
1196 HRESULT rc = i_checkStateDependency(MutableStateDep);
1197 if (FAILED(rc)) return rc;
1198
1199 if (aChipsetType != mHWData->mChipsetType)
1200 {
1201 i_setModified(IsModified_MachineData);
1202 mHWData.backup();
1203 mHWData->mChipsetType = aChipsetType;
1204
1205 // Resize network adapter array, to be finalized on commit/rollback.
1206 // We must not throw away entries yet, otherwise settings are lost
1207 // without a way to roll back.
1208 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1209 size_t oldCount = mNetworkAdapters.size();
1210 if (newCount > oldCount)
1211 {
1212 mNetworkAdapters.resize(newCount);
1213 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1214 {
1215 unconst(mNetworkAdapters[slot]).createObject();
1216 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1217 }
1218 }
1219 }
1220
1221 return S_OK;
1222}
1223
1224HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1225{
1226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1227
1228 aParavirtDebug = mHWData->mParavirtDebug;
1229 return S_OK;
1230}
1231
1232HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1233{
1234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1235
1236 HRESULT rc = i_checkStateDependency(MutableStateDep);
1237 if (FAILED(rc)) return rc;
1238
1239 /** @todo Parse/validate options? */
1240 if (aParavirtDebug != mHWData->mParavirtDebug)
1241 {
1242 i_setModified(IsModified_MachineData);
1243 mHWData.backup();
1244 mHWData->mParavirtDebug = aParavirtDebug;
1245 }
1246
1247 return S_OK;
1248}
1249
1250HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1251{
1252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 *aParavirtProvider = mHWData->mParavirtProvider;
1255
1256 return S_OK;
1257}
1258
1259HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1260{
1261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 HRESULT rc = i_checkStateDependency(MutableStateDep);
1264 if (FAILED(rc)) return rc;
1265
1266 if (aParavirtProvider != mHWData->mParavirtProvider)
1267 {
1268 i_setModified(IsModified_MachineData);
1269 mHWData.backup();
1270 mHWData->mParavirtProvider = aParavirtProvider;
1271 }
1272
1273 return S_OK;
1274}
1275
1276HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1277{
1278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 *aParavirtProvider = mHWData->mParavirtProvider;
1281 switch (mHWData->mParavirtProvider)
1282 {
1283 case ParavirtProvider_None:
1284 case ParavirtProvider_HyperV:
1285 case ParavirtProvider_KVM:
1286 case ParavirtProvider_Minimal:
1287 break;
1288
1289 /* Resolve dynamic provider types to the effective types. */
1290 default:
1291 {
1292 ComObjPtr<GuestOSType> pGuestOSType;
1293 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1294 pGuestOSType);
1295 if (FAILED(hrc2) || pGuestOSType.isNull())
1296 {
1297 *aParavirtProvider = ParavirtProvider_None;
1298 break;
1299 }
1300
1301 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1302 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1303
1304 switch (mHWData->mParavirtProvider)
1305 {
1306 case ParavirtProvider_Legacy:
1307 {
1308 if (fOsXGuest)
1309 *aParavirtProvider = ParavirtProvider_Minimal;
1310 else
1311 *aParavirtProvider = ParavirtProvider_None;
1312 break;
1313 }
1314
1315 case ParavirtProvider_Default:
1316 {
1317 if (fOsXGuest)
1318 *aParavirtProvider = ParavirtProvider_Minimal;
1319 else if ( mUserData->s.strOsType == "Windows10"
1320 || mUserData->s.strOsType == "Windows10_64"
1321 || mUserData->s.strOsType == "Windows81"
1322 || mUserData->s.strOsType == "Windows81_64"
1323 || mUserData->s.strOsType == "Windows8"
1324 || mUserData->s.strOsType == "Windows8_64"
1325 || mUserData->s.strOsType == "Windows7"
1326 || mUserData->s.strOsType == "Windows7_64"
1327 || mUserData->s.strOsType == "WindowsVista"
1328 || mUserData->s.strOsType == "WindowsVista_64"
1329 || mUserData->s.strOsType == "Windows2012"
1330 || mUserData->s.strOsType == "Windows2012_64"
1331 || mUserData->s.strOsType == "Windows2008"
1332 || mUserData->s.strOsType == "Windows2008_64")
1333 {
1334 *aParavirtProvider = ParavirtProvider_HyperV;
1335 }
1336 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1337 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1338 || mUserData->s.strOsType == "Linux"
1339 || mUserData->s.strOsType == "Linux_64"
1340 || mUserData->s.strOsType == "ArchLinux"
1341 || mUserData->s.strOsType == "ArchLinux_64"
1342 || mUserData->s.strOsType == "Debian"
1343 || mUserData->s.strOsType == "Debian_64"
1344 || mUserData->s.strOsType == "Fedora"
1345 || mUserData->s.strOsType == "Fedora_64"
1346 || mUserData->s.strOsType == "Gentoo"
1347 || mUserData->s.strOsType == "Gentoo_64"
1348 || mUserData->s.strOsType == "Mandriva"
1349 || mUserData->s.strOsType == "Mandriva_64"
1350 || mUserData->s.strOsType == "OpenSUSE"
1351 || mUserData->s.strOsType == "OpenSUSE_64"
1352 || mUserData->s.strOsType == "Oracle"
1353 || mUserData->s.strOsType == "Oracle_64"
1354 || mUserData->s.strOsType == "RedHat"
1355 || mUserData->s.strOsType == "RedHat_64"
1356 || mUserData->s.strOsType == "Turbolinux"
1357 || mUserData->s.strOsType == "Turbolinux_64"
1358 || mUserData->s.strOsType == "Ubuntu"
1359 || mUserData->s.strOsType == "Ubuntu_64"
1360 || mUserData->s.strOsType == "Xandros"
1361 || mUserData->s.strOsType == "Xandros_64")
1362 {
1363 *aParavirtProvider = ParavirtProvider_KVM;
1364 }
1365 else
1366 *aParavirtProvider = ParavirtProvider_None;
1367 break;
1368 }
1369
1370 default: AssertFailedBreak(); /* Shut up MSC. */
1371 }
1372 break;
1373 }
1374 }
1375
1376 Assert( *aParavirtProvider == ParavirtProvider_None
1377 || *aParavirtProvider == ParavirtProvider_Minimal
1378 || *aParavirtProvider == ParavirtProvider_HyperV
1379 || *aParavirtProvider == ParavirtProvider_KVM);
1380 return S_OK;
1381}
1382
1383HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1384{
1385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1386
1387 aHardwareVersion = mHWData->mHWVersion;
1388
1389 return S_OK;
1390}
1391
1392HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1393{
1394 /* check known version */
1395 Utf8Str hwVersion = aHardwareVersion;
1396 if ( hwVersion.compare("1") != 0
1397 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1398 return setError(E_INVALIDARG,
1399 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1400
1401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1402
1403 HRESULT rc = i_checkStateDependency(MutableStateDep);
1404 if (FAILED(rc)) return rc;
1405
1406 i_setModified(IsModified_MachineData);
1407 mHWData.backup();
1408 mHWData->mHWVersion = aHardwareVersion;
1409
1410 return S_OK;
1411}
1412
1413HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1414{
1415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1416
1417 if (!mHWData->mHardwareUUID.isZero())
1418 aHardwareUUID = mHWData->mHardwareUUID;
1419 else
1420 aHardwareUUID = mData->mUuid;
1421
1422 return S_OK;
1423}
1424
1425HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1426{
1427 if (!aHardwareUUID.isValid())
1428 return E_INVALIDARG;
1429
1430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 HRESULT rc = i_checkStateDependency(MutableStateDep);
1433 if (FAILED(rc)) return rc;
1434
1435 i_setModified(IsModified_MachineData);
1436 mHWData.backup();
1437 if (aHardwareUUID == mData->mUuid)
1438 mHWData->mHardwareUUID.clear();
1439 else
1440 mHWData->mHardwareUUID = aHardwareUUID;
1441
1442 return S_OK;
1443}
1444
1445HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1446{
1447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 *aMemorySize = mHWData->mMemorySize;
1450
1451 return S_OK;
1452}
1453
1454HRESULT Machine::setMemorySize(ULONG aMemorySize)
1455{
1456 /* check RAM limits */
1457 if ( aMemorySize < MM_RAM_MIN_IN_MB
1458 || aMemorySize > MM_RAM_MAX_IN_MB
1459 )
1460 return setError(E_INVALIDARG,
1461 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1462 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1463
1464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1465
1466 HRESULT rc = i_checkStateDependency(MutableStateDep);
1467 if (FAILED(rc)) return rc;
1468
1469 i_setModified(IsModified_MachineData);
1470 mHWData.backup();
1471 mHWData->mMemorySize = aMemorySize;
1472
1473 return S_OK;
1474}
1475
1476HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1477{
1478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 *aCPUCount = mHWData->mCPUCount;
1481
1482 return S_OK;
1483}
1484
1485HRESULT Machine::setCPUCount(ULONG aCPUCount)
1486{
1487 /* check CPU limits */
1488 if ( aCPUCount < SchemaDefs::MinCPUCount
1489 || aCPUCount > SchemaDefs::MaxCPUCount
1490 )
1491 return setError(E_INVALIDARG,
1492 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1493 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1494
1495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1496
1497 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1498 if (mHWData->mCPUHotPlugEnabled)
1499 {
1500 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1501 {
1502 if (mHWData->mCPUAttached[idx])
1503 return setError(E_INVALIDARG,
1504 tr("There is still a CPU attached to socket %lu."
1505 "Detach the CPU before removing the socket"),
1506 aCPUCount, idx+1);
1507 }
1508 }
1509
1510 HRESULT rc = i_checkStateDependency(MutableStateDep);
1511 if (FAILED(rc)) return rc;
1512
1513 i_setModified(IsModified_MachineData);
1514 mHWData.backup();
1515 mHWData->mCPUCount = aCPUCount;
1516
1517 return S_OK;
1518}
1519
1520HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1521{
1522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1523
1524 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1525
1526 return S_OK;
1527}
1528
1529HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1530{
1531 HRESULT rc = S_OK;
1532
1533 /* check throttle limits */
1534 if ( aCPUExecutionCap < 1
1535 || aCPUExecutionCap > 100
1536 )
1537 return setError(E_INVALIDARG,
1538 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1539 aCPUExecutionCap, 1, 100);
1540
1541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 alock.release();
1544 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1545 alock.acquire();
1546 if (FAILED(rc)) return rc;
1547
1548 i_setModified(IsModified_MachineData);
1549 mHWData.backup();
1550 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1551
1552 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1553 if (Global::IsOnline(mData->mMachineState))
1554 i_saveSettings(NULL);
1555
1556 return S_OK;
1557}
1558
1559HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1560{
1561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1562
1563 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1564
1565 return S_OK;
1566}
1567
1568HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1569{
1570 HRESULT rc = S_OK;
1571
1572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1573
1574 rc = i_checkStateDependency(MutableStateDep);
1575 if (FAILED(rc)) return rc;
1576
1577 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1578 {
1579 if (aCPUHotPlugEnabled)
1580 {
1581 i_setModified(IsModified_MachineData);
1582 mHWData.backup();
1583
1584 /* Add the amount of CPUs currently attached */
1585 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1586 mHWData->mCPUAttached[i] = true;
1587 }
1588 else
1589 {
1590 /*
1591 * We can disable hotplug only if the amount of maximum CPUs is equal
1592 * to the amount of attached CPUs
1593 */
1594 unsigned cCpusAttached = 0;
1595 unsigned iHighestId = 0;
1596
1597 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1598 {
1599 if (mHWData->mCPUAttached[i])
1600 {
1601 cCpusAttached++;
1602 iHighestId = i;
1603 }
1604 }
1605
1606 if ( (cCpusAttached != mHWData->mCPUCount)
1607 || (iHighestId >= mHWData->mCPUCount))
1608 return setError(E_INVALIDARG,
1609 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1610
1611 i_setModified(IsModified_MachineData);
1612 mHWData.backup();
1613 }
1614 }
1615
1616 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1617
1618 return rc;
1619}
1620
1621HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1622{
1623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1626
1627 return S_OK;
1628}
1629
1630HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1631{
1632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1633
1634 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1635 if (SUCCEEDED(hrc))
1636 {
1637 i_setModified(IsModified_MachineData);
1638 mHWData.backup();
1639 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1640 }
1641 return hrc;
1642}
1643
1644HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1645{
1646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1647 aCPUProfile = mHWData->mCpuProfile;
1648 return S_OK;
1649}
1650
1651HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1652{
1653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1654 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1655 if (SUCCEEDED(hrc))
1656 {
1657 i_setModified(IsModified_MachineData);
1658 mHWData.backup();
1659 /* Empty equals 'host'. */
1660 if (aCPUProfile.isNotEmpty())
1661 mHWData->mCpuProfile = aCPUProfile;
1662 else
1663 mHWData->mCpuProfile = "host";
1664 }
1665 return hrc;
1666}
1667
1668HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1669{
1670#ifdef VBOX_WITH_USB_CARDREADER
1671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1672
1673 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1674
1675 return S_OK;
1676#else
1677 NOREF(aEmulatedUSBCardReaderEnabled);
1678 return E_NOTIMPL;
1679#endif
1680}
1681
1682HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1683{
1684#ifdef VBOX_WITH_USB_CARDREADER
1685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1688 if (FAILED(rc)) return rc;
1689
1690 i_setModified(IsModified_MachineData);
1691 mHWData.backup();
1692 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1693
1694 return S_OK;
1695#else
1696 NOREF(aEmulatedUSBCardReaderEnabled);
1697 return E_NOTIMPL;
1698#endif
1699}
1700
1701HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1702{
1703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1704
1705 *aHPETEnabled = mHWData->mHPETEnabled;
1706
1707 return S_OK;
1708}
1709
1710HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1711{
1712 HRESULT rc = S_OK;
1713
1714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1715
1716 rc = i_checkStateDependency(MutableStateDep);
1717 if (FAILED(rc)) return rc;
1718
1719 i_setModified(IsModified_MachineData);
1720 mHWData.backup();
1721
1722 mHWData->mHPETEnabled = aHPETEnabled;
1723
1724 return rc;
1725}
1726
1727HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1728{
1729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1730
1731 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1732
1733 return S_OK;
1734}
1735
1736HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1737{
1738 switch (aGraphicsControllerType)
1739 {
1740 case GraphicsControllerType_Null:
1741 case GraphicsControllerType_VBoxVGA:
1742#ifdef VBOX_WITH_VMSVGA
1743 case GraphicsControllerType_VMSVGA:
1744 case GraphicsControllerType_VBoxSVGA:
1745#endif
1746 break;
1747 default:
1748 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1749 }
1750
1751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1752
1753 HRESULT rc = i_checkStateDependency(MutableStateDep);
1754 if (FAILED(rc)) return rc;
1755
1756 i_setModified(IsModified_MachineData);
1757 mHWData.backup();
1758 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1759
1760 return S_OK;
1761}
1762
1763HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766
1767 *aVRAMSize = mHWData->mVRAMSize;
1768
1769 return S_OK;
1770}
1771
1772HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1773{
1774 /* check VRAM limits */
1775 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1776 return setError(E_INVALIDARG,
1777 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1778 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1779
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 HRESULT rc = i_checkStateDependency(MutableStateDep);
1783 if (FAILED(rc)) return rc;
1784
1785 i_setModified(IsModified_MachineData);
1786 mHWData.backup();
1787 mHWData->mVRAMSize = aVRAMSize;
1788
1789 return S_OK;
1790}
1791
1792/** @todo this method should not be public */
1793HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1794{
1795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1796
1797 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1798
1799 return S_OK;
1800}
1801
1802/**
1803 * Set the memory balloon size.
1804 *
1805 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1806 * we have to make sure that we never call IGuest from here.
1807 */
1808HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1809{
1810 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1811#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1812 /* check limits */
1813 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1814 return setError(E_INVALIDARG,
1815 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1816 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1817
1818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 i_setModified(IsModified_MachineData);
1821 mHWData.backup();
1822 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1823
1824 return S_OK;
1825#else
1826 NOREF(aMemoryBalloonSize);
1827 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1828#endif
1829}
1830
1831HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1832{
1833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1834
1835 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1836 return S_OK;
1837}
1838
1839HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1840{
1841#ifdef VBOX_WITH_PAGE_SHARING
1842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1843
1844 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1845 i_setModified(IsModified_MachineData);
1846 mHWData.backup();
1847 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1848 return S_OK;
1849#else
1850 NOREF(aPageFusionEnabled);
1851 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1852#endif
1853}
1854
1855HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1856{
1857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1858
1859 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1860
1861 return S_OK;
1862}
1863
1864HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1865{
1866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1867
1868 HRESULT rc = i_checkStateDependency(MutableStateDep);
1869 if (FAILED(rc)) return rc;
1870
1871 /** @todo check validity! */
1872
1873 i_setModified(IsModified_MachineData);
1874 mHWData.backup();
1875 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1876
1877 return S_OK;
1878}
1879
1880
1881HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1882{
1883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1884
1885 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1886
1887 return S_OK;
1888}
1889
1890HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1891{
1892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1893
1894 HRESULT rc = i_checkStateDependency(MutableStateDep);
1895 if (FAILED(rc)) return rc;
1896
1897 /** @todo check validity! */
1898 i_setModified(IsModified_MachineData);
1899 mHWData.backup();
1900 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1901
1902 return S_OK;
1903}
1904
1905HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1906{
1907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1908
1909 *aMonitorCount = mHWData->mMonitorCount;
1910
1911 return S_OK;
1912}
1913
1914HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1915{
1916 /* make sure monitor count is a sensible number */
1917 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1918 return setError(E_INVALIDARG,
1919 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1920 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1921
1922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 HRESULT rc = i_checkStateDependency(MutableStateDep);
1925 if (FAILED(rc)) return rc;
1926
1927 i_setModified(IsModified_MachineData);
1928 mHWData.backup();
1929 mHWData->mMonitorCount = aMonitorCount;
1930
1931 return S_OK;
1932}
1933
1934HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1935{
1936 /* mBIOSSettings is constant during life time, no need to lock */
1937 aBIOSSettings = mBIOSSettings;
1938
1939 return S_OK;
1940}
1941
1942HRESULT Machine::getCaptureSettings(ComPtr<ICaptureSettings> &aCaptureSettings)
1943{
1944 /* mCaptureSettings is constant during life time, no need to lock */
1945 aCaptureSettings = mCaptureSettings;
1946
1947 return S_OK;
1948}
1949
1950HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953
1954 switch (aProperty)
1955 {
1956 case CPUPropertyType_PAE:
1957 *aValue = mHWData->mPAEEnabled;
1958 break;
1959
1960 case CPUPropertyType_LongMode:
1961 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1962 *aValue = TRUE;
1963 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1964 *aValue = FALSE;
1965#if HC_ARCH_BITS == 64
1966 else
1967 *aValue = TRUE;
1968#else
1969 else
1970 {
1971 *aValue = FALSE;
1972
1973 ComObjPtr<GuestOSType> pGuestOSType;
1974 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1975 pGuestOSType);
1976 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1977 {
1978 if (pGuestOSType->i_is64Bit())
1979 {
1980 ComObjPtr<Host> pHost = mParent->i_host();
1981 alock.release();
1982
1983 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1984 if (FAILED(hrc2))
1985 *aValue = FALSE;
1986 }
1987 }
1988 }
1989#endif
1990 break;
1991
1992 case CPUPropertyType_TripleFaultReset:
1993 *aValue = mHWData->mTripleFaultReset;
1994 break;
1995
1996 case CPUPropertyType_APIC:
1997 *aValue = mHWData->mAPIC;
1998 break;
1999
2000 case CPUPropertyType_X2APIC:
2001 *aValue = mHWData->mX2APIC;
2002 break;
2003
2004 case CPUPropertyType_IBPBOnVMExit:
2005 *aValue = mHWData->mIBPBOnVMExit;
2006 break;
2007
2008 case CPUPropertyType_IBPBOnVMEntry:
2009 *aValue = mHWData->mIBPBOnVMEntry;
2010 break;
2011
2012 case CPUPropertyType_SpecCtrl:
2013 *aValue = mHWData->mSpecCtrl;
2014 break;
2015
2016 case CPUPropertyType_SpecCtrlByHost:
2017 *aValue = mHWData->mSpecCtrlByHost;
2018 break;
2019
2020 case CPUPropertyType_HWVirt:
2021 *aValue = mHWData->mNestedHWVirt;
2022 break;
2023
2024 default:
2025 return E_INVALIDARG;
2026 }
2027 return S_OK;
2028}
2029
2030HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2031{
2032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2033
2034 HRESULT rc = i_checkStateDependency(MutableStateDep);
2035 if (FAILED(rc)) return rc;
2036
2037 switch (aProperty)
2038 {
2039 case CPUPropertyType_PAE:
2040 i_setModified(IsModified_MachineData);
2041 mHWData.backup();
2042 mHWData->mPAEEnabled = !!aValue;
2043 break;
2044
2045 case CPUPropertyType_LongMode:
2046 i_setModified(IsModified_MachineData);
2047 mHWData.backup();
2048 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2049 break;
2050
2051 case CPUPropertyType_TripleFaultReset:
2052 i_setModified(IsModified_MachineData);
2053 mHWData.backup();
2054 mHWData->mTripleFaultReset = !!aValue;
2055 break;
2056
2057 case CPUPropertyType_APIC:
2058 if (mHWData->mX2APIC)
2059 aValue = TRUE;
2060 i_setModified(IsModified_MachineData);
2061 mHWData.backup();
2062 mHWData->mAPIC = !!aValue;
2063 break;
2064
2065 case CPUPropertyType_X2APIC:
2066 i_setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mX2APIC = !!aValue;
2069 if (aValue)
2070 mHWData->mAPIC = !!aValue;
2071 break;
2072
2073 case CPUPropertyType_IBPBOnVMExit:
2074 i_setModified(IsModified_MachineData);
2075 mHWData.backup();
2076 mHWData->mIBPBOnVMExit = !!aValue;
2077 break;
2078
2079 case CPUPropertyType_IBPBOnVMEntry:
2080 i_setModified(IsModified_MachineData);
2081 mHWData.backup();
2082 mHWData->mIBPBOnVMEntry = !!aValue;
2083 break;
2084
2085 case CPUPropertyType_SpecCtrl:
2086 i_setModified(IsModified_MachineData);
2087 mHWData.backup();
2088 mHWData->mSpecCtrl = !!aValue;
2089 break;
2090
2091 case CPUPropertyType_SpecCtrlByHost:
2092 i_setModified(IsModified_MachineData);
2093 mHWData.backup();
2094 mHWData->mSpecCtrlByHost = !!aValue;
2095 break;
2096
2097 case CPUPropertyType_HWVirt:
2098 i_setModified(IsModified_MachineData);
2099 mHWData.backup();
2100 mHWData->mNestedHWVirt = !!aValue;
2101 break;
2102
2103 default:
2104 return E_INVALIDARG;
2105 }
2106 return S_OK;
2107}
2108
2109HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2110 ULONG *aValEcx, ULONG *aValEdx)
2111{
2112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2113 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2114 {
2115 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2116 it != mHWData->mCpuIdLeafList.end();
2117 ++it)
2118 {
2119 if (aOrdinal == 0)
2120 {
2121 const settings::CpuIdLeaf &rLeaf= *it;
2122 *aIdx = rLeaf.idx;
2123 *aSubIdx = rLeaf.idxSub;
2124 *aValEax = rLeaf.uEax;
2125 *aValEbx = rLeaf.uEbx;
2126 *aValEcx = rLeaf.uEcx;
2127 *aValEdx = rLeaf.uEdx;
2128 return S_OK;
2129 }
2130 aOrdinal--;
2131 }
2132 }
2133 return E_INVALIDARG;
2134}
2135
2136HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2137{
2138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2139
2140 /*
2141 * Search the list.
2142 */
2143 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2144 {
2145 const settings::CpuIdLeaf &rLeaf= *it;
2146 if ( rLeaf.idx == aIdx
2147 && ( aSubIdx == UINT32_MAX
2148 || rLeaf.idxSub == aSubIdx) )
2149 {
2150 *aValEax = rLeaf.uEax;
2151 *aValEbx = rLeaf.uEbx;
2152 *aValEcx = rLeaf.uEcx;
2153 *aValEdx = rLeaf.uEdx;
2154 return S_OK;
2155 }
2156 }
2157
2158 return E_INVALIDARG;
2159}
2160
2161
2162HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2163{
2164 /*
2165 * Validate input before taking locks and checking state.
2166 */
2167 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2168 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2169 if ( aIdx >= UINT32_C(0x20)
2170 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2171 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2172 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2173
2174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2175 HRESULT rc = i_checkStateDependency(MutableStateDep);
2176 if (FAILED(rc)) return rc;
2177
2178 /*
2179 * Impose a maximum number of leaves.
2180 */
2181 if (mHWData->mCpuIdLeafList.size() > 256)
2182 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2183
2184 /*
2185 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2186 */
2187 i_setModified(IsModified_MachineData);
2188 mHWData.backup();
2189
2190 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2191 {
2192 settings::CpuIdLeaf &rLeaf= *it;
2193 if ( rLeaf.idx == aIdx
2194 && ( aSubIdx == UINT32_MAX
2195 || rLeaf.idxSub == aSubIdx) )
2196 it = mHWData->mCpuIdLeafList.erase(it);
2197 else
2198 ++it;
2199 }
2200
2201 settings::CpuIdLeaf NewLeaf;
2202 NewLeaf.idx = aIdx;
2203 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2204 NewLeaf.uEax = aValEax;
2205 NewLeaf.uEbx = aValEbx;
2206 NewLeaf.uEcx = aValEcx;
2207 NewLeaf.uEdx = aValEdx;
2208 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2209 return S_OK;
2210}
2211
2212HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2213{
2214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2215
2216 HRESULT rc = i_checkStateDependency(MutableStateDep);
2217 if (FAILED(rc)) return rc;
2218
2219 /*
2220 * Do the removal.
2221 */
2222 bool fModified = false;
2223 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2224 {
2225 settings::CpuIdLeaf &rLeaf= *it;
2226 if ( rLeaf.idx == aIdx
2227 && ( aSubIdx == UINT32_MAX
2228 || rLeaf.idxSub == aSubIdx) )
2229 {
2230 if (!fModified)
2231 {
2232 fModified = true;
2233 i_setModified(IsModified_MachineData);
2234 mHWData.backup();
2235 }
2236 it = mHWData->mCpuIdLeafList.erase(it);
2237 }
2238 else
2239 ++it;
2240 }
2241
2242 return S_OK;
2243}
2244
2245HRESULT Machine::removeAllCPUIDLeaves()
2246{
2247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2248
2249 HRESULT rc = i_checkStateDependency(MutableStateDep);
2250 if (FAILED(rc)) return rc;
2251
2252 if (mHWData->mCpuIdLeafList.size() > 0)
2253 {
2254 i_setModified(IsModified_MachineData);
2255 mHWData.backup();
2256
2257 mHWData->mCpuIdLeafList.clear();
2258 }
2259
2260 return S_OK;
2261}
2262HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2263{
2264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2265
2266 switch(aProperty)
2267 {
2268 case HWVirtExPropertyType_Enabled:
2269 *aValue = mHWData->mHWVirtExEnabled;
2270 break;
2271
2272 case HWVirtExPropertyType_VPID:
2273 *aValue = mHWData->mHWVirtExVPIDEnabled;
2274 break;
2275
2276 case HWVirtExPropertyType_NestedPaging:
2277 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2278 break;
2279
2280 case HWVirtExPropertyType_UnrestrictedExecution:
2281 *aValue = mHWData->mHWVirtExUXEnabled;
2282 break;
2283
2284 case HWVirtExPropertyType_LargePages:
2285 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2286#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2287 *aValue = FALSE;
2288#endif
2289 break;
2290
2291 case HWVirtExPropertyType_Force:
2292 *aValue = mHWData->mHWVirtExForceEnabled;
2293 break;
2294
2295 case HWVirtExPropertyType_UseNativeApi:
2296 *aValue = mHWData->mHWVirtExUseNativeApi;
2297 break;
2298
2299 default:
2300 return E_INVALIDARG;
2301 }
2302 return S_OK;
2303}
2304
2305HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2306{
2307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 HRESULT rc = i_checkStateDependency(MutableStateDep);
2310 if (FAILED(rc)) return rc;
2311
2312 switch (aProperty)
2313 {
2314 case HWVirtExPropertyType_Enabled:
2315 i_setModified(IsModified_MachineData);
2316 mHWData.backup();
2317 mHWData->mHWVirtExEnabled = !!aValue;
2318 break;
2319
2320 case HWVirtExPropertyType_VPID:
2321 i_setModified(IsModified_MachineData);
2322 mHWData.backup();
2323 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2324 break;
2325
2326 case HWVirtExPropertyType_NestedPaging:
2327 i_setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2330 break;
2331
2332 case HWVirtExPropertyType_UnrestrictedExecution:
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mHWVirtExUXEnabled = !!aValue;
2336 break;
2337
2338 case HWVirtExPropertyType_LargePages:
2339 i_setModified(IsModified_MachineData);
2340 mHWData.backup();
2341 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2342 break;
2343
2344 case HWVirtExPropertyType_Force:
2345 i_setModified(IsModified_MachineData);
2346 mHWData.backup();
2347 mHWData->mHWVirtExForceEnabled = !!aValue;
2348 break;
2349
2350 case HWVirtExPropertyType_UseNativeApi:
2351 i_setModified(IsModified_MachineData);
2352 mHWData.backup();
2353 mHWData->mHWVirtExUseNativeApi = !!aValue;
2354 break;
2355
2356 default:
2357 return E_INVALIDARG;
2358 }
2359
2360 return S_OK;
2361}
2362
2363HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2364{
2365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2366
2367 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2368
2369 return S_OK;
2370}
2371
2372HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2373{
2374 /** @todo (r=dmik):
2375 * 1. Allow to change the name of the snapshot folder containing snapshots
2376 * 2. Rename the folder on disk instead of just changing the property
2377 * value (to be smart and not to leave garbage). Note that it cannot be
2378 * done here because the change may be rolled back. Thus, the right
2379 * place is #saveSettings().
2380 */
2381
2382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 HRESULT rc = i_checkStateDependency(MutableStateDep);
2385 if (FAILED(rc)) return rc;
2386
2387 if (!mData->mCurrentSnapshot.isNull())
2388 return setError(E_FAIL,
2389 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2390
2391 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2392
2393 if (strSnapshotFolder.isEmpty())
2394 strSnapshotFolder = "Snapshots";
2395 int vrc = i_calculateFullPath(strSnapshotFolder,
2396 strSnapshotFolder);
2397 if (RT_FAILURE(vrc))
2398 return setErrorBoth(E_FAIL, vrc,
2399 tr("Invalid snapshot folder '%s' (%Rrc)"),
2400 strSnapshotFolder.c_str(), vrc);
2401
2402 i_setModified(IsModified_MachineData);
2403 mUserData.backup();
2404
2405 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2406
2407 return S_OK;
2408}
2409
2410HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2411{
2412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2413
2414 aMediumAttachments.resize(mMediumAttachments->size());
2415 size_t i = 0;
2416 for (MediumAttachmentList::const_iterator
2417 it = mMediumAttachments->begin();
2418 it != mMediumAttachments->end();
2419 ++it, ++i)
2420 aMediumAttachments[i] = *it;
2421
2422 return S_OK;
2423}
2424
2425HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2426{
2427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2428
2429 Assert(!!mVRDEServer);
2430
2431 aVRDEServer = mVRDEServer;
2432
2433 return S_OK;
2434}
2435
2436HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2437{
2438 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2439
2440 aAudioAdapter = mAudioAdapter;
2441
2442 return S_OK;
2443}
2444
2445HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2446{
2447#ifdef VBOX_WITH_VUSB
2448 clearError();
2449 MultiResult rc(S_OK);
2450
2451# ifdef VBOX_WITH_USB
2452 rc = mParent->i_host()->i_checkUSBProxyService();
2453 if (FAILED(rc)) return rc;
2454# endif
2455
2456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2457
2458 aUSBControllers.resize(mUSBControllers->size());
2459 size_t i = 0;
2460 for (USBControllerList::const_iterator
2461 it = mUSBControllers->begin();
2462 it != mUSBControllers->end();
2463 ++it, ++i)
2464 aUSBControllers[i] = *it;
2465
2466 return S_OK;
2467#else
2468 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2469 * extended error info to indicate that USB is simply not available
2470 * (w/o treating it as a failure), for example, as in OSE */
2471 NOREF(aUSBControllers);
2472 ReturnComNotImplemented();
2473#endif /* VBOX_WITH_VUSB */
2474}
2475
2476HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2477{
2478#ifdef VBOX_WITH_VUSB
2479 clearError();
2480 MultiResult rc(S_OK);
2481
2482# ifdef VBOX_WITH_USB
2483 rc = mParent->i_host()->i_checkUSBProxyService();
2484 if (FAILED(rc)) return rc;
2485# endif
2486
2487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2488
2489 aUSBDeviceFilters = mUSBDeviceFilters;
2490 return rc;
2491#else
2492 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2493 * extended error info to indicate that USB is simply not available
2494 * (w/o treating it as a failure), for example, as in OSE */
2495 NOREF(aUSBDeviceFilters);
2496 ReturnComNotImplemented();
2497#endif /* VBOX_WITH_VUSB */
2498}
2499
2500HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2501{
2502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2503
2504 aSettingsFilePath = mData->m_strConfigFileFull;
2505
2506 return S_OK;
2507}
2508
2509HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2510{
2511 RT_NOREF(aSettingsFilePath);
2512 ReturnComNotImplemented();
2513}
2514
2515HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2516{
2517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2518
2519 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2520 if (FAILED(rc)) return rc;
2521
2522 if (!mData->pMachineConfigFile->fileExists())
2523 // this is a new machine, and no config file exists yet:
2524 *aSettingsModified = TRUE;
2525 else
2526 *aSettingsModified = (mData->flModifications != 0);
2527
2528 return S_OK;
2529}
2530
2531HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2532{
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 *aSessionState = mData->mSession.mState;
2536
2537 return S_OK;
2538}
2539
2540HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2541{
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 aSessionName = mData->mSession.mName;
2545
2546 return S_OK;
2547}
2548
2549HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2550{
2551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2552
2553 *aSessionPID = mData->mSession.mPID;
2554
2555 return S_OK;
2556}
2557
2558HRESULT Machine::getState(MachineState_T *aState)
2559{
2560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2561
2562 *aState = mData->mMachineState;
2563 Assert(mData->mMachineState != MachineState_Null);
2564
2565 return S_OK;
2566}
2567
2568HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2569{
2570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2571
2572 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2573
2574 return S_OK;
2575}
2576
2577HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2578{
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581 aStateFilePath = mSSData->strStateFilePath;
2582
2583 return S_OK;
2584}
2585
2586HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2587{
2588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2589
2590 i_getLogFolder(aLogFolder);
2591
2592 return S_OK;
2593}
2594
2595HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2596{
2597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2598
2599 aCurrentSnapshot = mData->mCurrentSnapshot;
2600
2601 return S_OK;
2602}
2603
2604HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2605{
2606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2607
2608 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2609 ? 0
2610 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2611
2612 return S_OK;
2613}
2614
2615HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2616{
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618
2619 /* Note: for machines with no snapshots, we always return FALSE
2620 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2621 * reasons :) */
2622
2623 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2624 ? FALSE
2625 : mData->mCurrentStateModified;
2626
2627 return S_OK;
2628}
2629
2630HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2631{
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 aSharedFolders.resize(mHWData->mSharedFolders.size());
2635 size_t i = 0;
2636 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2637 it = mHWData->mSharedFolders.begin();
2638 it != mHWData->mSharedFolders.end();
2639 ++it, ++i)
2640 aSharedFolders[i] = *it;
2641
2642 return S_OK;
2643}
2644
2645HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2646{
2647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2648
2649 *aClipboardMode = mHWData->mClipboardMode;
2650
2651 return S_OK;
2652}
2653
2654HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2655{
2656 HRESULT rc = S_OK;
2657
2658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2659
2660 alock.release();
2661 rc = i_onClipboardModeChange(aClipboardMode);
2662 alock.acquire();
2663 if (FAILED(rc)) return rc;
2664
2665 i_setModified(IsModified_MachineData);
2666 mHWData.backup();
2667 mHWData->mClipboardMode = aClipboardMode;
2668
2669 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2670 if (Global::IsOnline(mData->mMachineState))
2671 i_saveSettings(NULL);
2672
2673 return S_OK;
2674}
2675
2676HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2677{
2678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2679
2680 *aDnDMode = mHWData->mDnDMode;
2681
2682 return S_OK;
2683}
2684
2685HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2686{
2687 HRESULT rc = S_OK;
2688
2689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2690
2691 alock.release();
2692 rc = i_onDnDModeChange(aDnDMode);
2693
2694 alock.acquire();
2695 if (FAILED(rc)) return rc;
2696
2697 i_setModified(IsModified_MachineData);
2698 mHWData.backup();
2699 mHWData->mDnDMode = aDnDMode;
2700
2701 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2702 if (Global::IsOnline(mData->mMachineState))
2703 i_saveSettings(NULL);
2704
2705 return S_OK;
2706}
2707
2708HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2709{
2710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2711
2712 aStorageControllers.resize(mStorageControllers->size());
2713 size_t i = 0;
2714 for (StorageControllerList::const_iterator
2715 it = mStorageControllers->begin();
2716 it != mStorageControllers->end();
2717 ++it, ++i)
2718 aStorageControllers[i] = *it;
2719
2720 return S_OK;
2721}
2722
2723HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2724{
2725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2726
2727 *aEnabled = mUserData->s.fTeleporterEnabled;
2728
2729 return S_OK;
2730}
2731
2732HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2733{
2734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 /* Only allow it to be set to true when PoweredOff or Aborted.
2737 (Clearing it is always permitted.) */
2738 if ( aTeleporterEnabled
2739 && mData->mRegistered
2740 && ( !i_isSessionMachine()
2741 || ( mData->mMachineState != MachineState_PoweredOff
2742 && mData->mMachineState != MachineState_Teleported
2743 && mData->mMachineState != MachineState_Aborted
2744 )
2745 )
2746 )
2747 return setError(VBOX_E_INVALID_VM_STATE,
2748 tr("The machine is not powered off (state is %s)"),
2749 Global::stringifyMachineState(mData->mMachineState));
2750
2751 i_setModified(IsModified_MachineData);
2752 mUserData.backup();
2753 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2759{
2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2763
2764 return S_OK;
2765}
2766
2767HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2768{
2769 if (aTeleporterPort >= _64K)
2770 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2771
2772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2775 if (FAILED(rc)) return rc;
2776
2777 i_setModified(IsModified_MachineData);
2778 mUserData.backup();
2779 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2785{
2786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2789
2790 return S_OK;
2791}
2792
2793HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2794{
2795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2796
2797 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2798 if (FAILED(rc)) return rc;
2799
2800 i_setModified(IsModified_MachineData);
2801 mUserData.backup();
2802 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2816{
2817 /*
2818 * Hash the password first.
2819 */
2820 com::Utf8Str aT = aTeleporterPassword;
2821
2822 if (!aT.isEmpty())
2823 {
2824 if (VBoxIsPasswordHashed(&aT))
2825 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2826 VBoxHashPassword(&aT);
2827 }
2828
2829 /*
2830 * Do the update.
2831 */
2832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2833 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2834 if (SUCCEEDED(hrc))
2835 {
2836 i_setModified(IsModified_MachineData);
2837 mUserData.backup();
2838 mUserData->s.strTeleporterPassword = aT;
2839 }
2840
2841 return hrc;
2842}
2843
2844HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2849 return S_OK;
2850}
2851
2852HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2853{
2854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 /** @todo deal with running state change. */
2857 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2858 if (FAILED(rc)) return rc;
2859
2860 i_setModified(IsModified_MachineData);
2861 mUserData.backup();
2862 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2863 return S_OK;
2864}
2865
2866HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2867{
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2871 return S_OK;
2872}
2873
2874HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2875{
2876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2877
2878 /** @todo deal with running state change. */
2879 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2880 if (FAILED(rc)) return rc;
2881
2882 i_setModified(IsModified_MachineData);
2883 mUserData.backup();
2884 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2885 return S_OK;
2886}
2887
2888HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2889{
2890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2891
2892 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2893 return S_OK;
2894}
2895
2896HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2897{
2898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 /** @todo deal with running state change. */
2901 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2902 if (FAILED(rc)) return rc;
2903
2904 i_setModified(IsModified_MachineData);
2905 mUserData.backup();
2906 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2907 return S_OK;
2908}
2909
2910HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2911{
2912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2915
2916 return S_OK;
2917}
2918
2919HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2920{
2921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2922
2923 /** @todo deal with running state change. */
2924 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2925 if (FAILED(rc)) return rc;
2926
2927 i_setModified(IsModified_MachineData);
2928 mUserData.backup();
2929 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2935{
2936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2939 return S_OK;
2940}
2941
2942HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2943{
2944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2945
2946 /** @todo deal with running state change. */
2947 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2948 if (FAILED(rc)) return rc;
2949
2950 i_setModified(IsModified_MachineData);
2951 mUserData.backup();
2952 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2953 return S_OK;
2954}
2955
2956HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2957{
2958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2959
2960 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2961
2962 return S_OK;
2963}
2964
2965HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2966{
2967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2968
2969 /* Only allow it to be set to true when PoweredOff or Aborted.
2970 (Clearing it is always permitted.) */
2971 if ( aRTCUseUTC
2972 && mData->mRegistered
2973 && ( !i_isSessionMachine()
2974 || ( mData->mMachineState != MachineState_PoweredOff
2975 && mData->mMachineState != MachineState_Teleported
2976 && mData->mMachineState != MachineState_Aborted
2977 )
2978 )
2979 )
2980 return setError(VBOX_E_INVALID_VM_STATE,
2981 tr("The machine is not powered off (state is %s)"),
2982 Global::stringifyMachineState(mData->mMachineState));
2983
2984 i_setModified(IsModified_MachineData);
2985 mUserData.backup();
2986 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2987
2988 return S_OK;
2989}
2990
2991HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2992{
2993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2994
2995 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2996
2997 return S_OK;
2998}
2999
3000HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3001{
3002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 HRESULT rc = i_checkStateDependency(MutableStateDep);
3005 if (FAILED(rc)) return rc;
3006
3007 i_setModified(IsModified_MachineData);
3008 mHWData.backup();
3009 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3010
3011 return S_OK;
3012}
3013
3014HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3015{
3016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3017
3018 *aIOCacheSize = mHWData->mIOCacheSize;
3019
3020 return S_OK;
3021}
3022
3023HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3024{
3025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3026
3027 HRESULT rc = i_checkStateDependency(MutableStateDep);
3028 if (FAILED(rc)) return rc;
3029
3030 i_setModified(IsModified_MachineData);
3031 mHWData.backup();
3032 mHWData->mIOCacheSize = aIOCacheSize;
3033
3034 return S_OK;
3035}
3036
3037
3038/**
3039 * @note Locks objects!
3040 */
3041HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3042 LockType_T aLockType)
3043{
3044 /* check the session state */
3045 SessionState_T state;
3046 HRESULT rc = aSession->COMGETTER(State)(&state);
3047 if (FAILED(rc)) return rc;
3048
3049 if (state != SessionState_Unlocked)
3050 return setError(VBOX_E_INVALID_OBJECT_STATE,
3051 tr("The given session is busy"));
3052
3053 // get the client's IInternalSessionControl interface
3054 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3055 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3056 E_INVALIDARG);
3057
3058 // session name (only used in some code paths)
3059 Utf8Str strSessionName;
3060
3061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 if (!mData->mRegistered)
3064 return setError(E_UNEXPECTED,
3065 tr("The machine '%s' is not registered"),
3066 mUserData->s.strName.c_str());
3067
3068 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3069
3070 SessionState_T oldState = mData->mSession.mState;
3071 /* Hack: in case the session is closing and there is a progress object
3072 * which allows waiting for the session to be closed, take the opportunity
3073 * and do a limited wait (max. 1 second). This helps a lot when the system
3074 * is busy and thus session closing can take a little while. */
3075 if ( mData->mSession.mState == SessionState_Unlocking
3076 && mData->mSession.mProgress)
3077 {
3078 alock.release();
3079 mData->mSession.mProgress->WaitForCompletion(1000);
3080 alock.acquire();
3081 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3082 }
3083
3084 // try again now
3085 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3086 // (i.e. session machine exists)
3087 && (aLockType == LockType_Shared) // caller wants a shared link to the
3088 // existing session that holds the write lock:
3089 )
3090 {
3091 // OK, share the session... we are now dealing with three processes:
3092 // 1) VBoxSVC (where this code runs);
3093 // 2) process C: the caller's client process (who wants a shared session);
3094 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3095
3096 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3097 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3098 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3099 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3100 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3101
3102 /*
3103 * Release the lock before calling the client process. It's safe here
3104 * since the only thing to do after we get the lock again is to add
3105 * the remote control to the list (which doesn't directly influence
3106 * anything).
3107 */
3108 alock.release();
3109
3110 // get the console of the session holding the write lock (this is a remote call)
3111 ComPtr<IConsole> pConsoleW;
3112 if (mData->mSession.mLockType == LockType_VM)
3113 {
3114 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3115 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3116 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3117 if (FAILED(rc))
3118 // the failure may occur w/o any error info (from RPC), so provide one
3119 return setError(VBOX_E_VM_ERROR,
3120 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3121 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3122 }
3123
3124 // share the session machine and W's console with the caller's session
3125 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3126 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3127 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3128
3129 if (FAILED(rc))
3130 // the failure may occur w/o any error info (from RPC), so provide one
3131 return setError(VBOX_E_VM_ERROR,
3132 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3133 alock.acquire();
3134
3135 // need to revalidate the state after acquiring the lock again
3136 if (mData->mSession.mState != SessionState_Locked)
3137 {
3138 pSessionControl->Uninitialize();
3139 return setError(VBOX_E_INVALID_SESSION_STATE,
3140 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3141 mUserData->s.strName.c_str());
3142 }
3143
3144 // add the caller's session to the list
3145 mData->mSession.mRemoteControls.push_back(pSessionControl);
3146 }
3147 else if ( mData->mSession.mState == SessionState_Locked
3148 || mData->mSession.mState == SessionState_Unlocking
3149 )
3150 {
3151 // sharing not permitted, or machine still unlocking:
3152 return setError(VBOX_E_INVALID_OBJECT_STATE,
3153 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3154 mUserData->s.strName.c_str());
3155 }
3156 else
3157 {
3158 // machine is not locked: then write-lock the machine (create the session machine)
3159
3160 // must not be busy
3161 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3162
3163 // get the caller's session PID
3164 RTPROCESS pid = NIL_RTPROCESS;
3165 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3166 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3167 Assert(pid != NIL_RTPROCESS);
3168
3169 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3170
3171 if (fLaunchingVMProcess)
3172 {
3173 if (mData->mSession.mPID == NIL_RTPROCESS)
3174 {
3175 // two or more clients racing for a lock, the one which set the
3176 // session state to Spawning will win, the others will get an
3177 // error as we can't decide here if waiting a little would help
3178 // (only for shared locks this would avoid an error)
3179 return setError(VBOX_E_INVALID_OBJECT_STATE,
3180 tr("The machine '%s' already has a lock request pending"),
3181 mUserData->s.strName.c_str());
3182 }
3183
3184 // this machine is awaiting for a spawning session to be opened:
3185 // then the calling process must be the one that got started by
3186 // LaunchVMProcess()
3187
3188 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3189 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3190
3191#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3192 /* Hardened windows builds spawns three processes when a VM is
3193 launched, the 3rd one is the one that will end up here. */
3194 RTPROCESS ppid;
3195 int rc = RTProcQueryParent(pid, &ppid);
3196 if (RT_SUCCESS(rc))
3197 rc = RTProcQueryParent(ppid, &ppid);
3198 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3199 || rc == VERR_ACCESS_DENIED)
3200 {
3201 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3202 mData->mSession.mPID = pid;
3203 }
3204#endif
3205
3206 if (mData->mSession.mPID != pid)
3207 return setError(E_ACCESSDENIED,
3208 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3209 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3210 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3211 }
3212
3213 // create the mutable SessionMachine from the current machine
3214 ComObjPtr<SessionMachine> sessionMachine;
3215 sessionMachine.createObject();
3216 rc = sessionMachine->init(this);
3217 AssertComRC(rc);
3218
3219 /* NOTE: doing return from this function after this point but
3220 * before the end is forbidden since it may call SessionMachine::uninit()
3221 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3222 * lock while still holding the Machine lock in alock so that a deadlock
3223 * is possible due to the wrong lock order. */
3224
3225 if (SUCCEEDED(rc))
3226 {
3227 /*
3228 * Set the session state to Spawning to protect against subsequent
3229 * attempts to open a session and to unregister the machine after
3230 * we release the lock.
3231 */
3232 SessionState_T origState = mData->mSession.mState;
3233 mData->mSession.mState = SessionState_Spawning;
3234
3235#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3236 /* Get the client token ID to be passed to the client process */
3237 Utf8Str strTokenId;
3238 sessionMachine->i_getTokenId(strTokenId);
3239 Assert(!strTokenId.isEmpty());
3240#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3241 /* Get the client token to be passed to the client process */
3242 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3243 /* The token is now "owned" by pToken, fix refcount */
3244 if (!pToken.isNull())
3245 pToken->Release();
3246#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3247
3248 /*
3249 * Release the lock before calling the client process -- it will call
3250 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3251 * because the state is Spawning, so that LaunchVMProcess() and
3252 * LockMachine() calls will fail. This method, called before we
3253 * acquire the lock again, will fail because of the wrong PID.
3254 *
3255 * Note that mData->mSession.mRemoteControls accessed outside
3256 * the lock may not be modified when state is Spawning, so it's safe.
3257 */
3258 alock.release();
3259
3260 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3261#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3262 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3263#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3264 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3265 /* Now the token is owned by the client process. */
3266 pToken.setNull();
3267#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3268 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3269
3270 /* The failure may occur w/o any error info (from RPC), so provide one */
3271 if (FAILED(rc))
3272 setError(VBOX_E_VM_ERROR,
3273 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3274
3275 // get session name, either to remember or to compare against
3276 // the already known session name.
3277 {
3278 Bstr bstrSessionName;
3279 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3280 if (SUCCEEDED(rc2))
3281 strSessionName = bstrSessionName;
3282 }
3283
3284 if ( SUCCEEDED(rc)
3285 && fLaunchingVMProcess
3286 )
3287 {
3288 /* complete the remote session initialization */
3289
3290 /* get the console from the direct session */
3291 ComPtr<IConsole> console;
3292 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3293 ComAssertComRC(rc);
3294
3295 if (SUCCEEDED(rc) && !console)
3296 {
3297 ComAssert(!!console);
3298 rc = E_FAIL;
3299 }
3300
3301 /* assign machine & console to the remote session */
3302 if (SUCCEEDED(rc))
3303 {
3304 /*
3305 * after LaunchVMProcess(), the first and the only
3306 * entry in remoteControls is that remote session
3307 */
3308 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3309 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3310 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3311
3312 /* The failure may occur w/o any error info (from RPC), so provide one */
3313 if (FAILED(rc))
3314 setError(VBOX_E_VM_ERROR,
3315 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3316 }
3317
3318 if (FAILED(rc))
3319 pSessionControl->Uninitialize();
3320 }
3321
3322 /* acquire the lock again */
3323 alock.acquire();
3324
3325 /* Restore the session state */
3326 mData->mSession.mState = origState;
3327 }
3328
3329 // finalize spawning anyway (this is why we don't return on errors above)
3330 if (fLaunchingVMProcess)
3331 {
3332 Assert(mData->mSession.mName == strSessionName);
3333 /* Note that the progress object is finalized later */
3334 /** @todo Consider checking mData->mSession.mProgress for cancellation
3335 * around here. */
3336
3337 /* We don't reset mSession.mPID here because it is necessary for
3338 * SessionMachine::uninit() to reap the child process later. */
3339
3340 if (FAILED(rc))
3341 {
3342 /* Close the remote session, remove the remote control from the list
3343 * and reset session state to Closed (@note keep the code in sync
3344 * with the relevant part in checkForSpawnFailure()). */
3345
3346 Assert(mData->mSession.mRemoteControls.size() == 1);
3347 if (mData->mSession.mRemoteControls.size() == 1)
3348 {
3349 ErrorInfoKeeper eik;
3350 mData->mSession.mRemoteControls.front()->Uninitialize();
3351 }
3352
3353 mData->mSession.mRemoteControls.clear();
3354 mData->mSession.mState = SessionState_Unlocked;
3355 }
3356 }
3357 else
3358 {
3359 /* memorize PID of the directly opened session */
3360 if (SUCCEEDED(rc))
3361 mData->mSession.mPID = pid;
3362 }
3363
3364 if (SUCCEEDED(rc))
3365 {
3366 mData->mSession.mLockType = aLockType;
3367 /* memorize the direct session control and cache IUnknown for it */
3368 mData->mSession.mDirectControl = pSessionControl;
3369 mData->mSession.mState = SessionState_Locked;
3370 if (!fLaunchingVMProcess)
3371 mData->mSession.mName = strSessionName;
3372 /* associate the SessionMachine with this Machine */
3373 mData->mSession.mMachine = sessionMachine;
3374
3375 /* request an IUnknown pointer early from the remote party for later
3376 * identity checks (it will be internally cached within mDirectControl
3377 * at least on XPCOM) */
3378 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3379 NOREF(unk);
3380 }
3381
3382 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3383 * would break the lock order */
3384 alock.release();
3385
3386 /* uninitialize the created session machine on failure */
3387 if (FAILED(rc))
3388 sessionMachine->uninit();
3389 }
3390
3391 if (SUCCEEDED(rc))
3392 {
3393 /*
3394 * tell the client watcher thread to update the set of
3395 * machines that have open sessions
3396 */
3397 mParent->i_updateClientWatcher();
3398
3399 if (oldState != SessionState_Locked)
3400 /* fire an event */
3401 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3402 }
3403
3404 return rc;
3405}
3406
3407/**
3408 * @note Locks objects!
3409 */
3410HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3411 const com::Utf8Str &aName,
3412 const com::Utf8Str &aEnvironment,
3413 ComPtr<IProgress> &aProgress)
3414{
3415 Utf8Str strFrontend(aName);
3416 /* "emergencystop" doesn't need the session, so skip the checks/interface
3417 * retrieval. This code doesn't quite fit in here, but introducing a
3418 * special API method would be even more effort, and would require explicit
3419 * support by every API client. It's better to hide the feature a bit. */
3420 if (strFrontend != "emergencystop")
3421 CheckComArgNotNull(aSession);
3422
3423 HRESULT rc = S_OK;
3424 if (strFrontend.isEmpty())
3425 {
3426 Bstr bstrFrontend;
3427 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3428 if (FAILED(rc))
3429 return rc;
3430 strFrontend = bstrFrontend;
3431 if (strFrontend.isEmpty())
3432 {
3433 ComPtr<ISystemProperties> systemProperties;
3434 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3435 if (FAILED(rc))
3436 return rc;
3437 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3438 if (FAILED(rc))
3439 return rc;
3440 strFrontend = bstrFrontend;
3441 }
3442 /* paranoia - emergencystop is not a valid default */
3443 if (strFrontend == "emergencystop")
3444 strFrontend = Utf8Str::Empty;
3445 }
3446 /* default frontend: Qt GUI */
3447 if (strFrontend.isEmpty())
3448 strFrontend = "GUI/Qt";
3449
3450 if (strFrontend != "emergencystop")
3451 {
3452 /* check the session state */
3453 SessionState_T state;
3454 rc = aSession->COMGETTER(State)(&state);
3455 if (FAILED(rc))
3456 return rc;
3457
3458 if (state != SessionState_Unlocked)
3459 return setError(VBOX_E_INVALID_OBJECT_STATE,
3460 tr("The given session is busy"));
3461
3462 /* get the IInternalSessionControl interface */
3463 ComPtr<IInternalSessionControl> control(aSession);
3464 ComAssertMsgRet(!control.isNull(),
3465 ("No IInternalSessionControl interface"),
3466 E_INVALIDARG);
3467
3468 /* get the teleporter enable state for the progress object init. */
3469 BOOL fTeleporterEnabled;
3470 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3471 if (FAILED(rc))
3472 return rc;
3473
3474 /* create a progress object */
3475 ComObjPtr<ProgressProxy> progress;
3476 progress.createObject();
3477 rc = progress->init(mParent,
3478 static_cast<IMachine*>(this),
3479 Bstr(tr("Starting VM")).raw(),
3480 TRUE /* aCancelable */,
3481 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3482 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3483 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3484 2 /* uFirstOperationWeight */,
3485 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3486
3487 if (SUCCEEDED(rc))
3488 {
3489 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3490 if (SUCCEEDED(rc))
3491 {
3492 aProgress = progress;
3493
3494 /* signal the client watcher thread */
3495 mParent->i_updateClientWatcher();
3496
3497 /* fire an event */
3498 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3499 }
3500 }
3501 }
3502 else
3503 {
3504 /* no progress object - either instant success or failure */
3505 aProgress = NULL;
3506
3507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3508
3509 if (mData->mSession.mState != SessionState_Locked)
3510 return setError(VBOX_E_INVALID_OBJECT_STATE,
3511 tr("The machine '%s' is not locked by a session"),
3512 mUserData->s.strName.c_str());
3513
3514 /* must have a VM process associated - do not kill normal API clients
3515 * with an open session */
3516 if (!Global::IsOnline(mData->mMachineState))
3517 return setError(VBOX_E_INVALID_OBJECT_STATE,
3518 tr("The machine '%s' does not have a VM process"),
3519 mUserData->s.strName.c_str());
3520
3521 /* forcibly terminate the VM process */
3522 if (mData->mSession.mPID != NIL_RTPROCESS)
3523 RTProcTerminate(mData->mSession.mPID);
3524
3525 /* signal the client watcher thread, as most likely the client has
3526 * been terminated */
3527 mParent->i_updateClientWatcher();
3528 }
3529
3530 return rc;
3531}
3532
3533HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3534{
3535 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3536 return setError(E_INVALIDARG,
3537 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3538 aPosition, SchemaDefs::MaxBootPosition);
3539
3540 if (aDevice == DeviceType_USB)
3541 return setError(E_NOTIMPL,
3542 tr("Booting from USB device is currently not supported"));
3543
3544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3545
3546 HRESULT rc = i_checkStateDependency(MutableStateDep);
3547 if (FAILED(rc)) return rc;
3548
3549 i_setModified(IsModified_MachineData);
3550 mHWData.backup();
3551 mHWData->mBootOrder[aPosition - 1] = aDevice;
3552
3553 return S_OK;
3554}
3555
3556HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3557{
3558 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3559 return setError(E_INVALIDARG,
3560 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3561 aPosition, SchemaDefs::MaxBootPosition);
3562
3563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3564
3565 *aDevice = mHWData->mBootOrder[aPosition - 1];
3566
3567 return S_OK;
3568}
3569
3570HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3571 LONG aControllerPort,
3572 LONG aDevice,
3573 DeviceType_T aType,
3574 const ComPtr<IMedium> &aMedium)
3575{
3576 IMedium *aM = aMedium;
3577 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3578 aName.c_str(), aControllerPort, aDevice, aType, aM));
3579
3580 // request the host lock first, since might be calling Host methods for getting host drives;
3581 // next, protect the media tree all the while we're in here, as well as our member variables
3582 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3583 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3584
3585 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3586 if (FAILED(rc)) return rc;
3587
3588 /// @todo NEWMEDIA implicit machine registration
3589 if (!mData->mRegistered)
3590 return setError(VBOX_E_INVALID_OBJECT_STATE,
3591 tr("Cannot attach storage devices to an unregistered machine"));
3592
3593 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3594
3595 /* Check for an existing controller. */
3596 ComObjPtr<StorageController> ctl;
3597 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3598 if (FAILED(rc)) return rc;
3599
3600 StorageControllerType_T ctrlType;
3601 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3602 if (FAILED(rc))
3603 return setError(E_FAIL,
3604 tr("Could not get type of controller '%s'"),
3605 aName.c_str());
3606
3607 bool fSilent = false;
3608 Utf8Str strReconfig;
3609
3610 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3611 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3612 if ( mData->mMachineState == MachineState_Paused
3613 && strReconfig == "1")
3614 fSilent = true;
3615
3616 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3617 bool fHotplug = false;
3618 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3619 fHotplug = true;
3620
3621 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3622 return setError(VBOX_E_INVALID_VM_STATE,
3623 tr("Controller '%s' does not support hotplugging"),
3624 aName.c_str());
3625
3626 // check that the port and device are not out of range
3627 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3628 if (FAILED(rc)) return rc;
3629
3630 /* check if the device slot is already busy */
3631 MediumAttachment *pAttachTemp;
3632 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3633 aName,
3634 aControllerPort,
3635 aDevice)))
3636 {
3637 Medium *pMedium = pAttachTemp->i_getMedium();
3638 if (pMedium)
3639 {
3640 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3641 return setError(VBOX_E_OBJECT_IN_USE,
3642 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3643 pMedium->i_getLocationFull().c_str(),
3644 aControllerPort,
3645 aDevice,
3646 aName.c_str());
3647 }
3648 else
3649 return setError(VBOX_E_OBJECT_IN_USE,
3650 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3651 aControllerPort, aDevice, aName.c_str());
3652 }
3653
3654 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3655 if (aMedium && medium.isNull())
3656 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3657
3658 AutoCaller mediumCaller(medium);
3659 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3660
3661 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3662
3663 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3664 && !medium.isNull()
3665 )
3666 return setError(VBOX_E_OBJECT_IN_USE,
3667 tr("Medium '%s' is already attached to this virtual machine"),
3668 medium->i_getLocationFull().c_str());
3669
3670 if (!medium.isNull())
3671 {
3672 MediumType_T mtype = medium->i_getType();
3673 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3674 // For DVDs it's not written to the config file, so needs no global config
3675 // version bump. For floppies it's a new attribute "type", which is ignored
3676 // by older VirtualBox version, so needs no global config version bump either.
3677 // For hard disks this type is not accepted.
3678 if (mtype == MediumType_MultiAttach)
3679 {
3680 // This type is new with VirtualBox 4.0 and therefore requires settings
3681 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3682 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3683 // two reasons: The medium type is a property of the media registry tree, which
3684 // can reside in the global config file (for pre-4.0 media); we would therefore
3685 // possibly need to bump the global config version. We don't want to do that though
3686 // because that might make downgrading to pre-4.0 impossible.
3687 // As a result, we can only use these two new types if the medium is NOT in the
3688 // global registry:
3689 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3690 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3691 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3692 )
3693 return setError(VBOX_E_INVALID_OBJECT_STATE,
3694 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3695 "to machines that were created with VirtualBox 4.0 or later"),
3696 medium->i_getLocationFull().c_str());
3697 }
3698 }
3699
3700 bool fIndirect = false;
3701 if (!medium.isNull())
3702 fIndirect = medium->i_isReadOnly();
3703 bool associate = true;
3704
3705 do
3706 {
3707 if ( aType == DeviceType_HardDisk
3708 && mMediumAttachments.isBackedUp())
3709 {
3710 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3711
3712 /* check if the medium was attached to the VM before we started
3713 * changing attachments in which case the attachment just needs to
3714 * be restored */
3715 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3716 {
3717 AssertReturn(!fIndirect, E_FAIL);
3718
3719 /* see if it's the same bus/channel/device */
3720 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3721 {
3722 /* the simplest case: restore the whole attachment
3723 * and return, nothing else to do */
3724 mMediumAttachments->push_back(pAttachTemp);
3725
3726 /* Reattach the medium to the VM. */
3727 if (fHotplug || fSilent)
3728 {
3729 mediumLock.release();
3730 treeLock.release();
3731 alock.release();
3732
3733 MediumLockList *pMediumLockList(new MediumLockList());
3734
3735 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3736 medium /* pToLockWrite */,
3737 false /* fMediumLockWriteAll */,
3738 NULL,
3739 *pMediumLockList);
3740 alock.acquire();
3741 if (FAILED(rc))
3742 delete pMediumLockList;
3743 else
3744 {
3745 mData->mSession.mLockedMedia.Unlock();
3746 alock.release();
3747 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3748 mData->mSession.mLockedMedia.Lock();
3749 alock.acquire();
3750 }
3751 alock.release();
3752
3753 if (SUCCEEDED(rc))
3754 {
3755 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3756 /* Remove lock list in case of error. */
3757 if (FAILED(rc))
3758 {
3759 mData->mSession.mLockedMedia.Unlock();
3760 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3761 mData->mSession.mLockedMedia.Lock();
3762 }
3763 }
3764 }
3765
3766 return S_OK;
3767 }
3768
3769 /* bus/channel/device differ; we need a new attachment object,
3770 * but don't try to associate it again */
3771 associate = false;
3772 break;
3773 }
3774 }
3775
3776 /* go further only if the attachment is to be indirect */
3777 if (!fIndirect)
3778 break;
3779
3780 /* perform the so called smart attachment logic for indirect
3781 * attachments. Note that smart attachment is only applicable to base
3782 * hard disks. */
3783
3784 if (medium->i_getParent().isNull())
3785 {
3786 /* first, investigate the backup copy of the current hard disk
3787 * attachments to make it possible to re-attach existing diffs to
3788 * another device slot w/o losing their contents */
3789 if (mMediumAttachments.isBackedUp())
3790 {
3791 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3792
3793 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3794 uint32_t foundLevel = 0;
3795
3796 for (MediumAttachmentList::const_iterator
3797 it = oldAtts.begin();
3798 it != oldAtts.end();
3799 ++it)
3800 {
3801 uint32_t level = 0;
3802 MediumAttachment *pAttach = *it;
3803 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3804 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3805 if (pMedium.isNull())
3806 continue;
3807
3808 if (pMedium->i_getBase(&level) == medium)
3809 {
3810 /* skip the hard disk if its currently attached (we
3811 * cannot attach the same hard disk twice) */
3812 if (i_findAttachment(*mMediumAttachments.data(),
3813 pMedium))
3814 continue;
3815
3816 /* matched device, channel and bus (i.e. attached to the
3817 * same place) will win and immediately stop the search;
3818 * otherwise the attachment that has the youngest
3819 * descendant of medium will be used
3820 */
3821 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3822 {
3823 /* the simplest case: restore the whole attachment
3824 * and return, nothing else to do */
3825 mMediumAttachments->push_back(*it);
3826
3827 /* Reattach the medium to the VM. */
3828 if (fHotplug || fSilent)
3829 {
3830 mediumLock.release();
3831 treeLock.release();
3832 alock.release();
3833
3834 MediumLockList *pMediumLockList(new MediumLockList());
3835
3836 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3837 medium /* pToLockWrite */,
3838 false /* fMediumLockWriteAll */,
3839 NULL,
3840 *pMediumLockList);
3841 alock.acquire();
3842 if (FAILED(rc))
3843 delete pMediumLockList;
3844 else
3845 {
3846 mData->mSession.mLockedMedia.Unlock();
3847 alock.release();
3848 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3849 mData->mSession.mLockedMedia.Lock();
3850 alock.acquire();
3851 }
3852 alock.release();
3853
3854 if (SUCCEEDED(rc))
3855 {
3856 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3857 /* Remove lock list in case of error. */
3858 if (FAILED(rc))
3859 {
3860 mData->mSession.mLockedMedia.Unlock();
3861 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3862 mData->mSession.mLockedMedia.Lock();
3863 }
3864 }
3865 }
3866
3867 return S_OK;
3868 }
3869 else if ( foundIt == oldAtts.end()
3870 || level > foundLevel /* prefer younger */
3871 )
3872 {
3873 foundIt = it;
3874 foundLevel = level;
3875 }
3876 }
3877 }
3878
3879 if (foundIt != oldAtts.end())
3880 {
3881 /* use the previously attached hard disk */
3882 medium = (*foundIt)->i_getMedium();
3883 mediumCaller.attach(medium);
3884 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3885 mediumLock.attach(medium);
3886 /* not implicit, doesn't require association with this VM */
3887 fIndirect = false;
3888 associate = false;
3889 /* go right to the MediumAttachment creation */
3890 break;
3891 }
3892 }
3893
3894 /* must give up the medium lock and medium tree lock as below we
3895 * go over snapshots, which needs a lock with higher lock order. */
3896 mediumLock.release();
3897 treeLock.release();
3898
3899 /* then, search through snapshots for the best diff in the given
3900 * hard disk's chain to base the new diff on */
3901
3902 ComObjPtr<Medium> base;
3903 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3904 while (snap)
3905 {
3906 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3907
3908 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3909
3910 MediumAttachment *pAttachFound = NULL;
3911 uint32_t foundLevel = 0;
3912
3913 for (MediumAttachmentList::const_iterator
3914 it = snapAtts.begin();
3915 it != snapAtts.end();
3916 ++it)
3917 {
3918 MediumAttachment *pAttach = *it;
3919 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3920 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3921 if (pMedium.isNull())
3922 continue;
3923
3924 uint32_t level = 0;
3925 if (pMedium->i_getBase(&level) == medium)
3926 {
3927 /* matched device, channel and bus (i.e. attached to the
3928 * same place) will win and immediately stop the search;
3929 * otherwise the attachment that has the youngest
3930 * descendant of medium will be used
3931 */
3932 if ( pAttach->i_getDevice() == aDevice
3933 && pAttach->i_getPort() == aControllerPort
3934 && pAttach->i_getControllerName() == aName
3935 )
3936 {
3937 pAttachFound = pAttach;
3938 break;
3939 }
3940 else if ( !pAttachFound
3941 || level > foundLevel /* prefer younger */
3942 )
3943 {
3944 pAttachFound = pAttach;
3945 foundLevel = level;
3946 }
3947 }
3948 }
3949
3950 if (pAttachFound)
3951 {
3952 base = pAttachFound->i_getMedium();
3953 break;
3954 }
3955
3956 snap = snap->i_getParent();
3957 }
3958
3959 /* re-lock medium tree and the medium, as we need it below */
3960 treeLock.acquire();
3961 mediumLock.acquire();
3962
3963 /* found a suitable diff, use it as a base */
3964 if (!base.isNull())
3965 {
3966 medium = base;
3967 mediumCaller.attach(medium);
3968 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3969 mediumLock.attach(medium);
3970 }
3971 }
3972
3973 Utf8Str strFullSnapshotFolder;
3974 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3975
3976 ComObjPtr<Medium> diff;
3977 diff.createObject();
3978 // store this diff in the same registry as the parent
3979 Guid uuidRegistryParent;
3980 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3981 {
3982 // parent image has no registry: this can happen if we're attaching a new immutable
3983 // image that has not yet been attached (medium then points to the base and we're
3984 // creating the diff image for the immutable, and the parent is not yet registered);
3985 // put the parent in the machine registry then
3986 mediumLock.release();
3987 treeLock.release();
3988 alock.release();
3989 i_addMediumToRegistry(medium);
3990 alock.acquire();
3991 treeLock.acquire();
3992 mediumLock.acquire();
3993 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3994 }
3995 rc = diff->init(mParent,
3996 medium->i_getPreferredDiffFormat(),
3997 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3998 uuidRegistryParent,
3999 DeviceType_HardDisk);
4000 if (FAILED(rc)) return rc;
4001
4002 /* Apply the normal locking logic to the entire chain. */
4003 MediumLockList *pMediumLockList(new MediumLockList());
4004 mediumLock.release();
4005 treeLock.release();
4006 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4007 diff /* pToLockWrite */,
4008 false /* fMediumLockWriteAll */,
4009 medium,
4010 *pMediumLockList);
4011 treeLock.acquire();
4012 mediumLock.acquire();
4013 if (SUCCEEDED(rc))
4014 {
4015 mediumLock.release();
4016 treeLock.release();
4017 rc = pMediumLockList->Lock();
4018 treeLock.acquire();
4019 mediumLock.acquire();
4020 if (FAILED(rc))
4021 setError(rc,
4022 tr("Could not lock medium when creating diff '%s'"),
4023 diff->i_getLocationFull().c_str());
4024 else
4025 {
4026 /* will release the lock before the potentially lengthy
4027 * operation, so protect with the special state */
4028 MachineState_T oldState = mData->mMachineState;
4029 i_setMachineState(MachineState_SettingUp);
4030
4031 mediumLock.release();
4032 treeLock.release();
4033 alock.release();
4034
4035 rc = medium->i_createDiffStorage(diff,
4036 medium->i_getPreferredDiffVariant(),
4037 pMediumLockList,
4038 NULL /* aProgress */,
4039 true /* aWait */);
4040
4041 alock.acquire();
4042 treeLock.acquire();
4043 mediumLock.acquire();
4044
4045 i_setMachineState(oldState);
4046 }
4047 }
4048
4049 /* Unlock the media and free the associated memory. */
4050 delete pMediumLockList;
4051
4052 if (FAILED(rc)) return rc;
4053
4054 /* use the created diff for the actual attachment */
4055 medium = diff;
4056 mediumCaller.attach(medium);
4057 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4058 mediumLock.attach(medium);
4059 }
4060 while (0);
4061
4062 ComObjPtr<MediumAttachment> attachment;
4063 attachment.createObject();
4064 rc = attachment->init(this,
4065 medium,
4066 aName,
4067 aControllerPort,
4068 aDevice,
4069 aType,
4070 fIndirect,
4071 false /* fPassthrough */,
4072 false /* fTempEject */,
4073 false /* fNonRotational */,
4074 false /* fDiscard */,
4075 fHotplug /* fHotPluggable */,
4076 Utf8Str::Empty);
4077 if (FAILED(rc)) return rc;
4078
4079 if (associate && !medium.isNull())
4080 {
4081 // as the last step, associate the medium to the VM
4082 rc = medium->i_addBackReference(mData->mUuid);
4083 // here we can fail because of Deleting, or being in process of creating a Diff
4084 if (FAILED(rc)) return rc;
4085
4086 mediumLock.release();
4087 treeLock.release();
4088 alock.release();
4089 i_addMediumToRegistry(medium);
4090 alock.acquire();
4091 treeLock.acquire();
4092 mediumLock.acquire();
4093 }
4094
4095 /* success: finally remember the attachment */
4096 i_setModified(IsModified_Storage);
4097 mMediumAttachments.backup();
4098 mMediumAttachments->push_back(attachment);
4099
4100 mediumLock.release();
4101 treeLock.release();
4102 alock.release();
4103
4104 if (fHotplug || fSilent)
4105 {
4106 if (!medium.isNull())
4107 {
4108 MediumLockList *pMediumLockList(new MediumLockList());
4109
4110 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4111 medium /* pToLockWrite */,
4112 false /* fMediumLockWriteAll */,
4113 NULL,
4114 *pMediumLockList);
4115 alock.acquire();
4116 if (FAILED(rc))
4117 delete pMediumLockList;
4118 else
4119 {
4120 mData->mSession.mLockedMedia.Unlock();
4121 alock.release();
4122 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4123 mData->mSession.mLockedMedia.Lock();
4124 alock.acquire();
4125 }
4126 alock.release();
4127 }
4128
4129 if (SUCCEEDED(rc))
4130 {
4131 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4132 /* Remove lock list in case of error. */
4133 if (FAILED(rc))
4134 {
4135 mData->mSession.mLockedMedia.Unlock();
4136 mData->mSession.mLockedMedia.Remove(attachment);
4137 mData->mSession.mLockedMedia.Lock();
4138 }
4139 }
4140 }
4141
4142 /* Save modified registries, but skip this machine as it's the caller's
4143 * job to save its settings like all other settings changes. */
4144 mParent->i_unmarkRegistryModified(i_getId());
4145 mParent->i_saveModifiedRegistries();
4146
4147 return rc;
4148}
4149
4150HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4151 LONG aDevice)
4152{
4153 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4154 aName.c_str(), aControllerPort, aDevice));
4155
4156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4157
4158 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4159 if (FAILED(rc)) return rc;
4160
4161 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4162
4163 /* Check for an existing controller. */
4164 ComObjPtr<StorageController> ctl;
4165 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4166 if (FAILED(rc)) return rc;
4167
4168 StorageControllerType_T ctrlType;
4169 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4170 if (FAILED(rc))
4171 return setError(E_FAIL,
4172 tr("Could not get type of controller '%s'"),
4173 aName.c_str());
4174
4175 bool fSilent = false;
4176 Utf8Str strReconfig;
4177
4178 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4179 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4180 if ( mData->mMachineState == MachineState_Paused
4181 && strReconfig == "1")
4182 fSilent = true;
4183
4184 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4185 bool fHotplug = false;
4186 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4187 fHotplug = true;
4188
4189 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4190 return setError(VBOX_E_INVALID_VM_STATE,
4191 tr("Controller '%s' does not support hotplugging"),
4192 aName.c_str());
4193
4194 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4195 aName,
4196 aControllerPort,
4197 aDevice);
4198 if (!pAttach)
4199 return setError(VBOX_E_OBJECT_NOT_FOUND,
4200 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4201 aDevice, aControllerPort, aName.c_str());
4202
4203 if (fHotplug && !pAttach->i_getHotPluggable())
4204 return setError(VBOX_E_NOT_SUPPORTED,
4205 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4206 aDevice, aControllerPort, aName.c_str());
4207
4208 /*
4209 * The VM has to detach the device before we delete any implicit diffs.
4210 * If this fails we can roll back without loosing data.
4211 */
4212 if (fHotplug || fSilent)
4213 {
4214 alock.release();
4215 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4216 alock.acquire();
4217 }
4218 if (FAILED(rc)) return rc;
4219
4220 /* If we are here everything went well and we can delete the implicit now. */
4221 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4222
4223 alock.release();
4224
4225 /* Save modified registries, but skip this machine as it's the caller's
4226 * job to save its settings like all other settings changes. */
4227 mParent->i_unmarkRegistryModified(i_getId());
4228 mParent->i_saveModifiedRegistries();
4229
4230 return rc;
4231}
4232
4233HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4234 LONG aDevice, BOOL aPassthrough)
4235{
4236 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4237 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4238
4239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4240
4241 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4242 if (FAILED(rc)) return rc;
4243
4244 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4245
4246 /* Check for an existing controller. */
4247 ComObjPtr<StorageController> ctl;
4248 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4249 if (FAILED(rc)) return rc;
4250
4251 StorageControllerType_T ctrlType;
4252 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4253 if (FAILED(rc))
4254 return setError(E_FAIL,
4255 tr("Could not get type of controller '%s'"),
4256 aName.c_str());
4257
4258 bool fSilent = false;
4259 Utf8Str strReconfig;
4260
4261 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4262 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4263 if ( mData->mMachineState == MachineState_Paused
4264 && strReconfig == "1")
4265 fSilent = true;
4266
4267 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4268 bool fHotplug = false;
4269 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4270 fHotplug = true;
4271
4272 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4273 return setError(VBOX_E_INVALID_VM_STATE,
4274 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4275 aName.c_str());
4276
4277 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4278 aName,
4279 aControllerPort,
4280 aDevice);
4281 if (!pAttach)
4282 return setError(VBOX_E_OBJECT_NOT_FOUND,
4283 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4284 aDevice, aControllerPort, aName.c_str());
4285
4286
4287 i_setModified(IsModified_Storage);
4288 mMediumAttachments.backup();
4289
4290 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4291
4292 if (pAttach->i_getType() != DeviceType_DVD)
4293 return setError(E_INVALIDARG,
4294 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4295 aDevice, aControllerPort, aName.c_str());
4296 pAttach->i_updatePassthrough(!!aPassthrough);
4297
4298 attLock.release();
4299 alock.release();
4300 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4301
4302 return rc;
4303}
4304
4305HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4306 LONG aDevice, BOOL aTemporaryEject)
4307{
4308
4309 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4310 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4311
4312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4313
4314 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4315 if (FAILED(rc)) return rc;
4316
4317 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4318 aName,
4319 aControllerPort,
4320 aDevice);
4321 if (!pAttach)
4322 return setError(VBOX_E_OBJECT_NOT_FOUND,
4323 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4324 aDevice, aControllerPort, aName.c_str());
4325
4326
4327 i_setModified(IsModified_Storage);
4328 mMediumAttachments.backup();
4329
4330 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4331
4332 if (pAttach->i_getType() != DeviceType_DVD)
4333 return setError(E_INVALIDARG,
4334 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4335 aDevice, aControllerPort, aName.c_str());
4336 pAttach->i_updateTempEject(!!aTemporaryEject);
4337
4338 return S_OK;
4339}
4340
4341HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4342 LONG aDevice, BOOL aNonRotational)
4343{
4344
4345 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4346 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4347
4348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4349
4350 HRESULT rc = i_checkStateDependency(MutableStateDep);
4351 if (FAILED(rc)) return rc;
4352
4353 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4354
4355 if (Global::IsOnlineOrTransient(mData->mMachineState))
4356 return setError(VBOX_E_INVALID_VM_STATE,
4357 tr("Invalid machine state: %s"),
4358 Global::stringifyMachineState(mData->mMachineState));
4359
4360 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4361 aName,
4362 aControllerPort,
4363 aDevice);
4364 if (!pAttach)
4365 return setError(VBOX_E_OBJECT_NOT_FOUND,
4366 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4367 aDevice, aControllerPort, aName.c_str());
4368
4369
4370 i_setModified(IsModified_Storage);
4371 mMediumAttachments.backup();
4372
4373 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4374
4375 if (pAttach->i_getType() != DeviceType_HardDisk)
4376 return setError(E_INVALIDARG,
4377 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"),
4378 aDevice, aControllerPort, aName.c_str());
4379 pAttach->i_updateNonRotational(!!aNonRotational);
4380
4381 return S_OK;
4382}
4383
4384HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4385 LONG aDevice, BOOL aDiscard)
4386{
4387
4388 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4389 aName.c_str(), aControllerPort, aDevice, aDiscard));
4390
4391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4392
4393 HRESULT rc = i_checkStateDependency(MutableStateDep);
4394 if (FAILED(rc)) return rc;
4395
4396 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4397
4398 if (Global::IsOnlineOrTransient(mData->mMachineState))
4399 return setError(VBOX_E_INVALID_VM_STATE,
4400 tr("Invalid machine state: %s"),
4401 Global::stringifyMachineState(mData->mMachineState));
4402
4403 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4404 aName,
4405 aControllerPort,
4406 aDevice);
4407 if (!pAttach)
4408 return setError(VBOX_E_OBJECT_NOT_FOUND,
4409 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4410 aDevice, aControllerPort, aName.c_str());
4411
4412
4413 i_setModified(IsModified_Storage);
4414 mMediumAttachments.backup();
4415
4416 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4417
4418 if (pAttach->i_getType() != DeviceType_HardDisk)
4419 return setError(E_INVALIDARG,
4420 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"),
4421 aDevice, aControllerPort, aName.c_str());
4422 pAttach->i_updateDiscard(!!aDiscard);
4423
4424 return S_OK;
4425}
4426
4427HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4428 LONG aDevice, BOOL aHotPluggable)
4429{
4430 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4431 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4432
4433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4434
4435 HRESULT rc = i_checkStateDependency(MutableStateDep);
4436 if (FAILED(rc)) return rc;
4437
4438 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4439
4440 if (Global::IsOnlineOrTransient(mData->mMachineState))
4441 return setError(VBOX_E_INVALID_VM_STATE,
4442 tr("Invalid machine state: %s"),
4443 Global::stringifyMachineState(mData->mMachineState));
4444
4445 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4446 aName,
4447 aControllerPort,
4448 aDevice);
4449 if (!pAttach)
4450 return setError(VBOX_E_OBJECT_NOT_FOUND,
4451 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4452 aDevice, aControllerPort, aName.c_str());
4453
4454 /* Check for an existing controller. */
4455 ComObjPtr<StorageController> ctl;
4456 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4457 if (FAILED(rc)) return rc;
4458
4459 StorageControllerType_T ctrlType;
4460 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4461 if (FAILED(rc))
4462 return setError(E_FAIL,
4463 tr("Could not get type of controller '%s'"),
4464 aName.c_str());
4465
4466 if (!i_isControllerHotplugCapable(ctrlType))
4467 return setError(VBOX_E_NOT_SUPPORTED,
4468 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4469 aName.c_str());
4470
4471 i_setModified(IsModified_Storage);
4472 mMediumAttachments.backup();
4473
4474 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4475
4476 if (pAttach->i_getType() == DeviceType_Floppy)
4477 return setError(E_INVALIDARG,
4478 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"),
4479 aDevice, aControllerPort, aName.c_str());
4480 pAttach->i_updateHotPluggable(!!aHotPluggable);
4481
4482 return S_OK;
4483}
4484
4485HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4486 LONG aDevice)
4487{
4488 int rc = S_OK;
4489 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4490 aName.c_str(), aControllerPort, aDevice));
4491
4492 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4493
4494 return rc;
4495}
4496
4497HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4498 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4499{
4500 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4501 aName.c_str(), aControllerPort, aDevice));
4502
4503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4504
4505 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4506 if (FAILED(rc)) return rc;
4507
4508 if (Global::IsOnlineOrTransient(mData->mMachineState))
4509 return setError(VBOX_E_INVALID_VM_STATE,
4510 tr("Invalid machine state: %s"),
4511 Global::stringifyMachineState(mData->mMachineState));
4512
4513 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4514 aName,
4515 aControllerPort,
4516 aDevice);
4517 if (!pAttach)
4518 return setError(VBOX_E_OBJECT_NOT_FOUND,
4519 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4520 aDevice, aControllerPort, aName.c_str());
4521
4522
4523 i_setModified(IsModified_Storage);
4524 mMediumAttachments.backup();
4525
4526 IBandwidthGroup *iB = aBandwidthGroup;
4527 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4528 if (aBandwidthGroup && group.isNull())
4529 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4530
4531 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4532
4533 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4534 if (strBandwidthGroupOld.isNotEmpty())
4535 {
4536 /* Get the bandwidth group object and release it - this must not fail. */
4537 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4538 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4539 Assert(SUCCEEDED(rc));
4540
4541 pBandwidthGroupOld->i_release();
4542 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4543 }
4544
4545 if (!group.isNull())
4546 {
4547 group->i_reference();
4548 pAttach->i_updateBandwidthGroup(group->i_getName());
4549 }
4550
4551 return S_OK;
4552}
4553
4554HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4555 LONG aControllerPort,
4556 LONG aDevice,
4557 DeviceType_T aType)
4558{
4559 HRESULT rc = S_OK;
4560
4561 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4562 aName.c_str(), aControllerPort, aDevice, aType));
4563
4564 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4565
4566 return rc;
4567}
4568
4569
4570HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4571 LONG aControllerPort,
4572 LONG aDevice,
4573 BOOL aForce)
4574{
4575 int rc = S_OK;
4576 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4577 aName.c_str(), aControllerPort, aForce));
4578
4579 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4580
4581 return rc;
4582}
4583
4584HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4585 LONG aControllerPort,
4586 LONG aDevice,
4587 const ComPtr<IMedium> &aMedium,
4588 BOOL aForce)
4589{
4590 int rc = S_OK;
4591 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4592 aName.c_str(), aControllerPort, aDevice, aForce));
4593
4594 // request the host lock first, since might be calling Host methods for getting host drives;
4595 // next, protect the media tree all the while we're in here, as well as our member variables
4596 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4597 this->lockHandle(),
4598 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4599
4600 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4601 aName,
4602 aControllerPort,
4603 aDevice);
4604 if (pAttach.isNull())
4605 return setError(VBOX_E_OBJECT_NOT_FOUND,
4606 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4607 aDevice, aControllerPort, aName.c_str());
4608
4609 /* Remember previously mounted medium. The medium before taking the
4610 * backup is not necessarily the same thing. */
4611 ComObjPtr<Medium> oldmedium;
4612 oldmedium = pAttach->i_getMedium();
4613
4614 IMedium *iM = aMedium;
4615 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4616 if (aMedium && pMedium.isNull())
4617 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4618
4619 AutoCaller mediumCaller(pMedium);
4620 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4621
4622 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4623 if (pMedium)
4624 {
4625 DeviceType_T mediumType = pAttach->i_getType();
4626 switch (mediumType)
4627 {
4628 case DeviceType_DVD:
4629 case DeviceType_Floppy:
4630 break;
4631
4632 default:
4633 return setError(VBOX_E_INVALID_OBJECT_STATE,
4634 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4635 aControllerPort,
4636 aDevice,
4637 aName.c_str());
4638 }
4639 }
4640
4641 i_setModified(IsModified_Storage);
4642 mMediumAttachments.backup();
4643
4644 {
4645 // The backup operation makes the pAttach reference point to the
4646 // old settings. Re-get the correct reference.
4647 pAttach = i_findAttachment(*mMediumAttachments.data(),
4648 aName,
4649 aControllerPort,
4650 aDevice);
4651 if (!oldmedium.isNull())
4652 oldmedium->i_removeBackReference(mData->mUuid);
4653 if (!pMedium.isNull())
4654 {
4655 pMedium->i_addBackReference(mData->mUuid);
4656
4657 mediumLock.release();
4658 multiLock.release();
4659 i_addMediumToRegistry(pMedium);
4660 multiLock.acquire();
4661 mediumLock.acquire();
4662 }
4663
4664 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4665 pAttach->i_updateMedium(pMedium);
4666 }
4667
4668 i_setModified(IsModified_Storage);
4669
4670 mediumLock.release();
4671 multiLock.release();
4672 rc = i_onMediumChange(pAttach, aForce);
4673 multiLock.acquire();
4674 mediumLock.acquire();
4675
4676 /* On error roll back this change only. */
4677 if (FAILED(rc))
4678 {
4679 if (!pMedium.isNull())
4680 pMedium->i_removeBackReference(mData->mUuid);
4681 pAttach = i_findAttachment(*mMediumAttachments.data(),
4682 aName,
4683 aControllerPort,
4684 aDevice);
4685 /* If the attachment is gone in the meantime, bail out. */
4686 if (pAttach.isNull())
4687 return rc;
4688 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4689 if (!oldmedium.isNull())
4690 oldmedium->i_addBackReference(mData->mUuid);
4691 pAttach->i_updateMedium(oldmedium);
4692 }
4693
4694 mediumLock.release();
4695 multiLock.release();
4696
4697 /* Save modified registries, but skip this machine as it's the caller's
4698 * job to save its settings like all other settings changes. */
4699 mParent->i_unmarkRegistryModified(i_getId());
4700 mParent->i_saveModifiedRegistries();
4701
4702 return rc;
4703}
4704HRESULT Machine::getMedium(const com::Utf8Str &aName,
4705 LONG aControllerPort,
4706 LONG aDevice,
4707 ComPtr<IMedium> &aMedium)
4708{
4709 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4710 aName.c_str(), aControllerPort, aDevice));
4711
4712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4713
4714 aMedium = NULL;
4715
4716 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4717 aName,
4718 aControllerPort,
4719 aDevice);
4720 if (pAttach.isNull())
4721 return setError(VBOX_E_OBJECT_NOT_FOUND,
4722 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4723 aDevice, aControllerPort, aName.c_str());
4724
4725 aMedium = pAttach->i_getMedium();
4726
4727 return S_OK;
4728}
4729
4730HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4731{
4732
4733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4734
4735 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4736
4737 return S_OK;
4738}
4739
4740HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4741{
4742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4743
4744 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4745
4746 return S_OK;
4747}
4748
4749HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4750{
4751 /* Do not assert if slot is out of range, just return the advertised
4752 status. testdriver/vbox.py triggers this in logVmInfo. */
4753 if (aSlot >= mNetworkAdapters.size())
4754 return setError(E_INVALIDARG,
4755 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4756 aSlot, mNetworkAdapters.size());
4757
4758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4759
4760 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4761
4762 return S_OK;
4763}
4764
4765HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4766{
4767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4768
4769 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4770 size_t i = 0;
4771 for (settings::StringsMap::const_iterator
4772 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4773 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4774 ++it, ++i)
4775 aKeys[i] = it->first;
4776
4777 return S_OK;
4778}
4779
4780 /**
4781 * @note Locks this object for reading.
4782 */
4783HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4784 com::Utf8Str &aValue)
4785{
4786 /* start with nothing found */
4787 aValue = "";
4788
4789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4790
4791 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4792 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4793 // found:
4794 aValue = it->second; // source is a Utf8Str
4795
4796 /* return the result to caller (may be empty) */
4797 return S_OK;
4798}
4799
4800 /**
4801 * @note Locks mParent for writing + this object for writing.
4802 */
4803HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4804{
4805 /* Because non-ASCII characters in aKey have caused problems in the settings
4806 * they are rejected unless the key should be deleted. */
4807 if (!aValue.isEmpty())
4808 {
4809 for (size_t i = 0; i < aKey.length(); ++i)
4810 {
4811 char ch = aKey[i];
4812 if (!RTLocCIsPrint(ch))
4813 return E_INVALIDARG;
4814 }
4815 }
4816
4817 Utf8Str strOldValue; // empty
4818
4819 // locking note: we only hold the read lock briefly to look up the old value,
4820 // then release it and call the onExtraCanChange callbacks. There is a small
4821 // chance of a race insofar as the callback might be called twice if two callers
4822 // change the same key at the same time, but that's a much better solution
4823 // than the deadlock we had here before. The actual changing of the extradata
4824 // is then performed under the write lock and race-free.
4825
4826 // look up the old value first; if nothing has changed then we need not do anything
4827 {
4828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4829
4830 // For snapshots don't even think about allowing changes, extradata
4831 // is global for a machine, so there is nothing snapshot specific.
4832 if (i_isSnapshotMachine())
4833 return setError(VBOX_E_INVALID_VM_STATE,
4834 tr("Cannot set extradata for a snapshot"));
4835
4836 // check if the right IMachine instance is used
4837 if (mData->mRegistered && !i_isSessionMachine())
4838 return setError(VBOX_E_INVALID_VM_STATE,
4839 tr("Cannot set extradata for an immutable machine"));
4840
4841 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4842 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4843 strOldValue = it->second;
4844 }
4845
4846 bool fChanged;
4847 if ((fChanged = (strOldValue != aValue)))
4848 {
4849 // ask for permission from all listeners outside the locks;
4850 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4851 // lock to copy the list of callbacks to invoke
4852 Bstr error;
4853 Bstr bstrValue(aValue);
4854
4855 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4856 {
4857 const char *sep = error.isEmpty() ? "" : ": ";
4858 CBSTR err = error.raw();
4859 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4860 return setError(E_ACCESSDENIED,
4861 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4862 aKey.c_str(),
4863 aValue.c_str(),
4864 sep,
4865 err);
4866 }
4867
4868 // data is changing and change not vetoed: then write it out under the lock
4869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4870
4871 if (aValue.isEmpty())
4872 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4873 else
4874 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4875 // creates a new key if needed
4876
4877 bool fNeedsGlobalSaveSettings = false;
4878 // This saving of settings is tricky: there is no "old state" for the
4879 // extradata items at all (unlike all other settings), so the old/new
4880 // settings comparison would give a wrong result!
4881 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4882
4883 if (fNeedsGlobalSaveSettings)
4884 {
4885 // save the global settings; for that we should hold only the VirtualBox lock
4886 alock.release();
4887 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4888 mParent->i_saveSettings();
4889 }
4890 }
4891
4892 // fire notification outside the lock
4893 if (fChanged)
4894 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4895
4896 return S_OK;
4897}
4898
4899HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4900{
4901 aProgress = NULL;
4902 NOREF(aSettingsFilePath);
4903 ReturnComNotImplemented();
4904}
4905
4906HRESULT Machine::saveSettings()
4907{
4908 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4909
4910 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4911 if (FAILED(rc)) return rc;
4912
4913 /* the settings file path may never be null */
4914 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4915
4916 /* save all VM data excluding snapshots */
4917 bool fNeedsGlobalSaveSettings = false;
4918 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4919 mlock.release();
4920
4921 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4922 {
4923 // save the global settings; for that we should hold only the VirtualBox lock
4924 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4925 rc = mParent->i_saveSettings();
4926 }
4927
4928 return rc;
4929}
4930
4931
4932HRESULT Machine::discardSettings()
4933{
4934 /*
4935 * We need to take the machine list lock here as well as the machine one
4936 * or we'll get into trouble should any media stuff require rolling back.
4937 *
4938 * Details:
4939 *
4940 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4941 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4942 * 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]
4943 * 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
4944 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4945 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4946 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4947 * 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
4948 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4949 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4950 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4951 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4952 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4953 * 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]
4954 * 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] (*)
4955 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4956 * 0:005> k
4957 * # Child-SP RetAddr Call Site
4958 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4959 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4960 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4961 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4962 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4963 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4964 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4965 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4966 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4967 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4968 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4969 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4970 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4971 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4972 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4973 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4974 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4975 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4976 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4977 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4978 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4979 *
4980 */
4981 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4983
4984 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4985 if (FAILED(rc)) return rc;
4986
4987 /*
4988 * during this rollback, the session will be notified if data has
4989 * been actually changed
4990 */
4991 i_rollback(true /* aNotify */);
4992
4993 return S_OK;
4994}
4995
4996/** @note Locks objects! */
4997HRESULT Machine::unregister(AutoCaller &autoCaller,
4998 CleanupMode_T aCleanupMode,
4999 std::vector<ComPtr<IMedium> > &aMedia)
5000{
5001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5002
5003 Guid id(i_getId());
5004
5005 if (mData->mSession.mState != SessionState_Unlocked)
5006 return setError(VBOX_E_INVALID_OBJECT_STATE,
5007 tr("Cannot unregister the machine '%s' while it is locked"),
5008 mUserData->s.strName.c_str());
5009
5010 // wait for state dependents to drop to zero
5011 i_ensureNoStateDependencies();
5012
5013 if (!mData->mAccessible)
5014 {
5015 // inaccessible maschines can only be unregistered; uninitialize ourselves
5016 // here because currently there may be no unregistered that are inaccessible
5017 // (this state combination is not supported). Note releasing the caller and
5018 // leaving the lock before calling uninit()
5019 alock.release();
5020 autoCaller.release();
5021
5022 uninit();
5023
5024 mParent->i_unregisterMachine(this, id);
5025 // calls VirtualBox::i_saveSettings()
5026
5027 return S_OK;
5028 }
5029
5030 HRESULT rc = S_OK;
5031
5032 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5033 // discard saved state
5034 if (mData->mMachineState == MachineState_Saved)
5035 {
5036 // add the saved state file to the list of files the caller should delete
5037 Assert(!mSSData->strStateFilePath.isEmpty());
5038 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5039
5040 mSSData->strStateFilePath.setNull();
5041
5042 // unconditionally set the machine state to powered off, we now
5043 // know no session has locked the machine
5044 mData->mMachineState = MachineState_PoweredOff;
5045 }
5046
5047 size_t cSnapshots = 0;
5048 if (mData->mFirstSnapshot)
5049 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5050 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5051 // fail now before we start detaching media
5052 return setError(VBOX_E_INVALID_OBJECT_STATE,
5053 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5054 mUserData->s.strName.c_str(), cSnapshots);
5055
5056 // This list collects the medium objects from all medium attachments
5057 // which we will detach from the machine and its snapshots, in a specific
5058 // order which allows for closing all media without getting "media in use"
5059 // errors, simply by going through the list from the front to the back:
5060 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5061 // and must be closed before the parent media from the snapshots, or closing the parents
5062 // will fail because they still have children);
5063 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5064 // the root ("first") snapshot of the machine.
5065 MediaList llMedia;
5066
5067 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5068 && mMediumAttachments->size()
5069 )
5070 {
5071 // we have media attachments: detach them all and add the Medium objects to our list
5072 if (aCleanupMode != CleanupMode_UnregisterOnly)
5073 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5074 else
5075 return setError(VBOX_E_INVALID_OBJECT_STATE,
5076 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5077 mUserData->s.strName.c_str(), mMediumAttachments->size());
5078 }
5079
5080 if (cSnapshots)
5081 {
5082 // add the media from the medium attachments of the snapshots to llMedia
5083 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5084 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5085 // into the children first
5086
5087 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5088 MachineState_T oldState = mData->mMachineState;
5089 mData->mMachineState = MachineState_DeletingSnapshot;
5090
5091 // make a copy of the first snapshot so the refcount does not drop to 0
5092 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5093 // because of the AutoCaller voodoo)
5094 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5095
5096 // GO!
5097 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5098
5099 mData->mMachineState = oldState;
5100 }
5101
5102 if (FAILED(rc))
5103 {
5104 i_rollbackMedia();
5105 return rc;
5106 }
5107
5108 // commit all the media changes made above
5109 i_commitMedia();
5110
5111 mData->mRegistered = false;
5112
5113 // machine lock no longer needed
5114 alock.release();
5115
5116 // return media to caller
5117 aMedia.resize(llMedia.size());
5118 size_t i = 0;
5119 for (MediaList::const_iterator
5120 it = llMedia.begin();
5121 it != llMedia.end();
5122 ++it, ++i)
5123 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5124
5125 mParent->i_unregisterMachine(this, id);
5126 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5127
5128 return S_OK;
5129}
5130
5131/**
5132 * Task record for deleting a machine config.
5133 */
5134class Machine::DeleteConfigTask
5135 : public Machine::Task
5136{
5137public:
5138 DeleteConfigTask(Machine *m,
5139 Progress *p,
5140 const Utf8Str &t,
5141 const RTCList<ComPtr<IMedium> > &llMediums,
5142 const StringsList &llFilesToDelete)
5143 : Task(m, p, t),
5144 m_llMediums(llMediums),
5145 m_llFilesToDelete(llFilesToDelete)
5146 {}
5147
5148private:
5149 void handler()
5150 {
5151 try
5152 {
5153 m_pMachine->i_deleteConfigHandler(*this);
5154 }
5155 catch (...)
5156 {
5157 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5158 }
5159 }
5160
5161 RTCList<ComPtr<IMedium> > m_llMediums;
5162 StringsList m_llFilesToDelete;
5163
5164 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5165};
5166
5167/**
5168 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5169 * SessionMachine::taskHandler().
5170 *
5171 * @note Locks this object for writing.
5172 *
5173 * @param task
5174 * @return
5175 */
5176void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5177{
5178 LogFlowThisFuncEnter();
5179
5180 AutoCaller autoCaller(this);
5181 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5182 if (FAILED(autoCaller.rc()))
5183 {
5184 /* we might have been uninitialized because the session was accidentally
5185 * closed by the client, so don't assert */
5186 HRESULT rc = setError(E_FAIL,
5187 tr("The session has been accidentally closed"));
5188 task.m_pProgress->i_notifyComplete(rc);
5189 LogFlowThisFuncLeave();
5190 return;
5191 }
5192
5193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5194
5195 HRESULT rc = S_OK;
5196
5197 try
5198 {
5199 ULONG uLogHistoryCount = 3;
5200 ComPtr<ISystemProperties> systemProperties;
5201 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5202 if (FAILED(rc)) throw rc;
5203
5204 if (!systemProperties.isNull())
5205 {
5206 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5207 if (FAILED(rc)) throw rc;
5208 }
5209
5210 MachineState_T oldState = mData->mMachineState;
5211 i_setMachineState(MachineState_SettingUp);
5212 alock.release();
5213 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5214 {
5215 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5216 {
5217 AutoCaller mac(pMedium);
5218 if (FAILED(mac.rc())) throw mac.rc();
5219 Utf8Str strLocation = pMedium->i_getLocationFull();
5220 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5221 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5222 if (FAILED(rc)) throw rc;
5223 }
5224 if (pMedium->i_isMediumFormatFile())
5225 {
5226 ComPtr<IProgress> pProgress2;
5227 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5228 if (FAILED(rc)) throw rc;
5229 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5230 if (FAILED(rc)) throw rc;
5231 }
5232
5233 /* Close the medium, deliberately without checking the return
5234 * code, and without leaving any trace in the error info, as
5235 * a failure here is a very minor issue, which shouldn't happen
5236 * as above we even managed to delete the medium. */
5237 {
5238 ErrorInfoKeeper eik;
5239 pMedium->Close();
5240 }
5241 }
5242 i_setMachineState(oldState);
5243 alock.acquire();
5244
5245 // delete the files pushed on the task list by Machine::Delete()
5246 // (this includes saved states of the machine and snapshots and
5247 // medium storage files from the IMedium list passed in, and the
5248 // machine XML file)
5249 for (StringsList::const_iterator
5250 it = task.m_llFilesToDelete.begin();
5251 it != task.m_llFilesToDelete.end();
5252 ++it)
5253 {
5254 const Utf8Str &strFile = *it;
5255 LogFunc(("Deleting file %s\n", strFile.c_str()));
5256 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5257 if (FAILED(rc)) throw rc;
5258
5259 int vrc = RTFileDelete(strFile.c_str());
5260 if (RT_FAILURE(vrc))
5261 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5262 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5263 }
5264
5265 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5266 if (FAILED(rc)) throw rc;
5267
5268 /* delete the settings only when the file actually exists */
5269 if (mData->pMachineConfigFile->fileExists())
5270 {
5271 /* Delete any backup or uncommitted XML files. Ignore failures.
5272 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5273 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5274 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5275 RTFileDelete(otherXml.c_str());
5276 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5277 RTFileDelete(otherXml.c_str());
5278
5279 /* delete the Logs folder, nothing important should be left
5280 * there (we don't check for errors because the user might have
5281 * some private files there that we don't want to delete) */
5282 Utf8Str logFolder;
5283 getLogFolder(logFolder);
5284 Assert(logFolder.length());
5285 if (RTDirExists(logFolder.c_str()))
5286 {
5287 /* Delete all VBox.log[.N] files from the Logs folder
5288 * (this must be in sync with the rotation logic in
5289 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5290 * files that may have been created by the GUI. */
5291 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5292 logFolder.c_str(), RTPATH_DELIMITER);
5293 RTFileDelete(log.c_str());
5294 log = Utf8StrFmt("%s%cVBox.png",
5295 logFolder.c_str(), RTPATH_DELIMITER);
5296 RTFileDelete(log.c_str());
5297 for (int i = uLogHistoryCount; i > 0; i--)
5298 {
5299 log = Utf8StrFmt("%s%cVBox.log.%d",
5300 logFolder.c_str(), RTPATH_DELIMITER, i);
5301 RTFileDelete(log.c_str());
5302 log = Utf8StrFmt("%s%cVBox.png.%d",
5303 logFolder.c_str(), RTPATH_DELIMITER, i);
5304 RTFileDelete(log.c_str());
5305 }
5306#if defined(RT_OS_WINDOWS)
5307 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5308 RTFileDelete(log.c_str());
5309 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5310 RTFileDelete(log.c_str());
5311#endif
5312
5313 RTDirRemove(logFolder.c_str());
5314 }
5315
5316 /* delete the Snapshots folder, nothing important should be left
5317 * there (we don't check for errors because the user might have
5318 * some private files there that we don't want to delete) */
5319 Utf8Str strFullSnapshotFolder;
5320 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5321 Assert(!strFullSnapshotFolder.isEmpty());
5322 if (RTDirExists(strFullSnapshotFolder.c_str()))
5323 RTDirRemove(strFullSnapshotFolder.c_str());
5324
5325 // delete the directory that contains the settings file, but only
5326 // if it matches the VM name
5327 Utf8Str settingsDir;
5328 if (i_isInOwnDir(&settingsDir))
5329 RTDirRemove(settingsDir.c_str());
5330 }
5331
5332 alock.release();
5333
5334 mParent->i_saveModifiedRegistries();
5335 }
5336 catch (HRESULT aRC) { rc = aRC; }
5337
5338 task.m_pProgress->i_notifyComplete(rc);
5339
5340 LogFlowThisFuncLeave();
5341}
5342
5343HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5344{
5345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5346
5347 HRESULT rc = i_checkStateDependency(MutableStateDep);
5348 if (FAILED(rc)) return rc;
5349
5350 if (mData->mRegistered)
5351 return setError(VBOX_E_INVALID_VM_STATE,
5352 tr("Cannot delete settings of a registered machine"));
5353
5354 // collect files to delete
5355 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5356 if (mData->pMachineConfigFile->fileExists())
5357 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5358
5359 RTCList<ComPtr<IMedium> > llMediums;
5360 for (size_t i = 0; i < aMedia.size(); ++i)
5361 {
5362 IMedium *pIMedium(aMedia[i]);
5363 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5364 if (pMedium.isNull())
5365 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5366 SafeArray<BSTR> ids;
5367 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5368 if (FAILED(rc)) return rc;
5369 /* At this point the medium should not have any back references
5370 * anymore. If it has it is attached to another VM and *must* not
5371 * deleted. */
5372 if (ids.size() < 1)
5373 llMediums.append(pMedium);
5374 }
5375
5376 ComObjPtr<Progress> pProgress;
5377 pProgress.createObject();
5378 rc = pProgress->init(i_getVirtualBox(),
5379 static_cast<IMachine*>(this) /* aInitiator */,
5380 tr("Deleting files"),
5381 true /* fCancellable */,
5382 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5383 tr("Collecting file inventory"));
5384 if (FAILED(rc))
5385 return rc;
5386
5387 /* create and start the task on a separate thread (note that it will not
5388 * start working until we release alock) */
5389 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5390 rc = pTask->createThread();
5391 if (FAILED(rc))
5392 return rc;
5393
5394 pProgress.queryInterfaceTo(aProgress.asOutParam());
5395
5396 LogFlowFuncLeave();
5397
5398 return S_OK;
5399}
5400
5401HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5402{
5403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5404
5405 ComObjPtr<Snapshot> pSnapshot;
5406 HRESULT rc;
5407
5408 if (aNameOrId.isEmpty())
5409 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5410 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5411 else
5412 {
5413 Guid uuid(aNameOrId);
5414 if (uuid.isValid())
5415 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5416 else
5417 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5418 }
5419 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5420
5421 return rc;
5422}
5423
5424HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5425{
5426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5427
5428 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5429 if (FAILED(rc)) return rc;
5430
5431 ComObjPtr<SharedFolder> sharedFolder;
5432 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5433 if (SUCCEEDED(rc))
5434 return setError(VBOX_E_OBJECT_IN_USE,
5435 tr("Shared folder named '%s' already exists"),
5436 aName.c_str());
5437
5438 sharedFolder.createObject();
5439 rc = sharedFolder->init(i_getMachine(),
5440 aName,
5441 aHostPath,
5442 !!aWritable,
5443 !!aAutomount,
5444 true /* fFailOnError */);
5445 if (FAILED(rc)) return rc;
5446
5447 i_setModified(IsModified_SharedFolders);
5448 mHWData.backup();
5449 mHWData->mSharedFolders.push_back(sharedFolder);
5450
5451 /* inform the direct session if any */
5452 alock.release();
5453 i_onSharedFolderChange();
5454
5455 return S_OK;
5456}
5457
5458HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5459{
5460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5461
5462 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5463 if (FAILED(rc)) return rc;
5464
5465 ComObjPtr<SharedFolder> sharedFolder;
5466 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5467 if (FAILED(rc)) return rc;
5468
5469 i_setModified(IsModified_SharedFolders);
5470 mHWData.backup();
5471 mHWData->mSharedFolders.remove(sharedFolder);
5472
5473 /* inform the direct session if any */
5474 alock.release();
5475 i_onSharedFolderChange();
5476
5477 return S_OK;
5478}
5479
5480HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5481{
5482 /* start with No */
5483 *aCanShow = FALSE;
5484
5485 ComPtr<IInternalSessionControl> directControl;
5486 {
5487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5488
5489 if (mData->mSession.mState != SessionState_Locked)
5490 return setError(VBOX_E_INVALID_VM_STATE,
5491 tr("Machine is not locked for session (session state: %s)"),
5492 Global::stringifySessionState(mData->mSession.mState));
5493
5494 if (mData->mSession.mLockType == LockType_VM)
5495 directControl = mData->mSession.mDirectControl;
5496 }
5497
5498 /* ignore calls made after #OnSessionEnd() is called */
5499 if (!directControl)
5500 return S_OK;
5501
5502 LONG64 dummy;
5503 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5504}
5505
5506HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5507{
5508 ComPtr<IInternalSessionControl> directControl;
5509 {
5510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5511
5512 if (mData->mSession.mState != SessionState_Locked)
5513 return setError(E_FAIL,
5514 tr("Machine is not locked for session (session state: %s)"),
5515 Global::stringifySessionState(mData->mSession.mState));
5516
5517 if (mData->mSession.mLockType == LockType_VM)
5518 directControl = mData->mSession.mDirectControl;
5519 }
5520
5521 /* ignore calls made after #OnSessionEnd() is called */
5522 if (!directControl)
5523 return S_OK;
5524
5525 BOOL dummy;
5526 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5527}
5528
5529#ifdef VBOX_WITH_GUEST_PROPS
5530/**
5531 * Look up a guest property in VBoxSVC's internal structures.
5532 */
5533HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5534 com::Utf8Str &aValue,
5535 LONG64 *aTimestamp,
5536 com::Utf8Str &aFlags) const
5537{
5538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5539
5540 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5541 if (it != mHWData->mGuestProperties.end())
5542 {
5543 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5544 aValue = it->second.strValue;
5545 *aTimestamp = it->second.mTimestamp;
5546 GuestPropWriteFlags(it->second.mFlags, szFlags);
5547 aFlags = Utf8Str(szFlags);
5548 }
5549
5550 return S_OK;
5551}
5552
5553/**
5554 * Query the VM that a guest property belongs to for the property.
5555 * @returns E_ACCESSDENIED if the VM process is not available or not
5556 * currently handling queries and the lookup should then be done in
5557 * VBoxSVC.
5558 */
5559HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5560 com::Utf8Str &aValue,
5561 LONG64 *aTimestamp,
5562 com::Utf8Str &aFlags) const
5563{
5564 HRESULT rc = S_OK;
5565 BSTR bValue = NULL;
5566 BSTR bFlags = NULL;
5567
5568 ComPtr<IInternalSessionControl> directControl;
5569 {
5570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5571 if (mData->mSession.mLockType == LockType_VM)
5572 directControl = mData->mSession.mDirectControl;
5573 }
5574
5575 /* ignore calls made after #OnSessionEnd() is called */
5576 if (!directControl)
5577 rc = E_ACCESSDENIED;
5578 else
5579 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5580 0 /* accessMode */,
5581 &bValue, aTimestamp, &bFlags);
5582
5583 aValue = bValue;
5584 aFlags = bFlags;
5585
5586 return rc;
5587}
5588#endif // VBOX_WITH_GUEST_PROPS
5589
5590HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5591 com::Utf8Str &aValue,
5592 LONG64 *aTimestamp,
5593 com::Utf8Str &aFlags)
5594{
5595#ifndef VBOX_WITH_GUEST_PROPS
5596 ReturnComNotImplemented();
5597#else // VBOX_WITH_GUEST_PROPS
5598
5599 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5600
5601 if (rc == E_ACCESSDENIED)
5602 /* The VM is not running or the service is not (yet) accessible */
5603 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5604 return rc;
5605#endif // VBOX_WITH_GUEST_PROPS
5606}
5607
5608HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5609{
5610 LONG64 dummyTimestamp;
5611 com::Utf8Str dummyFlags;
5612 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5613 return rc;
5614
5615}
5616HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5617{
5618 com::Utf8Str dummyFlags;
5619 com::Utf8Str dummyValue;
5620 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5621 return rc;
5622}
5623
5624#ifdef VBOX_WITH_GUEST_PROPS
5625/**
5626 * Set a guest property in VBoxSVC's internal structures.
5627 */
5628HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5629 const com::Utf8Str &aFlags, bool fDelete)
5630{
5631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5632 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5633 if (FAILED(rc)) return rc;
5634
5635 try
5636 {
5637 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5638 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5639 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5640
5641 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5642 if (it == mHWData->mGuestProperties.end())
5643 {
5644 if (!fDelete)
5645 {
5646 i_setModified(IsModified_MachineData);
5647 mHWData.backupEx();
5648
5649 RTTIMESPEC time;
5650 HWData::GuestProperty prop;
5651 prop.strValue = Bstr(aValue).raw();
5652 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5653 prop.mFlags = fFlags;
5654 mHWData->mGuestProperties[aName] = prop;
5655 }
5656 }
5657 else
5658 {
5659 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5660 {
5661 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5662 }
5663 else
5664 {
5665 i_setModified(IsModified_MachineData);
5666 mHWData.backupEx();
5667
5668 /* The backupEx() operation invalidates our iterator,
5669 * so get a new one. */
5670 it = mHWData->mGuestProperties.find(aName);
5671 Assert(it != mHWData->mGuestProperties.end());
5672
5673 if (!fDelete)
5674 {
5675 RTTIMESPEC time;
5676 it->second.strValue = aValue;
5677 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5678 it->second.mFlags = fFlags;
5679 }
5680 else
5681 mHWData->mGuestProperties.erase(it);
5682 }
5683 }
5684
5685 if (SUCCEEDED(rc))
5686 {
5687 alock.release();
5688
5689 mParent->i_onGuestPropertyChange(mData->mUuid,
5690 Bstr(aName).raw(),
5691 Bstr(aValue).raw(),
5692 Bstr(aFlags).raw());
5693 }
5694 }
5695 catch (std::bad_alloc &)
5696 {
5697 rc = E_OUTOFMEMORY;
5698 }
5699
5700 return rc;
5701}
5702
5703/**
5704 * Set a property on the VM that that property belongs to.
5705 * @returns E_ACCESSDENIED if the VM process is not available or not
5706 * currently handling queries and the setting should then be done in
5707 * VBoxSVC.
5708 */
5709HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5710 const com::Utf8Str &aFlags, bool fDelete)
5711{
5712 HRESULT rc;
5713
5714 try
5715 {
5716 ComPtr<IInternalSessionControl> directControl;
5717 {
5718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5719 if (mData->mSession.mLockType == LockType_VM)
5720 directControl = mData->mSession.mDirectControl;
5721 }
5722
5723 BSTR dummy = NULL; /* will not be changed (setter) */
5724 LONG64 dummy64;
5725 if (!directControl)
5726 rc = E_ACCESSDENIED;
5727 else
5728 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5729 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5730 fDelete? 2: 1 /* accessMode */,
5731 &dummy, &dummy64, &dummy);
5732 }
5733 catch (std::bad_alloc &)
5734 {
5735 rc = E_OUTOFMEMORY;
5736 }
5737
5738 return rc;
5739}
5740#endif // VBOX_WITH_GUEST_PROPS
5741
5742HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5743 const com::Utf8Str &aFlags)
5744{
5745#ifndef VBOX_WITH_GUEST_PROPS
5746 ReturnComNotImplemented();
5747#else // VBOX_WITH_GUEST_PROPS
5748 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5749 if (rc == E_ACCESSDENIED)
5750 /* The VM is not running or the service is not (yet) accessible */
5751 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5752 return rc;
5753#endif // VBOX_WITH_GUEST_PROPS
5754}
5755
5756HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5757{
5758 return setGuestProperty(aProperty, aValue, "");
5759}
5760
5761HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5762{
5763#ifndef VBOX_WITH_GUEST_PROPS
5764 ReturnComNotImplemented();
5765#else // VBOX_WITH_GUEST_PROPS
5766 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5767 if (rc == E_ACCESSDENIED)
5768 /* The VM is not running or the service is not (yet) accessible */
5769 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5770 return rc;
5771#endif // VBOX_WITH_GUEST_PROPS
5772}
5773
5774#ifdef VBOX_WITH_GUEST_PROPS
5775/**
5776 * Enumerate the guest properties in VBoxSVC's internal structures.
5777 */
5778HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5779 std::vector<com::Utf8Str> &aNames,
5780 std::vector<com::Utf8Str> &aValues,
5781 std::vector<LONG64> &aTimestamps,
5782 std::vector<com::Utf8Str> &aFlags)
5783{
5784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5785 Utf8Str strPatterns(aPatterns);
5786
5787 /*
5788 * Look for matching patterns and build up a list.
5789 */
5790 HWData::GuestPropertyMap propMap;
5791 for (HWData::GuestPropertyMap::const_iterator
5792 it = mHWData->mGuestProperties.begin();
5793 it != mHWData->mGuestProperties.end();
5794 ++it)
5795 {
5796 if ( strPatterns.isEmpty()
5797 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5798 RTSTR_MAX,
5799 it->first.c_str(),
5800 RTSTR_MAX,
5801 NULL)
5802 )
5803 propMap.insert(*it);
5804 }
5805
5806 alock.release();
5807
5808 /*
5809 * And build up the arrays for returning the property information.
5810 */
5811 size_t cEntries = propMap.size();
5812
5813 aNames.resize(cEntries);
5814 aValues.resize(cEntries);
5815 aTimestamps.resize(cEntries);
5816 aFlags.resize(cEntries);
5817
5818 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5819 size_t i = 0;
5820 for (HWData::GuestPropertyMap::const_iterator
5821 it = propMap.begin();
5822 it != propMap.end();
5823 ++it, ++i)
5824 {
5825 aNames[i] = it->first;
5826 aValues[i] = it->second.strValue;
5827 aTimestamps[i] = it->second.mTimestamp;
5828 GuestPropWriteFlags(it->second.mFlags, szFlags);
5829 aFlags[i] = Utf8Str(szFlags);
5830 }
5831
5832 return S_OK;
5833}
5834
5835/**
5836 * Enumerate the properties managed by a VM.
5837 * @returns E_ACCESSDENIED if the VM process is not available or not
5838 * currently handling queries and the setting should then be done in
5839 * VBoxSVC.
5840 */
5841HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5842 std::vector<com::Utf8Str> &aNames,
5843 std::vector<com::Utf8Str> &aValues,
5844 std::vector<LONG64> &aTimestamps,
5845 std::vector<com::Utf8Str> &aFlags)
5846{
5847 HRESULT rc;
5848 ComPtr<IInternalSessionControl> directControl;
5849 {
5850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5851 if (mData->mSession.mLockType == LockType_VM)
5852 directControl = mData->mSession.mDirectControl;
5853 }
5854
5855 com::SafeArray<BSTR> bNames;
5856 com::SafeArray<BSTR> bValues;
5857 com::SafeArray<LONG64> bTimestamps;
5858 com::SafeArray<BSTR> bFlags;
5859
5860 if (!directControl)
5861 rc = E_ACCESSDENIED;
5862 else
5863 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5864 ComSafeArrayAsOutParam(bNames),
5865 ComSafeArrayAsOutParam(bValues),
5866 ComSafeArrayAsOutParam(bTimestamps),
5867 ComSafeArrayAsOutParam(bFlags));
5868 size_t i;
5869 aNames.resize(bNames.size());
5870 for (i = 0; i < bNames.size(); ++i)
5871 aNames[i] = Utf8Str(bNames[i]);
5872 aValues.resize(bValues.size());
5873 for (i = 0; i < bValues.size(); ++i)
5874 aValues[i] = Utf8Str(bValues[i]);
5875 aTimestamps.resize(bTimestamps.size());
5876 for (i = 0; i < bTimestamps.size(); ++i)
5877 aTimestamps[i] = bTimestamps[i];
5878 aFlags.resize(bFlags.size());
5879 for (i = 0; i < bFlags.size(); ++i)
5880 aFlags[i] = Utf8Str(bFlags[i]);
5881
5882 return rc;
5883}
5884#endif // VBOX_WITH_GUEST_PROPS
5885HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5886 std::vector<com::Utf8Str> &aNames,
5887 std::vector<com::Utf8Str> &aValues,
5888 std::vector<LONG64> &aTimestamps,
5889 std::vector<com::Utf8Str> &aFlags)
5890{
5891#ifndef VBOX_WITH_GUEST_PROPS
5892 ReturnComNotImplemented();
5893#else // VBOX_WITH_GUEST_PROPS
5894
5895 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5896
5897 if (rc == E_ACCESSDENIED)
5898 /* The VM is not running or the service is not (yet) accessible */
5899 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5900 return rc;
5901#endif // VBOX_WITH_GUEST_PROPS
5902}
5903
5904HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5905 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5906{
5907 MediumAttachmentList atts;
5908
5909 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5910 if (FAILED(rc)) return rc;
5911
5912 aMediumAttachments.resize(atts.size());
5913 size_t i = 0;
5914 for (MediumAttachmentList::const_iterator
5915 it = atts.begin();
5916 it != atts.end();
5917 ++it, ++i)
5918 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5919
5920 return S_OK;
5921}
5922
5923HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5924 LONG aControllerPort,
5925 LONG aDevice,
5926 ComPtr<IMediumAttachment> &aAttachment)
5927{
5928 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5929 aName.c_str(), aControllerPort, aDevice));
5930
5931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5932
5933 aAttachment = NULL;
5934
5935 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5936 aName,
5937 aControllerPort,
5938 aDevice);
5939 if (pAttach.isNull())
5940 return setError(VBOX_E_OBJECT_NOT_FOUND,
5941 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5942 aDevice, aControllerPort, aName.c_str());
5943
5944 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5945
5946 return S_OK;
5947}
5948
5949
5950HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5951 StorageBus_T aConnectionType,
5952 ComPtr<IStorageController> &aController)
5953{
5954 if ( (aConnectionType <= StorageBus_Null)
5955 || (aConnectionType > StorageBus_PCIe))
5956 return setError(E_INVALIDARG,
5957 tr("Invalid connection type: %d"),
5958 aConnectionType);
5959
5960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5961
5962 HRESULT rc = i_checkStateDependency(MutableStateDep);
5963 if (FAILED(rc)) return rc;
5964
5965 /* try to find one with the name first. */
5966 ComObjPtr<StorageController> ctrl;
5967
5968 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5969 if (SUCCEEDED(rc))
5970 return setError(VBOX_E_OBJECT_IN_USE,
5971 tr("Storage controller named '%s' already exists"),
5972 aName.c_str());
5973
5974 ctrl.createObject();
5975
5976 /* get a new instance number for the storage controller */
5977 ULONG ulInstance = 0;
5978 bool fBootable = true;
5979 for (StorageControllerList::const_iterator
5980 it = mStorageControllers->begin();
5981 it != mStorageControllers->end();
5982 ++it)
5983 {
5984 if ((*it)->i_getStorageBus() == aConnectionType)
5985 {
5986 ULONG ulCurInst = (*it)->i_getInstance();
5987
5988 if (ulCurInst >= ulInstance)
5989 ulInstance = ulCurInst + 1;
5990
5991 /* Only one controller of each type can be marked as bootable. */
5992 if ((*it)->i_getBootable())
5993 fBootable = false;
5994 }
5995 }
5996
5997 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5998 if (FAILED(rc)) return rc;
5999
6000 i_setModified(IsModified_Storage);
6001 mStorageControllers.backup();
6002 mStorageControllers->push_back(ctrl);
6003
6004 ctrl.queryInterfaceTo(aController.asOutParam());
6005
6006 /* inform the direct session if any */
6007 alock.release();
6008 i_onStorageControllerChange();
6009
6010 return S_OK;
6011}
6012
6013HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6014 ComPtr<IStorageController> &aStorageController)
6015{
6016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6017
6018 ComObjPtr<StorageController> ctrl;
6019
6020 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6021 if (SUCCEEDED(rc))
6022 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6023
6024 return rc;
6025}
6026
6027HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6028 ULONG aInstance,
6029 ComPtr<IStorageController> &aStorageController)
6030{
6031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6032
6033 for (StorageControllerList::const_iterator
6034 it = mStorageControllers->begin();
6035 it != mStorageControllers->end();
6036 ++it)
6037 {
6038 if ( (*it)->i_getStorageBus() == aConnectionType
6039 && (*it)->i_getInstance() == aInstance)
6040 {
6041 (*it).queryInterfaceTo(aStorageController.asOutParam());
6042 return S_OK;
6043 }
6044 }
6045
6046 return setError(VBOX_E_OBJECT_NOT_FOUND,
6047 tr("Could not find a storage controller with instance number '%lu'"),
6048 aInstance);
6049}
6050
6051HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6052{
6053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6054
6055 HRESULT rc = i_checkStateDependency(MutableStateDep);
6056 if (FAILED(rc)) return rc;
6057
6058 ComObjPtr<StorageController> ctrl;
6059
6060 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6061 if (SUCCEEDED(rc))
6062 {
6063 /* Ensure that only one controller of each type is marked as bootable. */
6064 if (aBootable == TRUE)
6065 {
6066 for (StorageControllerList::const_iterator
6067 it = mStorageControllers->begin();
6068 it != mStorageControllers->end();
6069 ++it)
6070 {
6071 ComObjPtr<StorageController> aCtrl = (*it);
6072
6073 if ( (aCtrl->i_getName() != aName)
6074 && aCtrl->i_getBootable() == TRUE
6075 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6076 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6077 {
6078 aCtrl->i_setBootable(FALSE);
6079 break;
6080 }
6081 }
6082 }
6083
6084 if (SUCCEEDED(rc))
6085 {
6086 ctrl->i_setBootable(aBootable);
6087 i_setModified(IsModified_Storage);
6088 }
6089 }
6090
6091 if (SUCCEEDED(rc))
6092 {
6093 /* inform the direct session if any */
6094 alock.release();
6095 i_onStorageControllerChange();
6096 }
6097
6098 return rc;
6099}
6100
6101HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6102{
6103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6104
6105 HRESULT rc = i_checkStateDependency(MutableStateDep);
6106 if (FAILED(rc)) return rc;
6107
6108 ComObjPtr<StorageController> ctrl;
6109 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6110 if (FAILED(rc)) return rc;
6111
6112 {
6113 /* find all attached devices to the appropriate storage controller and detach them all */
6114 // make a temporary list because detachDevice invalidates iterators into
6115 // mMediumAttachments
6116 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6117
6118 for (MediumAttachmentList::const_iterator
6119 it = llAttachments2.begin();
6120 it != llAttachments2.end();
6121 ++it)
6122 {
6123 MediumAttachment *pAttachTemp = *it;
6124
6125 AutoCaller localAutoCaller(pAttachTemp);
6126 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6127
6128 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6129
6130 if (pAttachTemp->i_getControllerName() == aName)
6131 {
6132 rc = i_detachDevice(pAttachTemp, alock, NULL);
6133 if (FAILED(rc)) return rc;
6134 }
6135 }
6136 }
6137
6138 /* We can remove it now. */
6139 i_setModified(IsModified_Storage);
6140 mStorageControllers.backup();
6141
6142 ctrl->i_unshare();
6143
6144 mStorageControllers->remove(ctrl);
6145
6146 /* inform the direct session if any */
6147 alock.release();
6148 i_onStorageControllerChange();
6149
6150 return S_OK;
6151}
6152
6153HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6154 ComPtr<IUSBController> &aController)
6155{
6156 if ( (aType <= USBControllerType_Null)
6157 || (aType >= USBControllerType_Last))
6158 return setError(E_INVALIDARG,
6159 tr("Invalid USB controller type: %d"),
6160 aType);
6161
6162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6163
6164 HRESULT rc = i_checkStateDependency(MutableStateDep);
6165 if (FAILED(rc)) return rc;
6166
6167 /* try to find one with the same type first. */
6168 ComObjPtr<USBController> ctrl;
6169
6170 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6171 if (SUCCEEDED(rc))
6172 return setError(VBOX_E_OBJECT_IN_USE,
6173 tr("USB controller named '%s' already exists"),
6174 aName.c_str());
6175
6176 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6177 ULONG maxInstances;
6178 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6179 if (FAILED(rc))
6180 return rc;
6181
6182 ULONG cInstances = i_getUSBControllerCountByType(aType);
6183 if (cInstances >= maxInstances)
6184 return setError(E_INVALIDARG,
6185 tr("Too many USB controllers of this type"));
6186
6187 ctrl.createObject();
6188
6189 rc = ctrl->init(this, aName, aType);
6190 if (FAILED(rc)) return rc;
6191
6192 i_setModified(IsModified_USB);
6193 mUSBControllers.backup();
6194 mUSBControllers->push_back(ctrl);
6195
6196 ctrl.queryInterfaceTo(aController.asOutParam());
6197
6198 /* inform the direct session if any */
6199 alock.release();
6200 i_onUSBControllerChange();
6201
6202 return S_OK;
6203}
6204
6205HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6206{
6207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6208
6209 ComObjPtr<USBController> ctrl;
6210
6211 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6212 if (SUCCEEDED(rc))
6213 ctrl.queryInterfaceTo(aController.asOutParam());
6214
6215 return rc;
6216}
6217
6218HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6219 ULONG *aControllers)
6220{
6221 if ( (aType <= USBControllerType_Null)
6222 || (aType >= USBControllerType_Last))
6223 return setError(E_INVALIDARG,
6224 tr("Invalid USB controller type: %d"),
6225 aType);
6226
6227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6228
6229 ComObjPtr<USBController> ctrl;
6230
6231 *aControllers = i_getUSBControllerCountByType(aType);
6232
6233 return S_OK;
6234}
6235
6236HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6237{
6238
6239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6240
6241 HRESULT rc = i_checkStateDependency(MutableStateDep);
6242 if (FAILED(rc)) return rc;
6243
6244 ComObjPtr<USBController> ctrl;
6245 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6246 if (FAILED(rc)) return rc;
6247
6248 i_setModified(IsModified_USB);
6249 mUSBControllers.backup();
6250
6251 ctrl->i_unshare();
6252
6253 mUSBControllers->remove(ctrl);
6254
6255 /* inform the direct session if any */
6256 alock.release();
6257 i_onUSBControllerChange();
6258
6259 return S_OK;
6260}
6261
6262HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6263 ULONG *aOriginX,
6264 ULONG *aOriginY,
6265 ULONG *aWidth,
6266 ULONG *aHeight,
6267 BOOL *aEnabled)
6268{
6269 uint32_t u32OriginX= 0;
6270 uint32_t u32OriginY= 0;
6271 uint32_t u32Width = 0;
6272 uint32_t u32Height = 0;
6273 uint16_t u16Flags = 0;
6274
6275 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6276 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6277 if (RT_FAILURE(vrc))
6278 {
6279#ifdef RT_OS_WINDOWS
6280 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6281 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6282 * So just assign fEnable to TRUE again.
6283 * The right fix would be to change GUI API wrappers to make sure that parameters
6284 * are changed only if API succeeds.
6285 */
6286 *aEnabled = TRUE;
6287#endif
6288 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6289 tr("Saved guest size is not available (%Rrc)"),
6290 vrc);
6291 }
6292
6293 *aOriginX = u32OriginX;
6294 *aOriginY = u32OriginY;
6295 *aWidth = u32Width;
6296 *aHeight = u32Height;
6297 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6298
6299 return S_OK;
6300}
6301
6302HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6303 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6304{
6305 if (aScreenId != 0)
6306 return E_NOTIMPL;
6307
6308 if ( aBitmapFormat != BitmapFormat_BGR0
6309 && aBitmapFormat != BitmapFormat_BGRA
6310 && aBitmapFormat != BitmapFormat_RGBA
6311 && aBitmapFormat != BitmapFormat_PNG)
6312 return setError(E_NOTIMPL,
6313 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6314
6315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6316
6317 uint8_t *pu8Data = NULL;
6318 uint32_t cbData = 0;
6319 uint32_t u32Width = 0;
6320 uint32_t u32Height = 0;
6321
6322 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6323
6324 if (RT_FAILURE(vrc))
6325 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6326 tr("Saved thumbnail data is not available (%Rrc)"),
6327 vrc);
6328
6329 HRESULT hr = S_OK;
6330
6331 *aWidth = u32Width;
6332 *aHeight = u32Height;
6333
6334 if (cbData > 0)
6335 {
6336 /* Convert pixels to the format expected by the API caller. */
6337 if (aBitmapFormat == BitmapFormat_BGR0)
6338 {
6339 /* [0] B, [1] G, [2] R, [3] 0. */
6340 aData.resize(cbData);
6341 memcpy(&aData.front(), pu8Data, cbData);
6342 }
6343 else if (aBitmapFormat == BitmapFormat_BGRA)
6344 {
6345 /* [0] B, [1] G, [2] R, [3] A. */
6346 aData.resize(cbData);
6347 for (uint32_t i = 0; i < cbData; i += 4)
6348 {
6349 aData[i] = pu8Data[i];
6350 aData[i + 1] = pu8Data[i + 1];
6351 aData[i + 2] = pu8Data[i + 2];
6352 aData[i + 3] = 0xff;
6353 }
6354 }
6355 else if (aBitmapFormat == BitmapFormat_RGBA)
6356 {
6357 /* [0] R, [1] G, [2] B, [3] A. */
6358 aData.resize(cbData);
6359 for (uint32_t i = 0; i < cbData; i += 4)
6360 {
6361 aData[i] = pu8Data[i + 2];
6362 aData[i + 1] = pu8Data[i + 1];
6363 aData[i + 2] = pu8Data[i];
6364 aData[i + 3] = 0xff;
6365 }
6366 }
6367 else if (aBitmapFormat == BitmapFormat_PNG)
6368 {
6369 uint8_t *pu8PNG = NULL;
6370 uint32_t cbPNG = 0;
6371 uint32_t cxPNG = 0;
6372 uint32_t cyPNG = 0;
6373
6374 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6375
6376 if (RT_SUCCESS(vrc))
6377 {
6378 aData.resize(cbPNG);
6379 if (cbPNG)
6380 memcpy(&aData.front(), pu8PNG, cbPNG);
6381 }
6382 else
6383 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6384 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6385 vrc);
6386
6387 RTMemFree(pu8PNG);
6388 }
6389 }
6390
6391 freeSavedDisplayScreenshot(pu8Data);
6392
6393 return hr;
6394}
6395
6396HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6397 ULONG *aWidth,
6398 ULONG *aHeight,
6399 std::vector<BitmapFormat_T> &aBitmapFormats)
6400{
6401 if (aScreenId != 0)
6402 return E_NOTIMPL;
6403
6404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6405
6406 uint8_t *pu8Data = NULL;
6407 uint32_t cbData = 0;
6408 uint32_t u32Width = 0;
6409 uint32_t u32Height = 0;
6410
6411 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6412
6413 if (RT_FAILURE(vrc))
6414 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6415 tr("Saved screenshot data is not available (%Rrc)"),
6416 vrc);
6417
6418 *aWidth = u32Width;
6419 *aHeight = u32Height;
6420 aBitmapFormats.resize(1);
6421 aBitmapFormats[0] = BitmapFormat_PNG;
6422
6423 freeSavedDisplayScreenshot(pu8Data);
6424
6425 return S_OK;
6426}
6427
6428HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6429 BitmapFormat_T aBitmapFormat,
6430 ULONG *aWidth,
6431 ULONG *aHeight,
6432 std::vector<BYTE> &aData)
6433{
6434 if (aScreenId != 0)
6435 return E_NOTIMPL;
6436
6437 if (aBitmapFormat != BitmapFormat_PNG)
6438 return E_NOTIMPL;
6439
6440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6441
6442 uint8_t *pu8Data = NULL;
6443 uint32_t cbData = 0;
6444 uint32_t u32Width = 0;
6445 uint32_t u32Height = 0;
6446
6447 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6448
6449 if (RT_FAILURE(vrc))
6450 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6451 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6452 vrc);
6453
6454 *aWidth = u32Width;
6455 *aHeight = u32Height;
6456
6457 aData.resize(cbData);
6458 if (cbData)
6459 memcpy(&aData.front(), pu8Data, cbData);
6460
6461 freeSavedDisplayScreenshot(pu8Data);
6462
6463 return S_OK;
6464}
6465
6466HRESULT Machine::hotPlugCPU(ULONG aCpu)
6467{
6468 HRESULT rc = S_OK;
6469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6470
6471 if (!mHWData->mCPUHotPlugEnabled)
6472 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6473
6474 if (aCpu >= mHWData->mCPUCount)
6475 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6476
6477 if (mHWData->mCPUAttached[aCpu])
6478 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6479
6480 alock.release();
6481 rc = i_onCPUChange(aCpu, false);
6482 alock.acquire();
6483 if (FAILED(rc)) return rc;
6484
6485 i_setModified(IsModified_MachineData);
6486 mHWData.backup();
6487 mHWData->mCPUAttached[aCpu] = true;
6488
6489 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6490 if (Global::IsOnline(mData->mMachineState))
6491 i_saveSettings(NULL);
6492
6493 return S_OK;
6494}
6495
6496HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6497{
6498 HRESULT rc = S_OK;
6499
6500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6501
6502 if (!mHWData->mCPUHotPlugEnabled)
6503 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6504
6505 if (aCpu >= SchemaDefs::MaxCPUCount)
6506 return setError(E_INVALIDARG,
6507 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6508 SchemaDefs::MaxCPUCount);
6509
6510 if (!mHWData->mCPUAttached[aCpu])
6511 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6512
6513 /* CPU 0 can't be detached */
6514 if (aCpu == 0)
6515 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6516
6517 alock.release();
6518 rc = i_onCPUChange(aCpu, true);
6519 alock.acquire();
6520 if (FAILED(rc)) return rc;
6521
6522 i_setModified(IsModified_MachineData);
6523 mHWData.backup();
6524 mHWData->mCPUAttached[aCpu] = false;
6525
6526 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6527 if (Global::IsOnline(mData->mMachineState))
6528 i_saveSettings(NULL);
6529
6530 return S_OK;
6531}
6532
6533HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6534{
6535 *aAttached = false;
6536
6537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6538
6539 /* If hotplug is enabled the CPU is always enabled. */
6540 if (!mHWData->mCPUHotPlugEnabled)
6541 {
6542 if (aCpu < mHWData->mCPUCount)
6543 *aAttached = true;
6544 }
6545 else
6546 {
6547 if (aCpu < SchemaDefs::MaxCPUCount)
6548 *aAttached = mHWData->mCPUAttached[aCpu];
6549 }
6550
6551 return S_OK;
6552}
6553
6554HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6555{
6556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 Utf8Str log = i_getLogFilename(aIdx);
6559 if (!RTFileExists(log.c_str()))
6560 log.setNull();
6561 aFilename = log;
6562
6563 return S_OK;
6564}
6565
6566HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6567{
6568 if (aSize < 0)
6569 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6570
6571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6572
6573 HRESULT rc = S_OK;
6574 Utf8Str log = i_getLogFilename(aIdx);
6575
6576 /* do not unnecessarily hold the lock while doing something which does
6577 * not need the lock and potentially takes a long time. */
6578 alock.release();
6579
6580 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6581 * keeps the SOAP reply size under 1M for the webservice (we're using
6582 * base64 encoded strings for binary data for years now, avoiding the
6583 * expansion of each byte array element to approx. 25 bytes of XML. */
6584 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6585 aData.resize(cbData);
6586
6587 RTFILE LogFile;
6588 int vrc = RTFileOpen(&LogFile, log.c_str(),
6589 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6590 if (RT_SUCCESS(vrc))
6591 {
6592 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6593 if (RT_SUCCESS(vrc))
6594 aData.resize(cbData);
6595 else
6596 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6597 tr("Could not read log file '%s' (%Rrc)"),
6598 log.c_str(), vrc);
6599 RTFileClose(LogFile);
6600 }
6601 else
6602 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6603 tr("Could not open log file '%s' (%Rrc)"),
6604 log.c_str(), vrc);
6605
6606 if (FAILED(rc))
6607 aData.resize(0);
6608
6609 return rc;
6610}
6611
6612
6613/**
6614 * Currently this method doesn't attach device to the running VM,
6615 * just makes sure it's plugged on next VM start.
6616 */
6617HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6618{
6619 // lock scope
6620 {
6621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6622
6623 HRESULT rc = i_checkStateDependency(MutableStateDep);
6624 if (FAILED(rc)) return rc;
6625
6626 ChipsetType_T aChipset = ChipsetType_PIIX3;
6627 COMGETTER(ChipsetType)(&aChipset);
6628
6629 if (aChipset != ChipsetType_ICH9)
6630 {
6631 return setError(E_INVALIDARG,
6632 tr("Host PCI attachment only supported with ICH9 chipset"));
6633 }
6634
6635 // check if device with this host PCI address already attached
6636 for (HWData::PCIDeviceAssignmentList::const_iterator
6637 it = mHWData->mPCIDeviceAssignments.begin();
6638 it != mHWData->mPCIDeviceAssignments.end();
6639 ++it)
6640 {
6641 LONG iHostAddress = -1;
6642 ComPtr<PCIDeviceAttachment> pAttach;
6643 pAttach = *it;
6644 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6645 if (iHostAddress == aHostAddress)
6646 return setError(E_INVALIDARG,
6647 tr("Device with host PCI address already attached to this VM"));
6648 }
6649
6650 ComObjPtr<PCIDeviceAttachment> pda;
6651 char name[32];
6652
6653 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6654 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6655 pda.createObject();
6656 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6657 i_setModified(IsModified_MachineData);
6658 mHWData.backup();
6659 mHWData->mPCIDeviceAssignments.push_back(pda);
6660 }
6661
6662 return S_OK;
6663}
6664
6665/**
6666 * Currently this method doesn't detach device from the running VM,
6667 * just makes sure it's not plugged on next VM start.
6668 */
6669HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6670{
6671 ComObjPtr<PCIDeviceAttachment> pAttach;
6672 bool fRemoved = false;
6673 HRESULT rc;
6674
6675 // lock scope
6676 {
6677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6678
6679 rc = i_checkStateDependency(MutableStateDep);
6680 if (FAILED(rc)) return rc;
6681
6682 for (HWData::PCIDeviceAssignmentList::const_iterator
6683 it = mHWData->mPCIDeviceAssignments.begin();
6684 it != mHWData->mPCIDeviceAssignments.end();
6685 ++it)
6686 {
6687 LONG iHostAddress = -1;
6688 pAttach = *it;
6689 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6690 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6691 {
6692 i_setModified(IsModified_MachineData);
6693 mHWData.backup();
6694 mHWData->mPCIDeviceAssignments.remove(pAttach);
6695 fRemoved = true;
6696 break;
6697 }
6698 }
6699 }
6700
6701
6702 /* Fire event outside of the lock */
6703 if (fRemoved)
6704 {
6705 Assert(!pAttach.isNull());
6706 ComPtr<IEventSource> es;
6707 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6708 Assert(SUCCEEDED(rc));
6709 Bstr mid;
6710 rc = this->COMGETTER(Id)(mid.asOutParam());
6711 Assert(SUCCEEDED(rc));
6712 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6713 }
6714
6715 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6716 tr("No host PCI device %08x attached"),
6717 aHostAddress
6718 );
6719}
6720
6721HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6722{
6723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6724
6725 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6726 size_t i = 0;
6727 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6728 it = mHWData->mPCIDeviceAssignments.begin();
6729 it != mHWData->mPCIDeviceAssignments.end();
6730 ++it, ++i)
6731 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6732
6733 return S_OK;
6734}
6735
6736HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6737{
6738 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6739
6740 return S_OK;
6741}
6742
6743HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6744{
6745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6746
6747 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6748
6749 return S_OK;
6750}
6751
6752HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6753{
6754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6755 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6756 if (SUCCEEDED(hrc))
6757 {
6758 hrc = mHWData.backupEx();
6759 if (SUCCEEDED(hrc))
6760 {
6761 i_setModified(IsModified_MachineData);
6762 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6763 }
6764 }
6765 return hrc;
6766}
6767
6768HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6769{
6770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6771 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6772 return S_OK;
6773}
6774
6775HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6776{
6777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6778 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6779 if (SUCCEEDED(hrc))
6780 {
6781 hrc = mHWData.backupEx();
6782 if (SUCCEEDED(hrc))
6783 {
6784 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6785 if (SUCCEEDED(hrc))
6786 i_setModified(IsModified_MachineData);
6787 }
6788 }
6789 return hrc;
6790}
6791
6792HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6793{
6794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6795
6796 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6797
6798 return S_OK;
6799}
6800
6801HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6802{
6803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6804 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6805 if (SUCCEEDED(hrc))
6806 {
6807 hrc = mHWData.backupEx();
6808 if (SUCCEEDED(hrc))
6809 {
6810 i_setModified(IsModified_MachineData);
6811 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6812 }
6813 }
6814 return hrc;
6815}
6816
6817HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6818{
6819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6820
6821 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6822
6823 return S_OK;
6824}
6825
6826HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6827{
6828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6831 if ( SUCCEEDED(hrc)
6832 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6833 {
6834 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6835 int vrc;
6836
6837 if (aAutostartEnabled)
6838 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6839 else
6840 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6841
6842 if (RT_SUCCESS(vrc))
6843 {
6844 hrc = mHWData.backupEx();
6845 if (SUCCEEDED(hrc))
6846 {
6847 i_setModified(IsModified_MachineData);
6848 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6849 }
6850 }
6851 else if (vrc == VERR_NOT_SUPPORTED)
6852 hrc = setError(VBOX_E_NOT_SUPPORTED,
6853 tr("The VM autostart feature is not supported on this platform"));
6854 else if (vrc == VERR_PATH_NOT_FOUND)
6855 hrc = setError(E_FAIL,
6856 tr("The path to the autostart database is not set"));
6857 else
6858 hrc = setError(E_UNEXPECTED,
6859 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6860 aAutostartEnabled ? "Adding" : "Removing",
6861 mUserData->s.strName.c_str(), vrc);
6862 }
6863 return hrc;
6864}
6865
6866HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6867{
6868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6869
6870 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6871
6872 return S_OK;
6873}
6874
6875HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6876{
6877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6878 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6879 if (SUCCEEDED(hrc))
6880 {
6881 hrc = mHWData.backupEx();
6882 if (SUCCEEDED(hrc))
6883 {
6884 i_setModified(IsModified_MachineData);
6885 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6886 }
6887 }
6888 return hrc;
6889}
6890
6891HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6892{
6893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6894
6895 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6896
6897 return S_OK;
6898}
6899
6900HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6901{
6902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6903 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6904 if ( SUCCEEDED(hrc)
6905 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6906 {
6907 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6908 int vrc;
6909
6910 if (aAutostopType != AutostopType_Disabled)
6911 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6912 else
6913 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6914
6915 if (RT_SUCCESS(vrc))
6916 {
6917 hrc = mHWData.backupEx();
6918 if (SUCCEEDED(hrc))
6919 {
6920 i_setModified(IsModified_MachineData);
6921 mHWData->mAutostart.enmAutostopType = aAutostopType;
6922 }
6923 }
6924 else if (vrc == VERR_NOT_SUPPORTED)
6925 hrc = setError(VBOX_E_NOT_SUPPORTED,
6926 tr("The VM autostop feature is not supported on this platform"));
6927 else if (vrc == VERR_PATH_NOT_FOUND)
6928 hrc = setError(E_FAIL,
6929 tr("The path to the autostart database is not set"));
6930 else
6931 hrc = setError(E_UNEXPECTED,
6932 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6933 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6934 mUserData->s.strName.c_str(), vrc);
6935 }
6936 return hrc;
6937}
6938
6939HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6940{
6941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6942
6943 aDefaultFrontend = mHWData->mDefaultFrontend;
6944
6945 return S_OK;
6946}
6947
6948HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6949{
6950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6951 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6952 if (SUCCEEDED(hrc))
6953 {
6954 hrc = mHWData.backupEx();
6955 if (SUCCEEDED(hrc))
6956 {
6957 i_setModified(IsModified_MachineData);
6958 mHWData->mDefaultFrontend = aDefaultFrontend;
6959 }
6960 }
6961 return hrc;
6962}
6963
6964HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6965{
6966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6967 size_t cbIcon = mUserData->s.ovIcon.size();
6968 aIcon.resize(cbIcon);
6969 if (cbIcon)
6970 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6971 return S_OK;
6972}
6973
6974HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6975{
6976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6977 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6978 if (SUCCEEDED(hrc))
6979 {
6980 i_setModified(IsModified_MachineData);
6981 mUserData.backup();
6982 size_t cbIcon = aIcon.size();
6983 mUserData->s.ovIcon.resize(cbIcon);
6984 if (cbIcon)
6985 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6986 }
6987 return hrc;
6988}
6989
6990HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6991{
6992#ifdef VBOX_WITH_USB
6993 *aUSBProxyAvailable = true;
6994#else
6995 *aUSBProxyAvailable = false;
6996#endif
6997 return S_OK;
6998}
6999
7000HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7001{
7002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7003
7004 aVMProcessPriority = mUserData->s.strVMPriority;
7005
7006 return S_OK;
7007}
7008
7009HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7010{
7011 RT_NOREF(aVMProcessPriority);
7012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7013 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7014 if (SUCCEEDED(hrc))
7015 {
7016 /** @todo r=klaus: currently this is marked as not implemented, as
7017 * the code for setting the priority of the process is not there
7018 * (neither when starting the VM nor at runtime). */
7019 ReturnComNotImplemented();
7020#if 0
7021 hrc = mUserData.backupEx();
7022 if (SUCCEEDED(hrc))
7023 {
7024 i_setModified(IsModified_MachineData);
7025 mUserData->s.strVMPriority = aVMProcessPriority;
7026 }
7027#endif
7028 }
7029 return hrc;
7030}
7031
7032HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7033 ComPtr<IProgress> &aProgress)
7034{
7035 ComObjPtr<Progress> pP;
7036 Progress *ppP = pP;
7037 IProgress *iP = static_cast<IProgress *>(ppP);
7038 IProgress **pProgress = &iP;
7039
7040 IMachine *pTarget = aTarget;
7041
7042 /* Convert the options. */
7043 RTCList<CloneOptions_T> optList;
7044 if (aOptions.size())
7045 for (size_t i = 0; i < aOptions.size(); ++i)
7046 optList.append(aOptions[i]);
7047
7048 if (optList.contains(CloneOptions_Link))
7049 {
7050 if (!i_isSnapshotMachine())
7051 return setError(E_INVALIDARG,
7052 tr("Linked clone can only be created from a snapshot"));
7053 if (aMode != CloneMode_MachineState)
7054 return setError(E_INVALIDARG,
7055 tr("Linked clone can only be created for a single machine state"));
7056 }
7057 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7058
7059 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7060
7061 HRESULT rc = pWorker->start(pProgress);
7062
7063 pP = static_cast<Progress *>(*pProgress);
7064 pP.queryInterfaceTo(aProgress.asOutParam());
7065
7066 return rc;
7067
7068}
7069
7070HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7071 const com::Utf8Str &aType,
7072 ComPtr<IProgress> &aProgress)
7073{
7074 LogFlowThisFuncEnter();
7075
7076 ComObjPtr<Progress> progress;
7077
7078 progress.createObject();
7079
7080 HRESULT rc = S_OK;
7081 Utf8Str targetPath = aTargetPath;
7082 Utf8Str type = aType;
7083
7084 /* Initialize our worker task */
7085 MachineMoveVM* task = NULL;
7086 try
7087 {
7088 task = new MachineMoveVM(this, targetPath, type, progress);
7089 }
7090 catch(...)
7091 {
7092 delete task;
7093 return rc;
7094 }
7095
7096 /*
7097 * task pointer will be owned by the ThreadTask class.
7098 * There is no need to call operator "delete" in the end.
7099 */
7100 rc = task->init();
7101 if (SUCCEEDED(rc))
7102 {
7103 rc = task->createThread();
7104 if (FAILED(rc))
7105 {
7106 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7107 }
7108
7109 /* Return progress to the caller */
7110 progress.queryInterfaceTo(aProgress.asOutParam());
7111 }
7112
7113 LogFlowThisFuncLeave();
7114 return rc;
7115
7116}
7117
7118HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7119{
7120 NOREF(aProgress);
7121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7122
7123 // This check should always fail.
7124 HRESULT rc = i_checkStateDependency(MutableStateDep);
7125 if (FAILED(rc)) return rc;
7126
7127 AssertFailedReturn(E_NOTIMPL);
7128}
7129
7130HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7131{
7132 NOREF(aSavedStateFile);
7133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7134
7135 // This check should always fail.
7136 HRESULT rc = i_checkStateDependency(MutableStateDep);
7137 if (FAILED(rc)) return rc;
7138
7139 AssertFailedReturn(E_NOTIMPL);
7140}
7141
7142HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7143{
7144 NOREF(aFRemoveFile);
7145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7146
7147 // This check should always fail.
7148 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7149 if (FAILED(rc)) return rc;
7150
7151 AssertFailedReturn(E_NOTIMPL);
7152}
7153
7154// public methods for internal purposes
7155/////////////////////////////////////////////////////////////////////////////
7156
7157/**
7158 * Adds the given IsModified_* flag to the dirty flags of the machine.
7159 * This must be called either during i_loadSettings or under the machine write lock.
7160 * @param fl Flag
7161 * @param fAllowStateModification If state modifications are allowed.
7162 */
7163void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7164{
7165 mData->flModifications |= fl;
7166 if (fAllowStateModification && i_isStateModificationAllowed())
7167 mData->mCurrentStateModified = true;
7168}
7169
7170/**
7171 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7172 * care of the write locking.
7173 *
7174 * @param fModification The flag to add.
7175 * @param fAllowStateModification If state modifications are allowed.
7176 */
7177void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7178{
7179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7180 i_setModified(fModification, fAllowStateModification);
7181}
7182
7183/**
7184 * Saves the registry entry of this machine to the given configuration node.
7185 *
7186 * @param data Machine registry data.
7187 *
7188 * @note locks this object for reading.
7189 */
7190HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7191{
7192 AutoLimitedCaller autoCaller(this);
7193 AssertComRCReturnRC(autoCaller.rc());
7194
7195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7196
7197 data.uuid = mData->mUuid;
7198 data.strSettingsFile = mData->m_strConfigFile;
7199
7200 return S_OK;
7201}
7202
7203/**
7204 * Calculates the absolute path of the given path taking the directory of the
7205 * machine settings file as the current directory.
7206 *
7207 * @param strPath Path to calculate the absolute path for.
7208 * @param aResult Where to put the result (used only on success, can be the
7209 * same Utf8Str instance as passed in @a aPath).
7210 * @return IPRT result.
7211 *
7212 * @note Locks this object for reading.
7213 */
7214int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7215{
7216 AutoCaller autoCaller(this);
7217 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7218
7219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7220
7221 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7222
7223 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7224
7225 strSettingsDir.stripFilename();
7226 char folder[RTPATH_MAX];
7227 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7228 if (RT_SUCCESS(vrc))
7229 aResult = folder;
7230
7231 return vrc;
7232}
7233
7234/**
7235 * Copies strSource to strTarget, making it relative to the machine folder
7236 * if it is a subdirectory thereof, or simply copying it otherwise.
7237 *
7238 * @param strSource Path to evaluate and copy.
7239 * @param strTarget Buffer to receive target path.
7240 *
7241 * @note Locks this object for reading.
7242 */
7243void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7244 Utf8Str &strTarget)
7245{
7246 AutoCaller autoCaller(this);
7247 AssertComRCReturn(autoCaller.rc(), (void)0);
7248
7249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7250
7251 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7252 // use strTarget as a temporary buffer to hold the machine settings dir
7253 strTarget = mData->m_strConfigFileFull;
7254 strTarget.stripFilename();
7255 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7256 {
7257 // is relative: then append what's left
7258 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7259 // for empty paths (only possible for subdirs) use "." to avoid
7260 // triggering default settings for not present config attributes.
7261 if (strTarget.isEmpty())
7262 strTarget = ".";
7263 }
7264 else
7265 // is not relative: then overwrite
7266 strTarget = strSource;
7267}
7268
7269/**
7270 * Returns the full path to the machine's log folder in the
7271 * \a aLogFolder argument.
7272 */
7273void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7274{
7275 AutoCaller autoCaller(this);
7276 AssertComRCReturnVoid(autoCaller.rc());
7277
7278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7279
7280 char szTmp[RTPATH_MAX];
7281 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7282 if (RT_SUCCESS(vrc))
7283 {
7284 if (szTmp[0] && !mUserData.isNull())
7285 {
7286 char szTmp2[RTPATH_MAX];
7287 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7288 if (RT_SUCCESS(vrc))
7289 aLogFolder = Utf8StrFmt("%s%c%s",
7290 szTmp2,
7291 RTPATH_DELIMITER,
7292 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7293 }
7294 else
7295 vrc = VERR_PATH_IS_RELATIVE;
7296 }
7297
7298 if (RT_FAILURE(vrc))
7299 {
7300 // fallback if VBOX_USER_LOGHOME is not set or invalid
7301 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7302 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7303 aLogFolder.append(RTPATH_DELIMITER);
7304 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7305 }
7306}
7307
7308/**
7309 * Returns the full path to the machine's log file for an given index.
7310 */
7311Utf8Str Machine::i_getLogFilename(ULONG idx)
7312{
7313 Utf8Str logFolder;
7314 getLogFolder(logFolder);
7315 Assert(logFolder.length());
7316
7317 Utf8Str log;
7318 if (idx == 0)
7319 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7320#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7321 else if (idx == 1)
7322 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7323 else
7324 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7325#else
7326 else
7327 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7328#endif
7329 return log;
7330}
7331
7332/**
7333 * Returns the full path to the machine's hardened log file.
7334 */
7335Utf8Str Machine::i_getHardeningLogFilename(void)
7336{
7337 Utf8Str strFilename;
7338 getLogFolder(strFilename);
7339 Assert(strFilename.length());
7340 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7341 return strFilename;
7342}
7343
7344
7345/**
7346 * Composes a unique saved state filename based on the current system time. The filename is
7347 * granular to the second so this will work so long as no more than one snapshot is taken on
7348 * a machine per second.
7349 *
7350 * Before version 4.1, we used this formula for saved state files:
7351 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7352 * which no longer works because saved state files can now be shared between the saved state of the
7353 * "saved" machine and an online snapshot, and the following would cause problems:
7354 * 1) save machine
7355 * 2) create online snapshot from that machine state --> reusing saved state file
7356 * 3) save machine again --> filename would be reused, breaking the online snapshot
7357 *
7358 * So instead we now use a timestamp.
7359 *
7360 * @param strStateFilePath
7361 */
7362
7363void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7364{
7365 AutoCaller autoCaller(this);
7366 AssertComRCReturnVoid(autoCaller.rc());
7367
7368 {
7369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7370 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7371 }
7372
7373 RTTIMESPEC ts;
7374 RTTimeNow(&ts);
7375 RTTIME time;
7376 RTTimeExplode(&time, &ts);
7377
7378 strStateFilePath += RTPATH_DELIMITER;
7379 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7380 time.i32Year, time.u8Month, time.u8MonthDay,
7381 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7382}
7383
7384/**
7385 * Returns whether at least one USB controller is present for the VM.
7386 */
7387bool Machine::i_isUSBControllerPresent()
7388{
7389 AutoCaller autoCaller(this);
7390 AssertComRCReturn(autoCaller.rc(), false);
7391
7392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7393
7394 return (mUSBControllers->size() > 0);
7395}
7396
7397/**
7398 * @note Locks this object for writing, calls the client process
7399 * (inside the lock).
7400 */
7401HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7402 const Utf8Str &strFrontend,
7403 const Utf8Str &strEnvironment,
7404 ProgressProxy *aProgress)
7405{
7406 LogFlowThisFuncEnter();
7407
7408 AssertReturn(aControl, E_FAIL);
7409 AssertReturn(aProgress, E_FAIL);
7410 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7411
7412 AutoCaller autoCaller(this);
7413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7414
7415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7416
7417 if (!mData->mRegistered)
7418 return setError(E_UNEXPECTED,
7419 tr("The machine '%s' is not registered"),
7420 mUserData->s.strName.c_str());
7421
7422 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7423
7424 /* The process started when launching a VM with separate UI/VM processes is always
7425 * the UI process, i.e. needs special handling as it won't claim the session. */
7426 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7427
7428 if (fSeparate)
7429 {
7430 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7431 return setError(VBOX_E_INVALID_OBJECT_STATE,
7432 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7433 mUserData->s.strName.c_str());
7434 }
7435 else
7436 {
7437 if ( mData->mSession.mState == SessionState_Locked
7438 || mData->mSession.mState == SessionState_Spawning
7439 || mData->mSession.mState == SessionState_Unlocking)
7440 return setError(VBOX_E_INVALID_OBJECT_STATE,
7441 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7442 mUserData->s.strName.c_str());
7443
7444 /* may not be busy */
7445 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7446 }
7447
7448 /* get the path to the executable */
7449 char szPath[RTPATH_MAX];
7450 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7451 size_t cchBufLeft = strlen(szPath);
7452 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7453 szPath[cchBufLeft] = 0;
7454 char *pszNamePart = szPath + cchBufLeft;
7455 cchBufLeft = sizeof(szPath) - cchBufLeft;
7456
7457 int vrc = VINF_SUCCESS;
7458 RTPROCESS pid = NIL_RTPROCESS;
7459
7460 RTENV env = RTENV_DEFAULT;
7461
7462 if (!strEnvironment.isEmpty())
7463 {
7464 char *newEnvStr = NULL;
7465
7466 do
7467 {
7468 /* clone the current environment */
7469 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7470 AssertRCBreakStmt(vrc2, vrc = vrc2);
7471
7472 newEnvStr = RTStrDup(strEnvironment.c_str());
7473 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7474
7475 /* put new variables to the environment
7476 * (ignore empty variable names here since RTEnv API
7477 * intentionally doesn't do that) */
7478 char *var = newEnvStr;
7479 for (char *p = newEnvStr; *p; ++p)
7480 {
7481 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7482 {
7483 *p = '\0';
7484 if (*var)
7485 {
7486 char *val = strchr(var, '=');
7487 if (val)
7488 {
7489 *val++ = '\0';
7490 vrc2 = RTEnvSetEx(env, var, val);
7491 }
7492 else
7493 vrc2 = RTEnvUnsetEx(env, var);
7494 if (RT_FAILURE(vrc2))
7495 break;
7496 }
7497 var = p + 1;
7498 }
7499 }
7500 if (RT_SUCCESS(vrc2) && *var)
7501 vrc2 = RTEnvPutEx(env, var);
7502
7503 AssertRCBreakStmt(vrc2, vrc = vrc2);
7504 }
7505 while (0);
7506
7507 if (newEnvStr != NULL)
7508 RTStrFree(newEnvStr);
7509 }
7510
7511 /* Hardening logging */
7512#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7513 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7514 {
7515 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7516 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7517 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7518 {
7519 Utf8Str strStartupLogDir = strHardeningLogFile;
7520 strStartupLogDir.stripFilename();
7521 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7522 file without stripping the file. */
7523 }
7524 strSupHardeningLogArg.append(strHardeningLogFile);
7525
7526 /* Remove legacy log filename to avoid confusion. */
7527 Utf8Str strOldStartupLogFile;
7528 getLogFolder(strOldStartupLogFile);
7529 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7530 RTFileDelete(strOldStartupLogFile.c_str());
7531 }
7532 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7533#else
7534 const char *pszSupHardeningLogArg = NULL;
7535#endif
7536
7537 Utf8Str strCanonicalName;
7538
7539#ifdef VBOX_WITH_QTGUI
7540 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7541 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7542 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7543 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7544 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7545 {
7546 strCanonicalName = "GUI/Qt";
7547# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7548 /* Modify the base path so that we don't need to use ".." below. */
7549 RTPathStripTrailingSlash(szPath);
7550 RTPathStripFilename(szPath);
7551 cchBufLeft = strlen(szPath);
7552 pszNamePart = szPath + cchBufLeft;
7553 cchBufLeft = sizeof(szPath) - cchBufLeft;
7554
7555# define OSX_APP_NAME "VirtualBoxVM"
7556# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7557
7558 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7559 if ( strAppOverride.contains(".")
7560 || strAppOverride.contains("/")
7561 || strAppOverride.contains("\\")
7562 || strAppOverride.contains(":"))
7563 strAppOverride.setNull();
7564 Utf8Str strAppPath;
7565 if (!strAppOverride.isEmpty())
7566 {
7567 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7568 Utf8Str strFullPath(szPath);
7569 strFullPath.append(strAppPath);
7570 /* there is a race, but people using this deserve the failure */
7571 if (!RTFileExists(strFullPath.c_str()))
7572 strAppOverride.setNull();
7573 }
7574 if (strAppOverride.isEmpty())
7575 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7576 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7577 strcpy(pszNamePart, strAppPath.c_str());
7578# else
7579# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7580 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7581# else
7582 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7583# endif
7584 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7585 strcpy(pszNamePart, s_szVirtualBox_exe);
7586# endif
7587
7588 Utf8Str idStr = mData->mUuid.toString();
7589 const char *apszArgs[] =
7590 {
7591 szPath,
7592 "--comment", mUserData->s.strName.c_str(),
7593 "--startvm", idStr.c_str(),
7594 "--no-startvm-errormsgbox",
7595 NULL, /* For "--separate". */
7596 NULL, /* For "--sup-startup-log". */
7597 NULL
7598 };
7599 unsigned iArg = 6;
7600 if (fSeparate)
7601 apszArgs[iArg++] = "--separate";
7602 apszArgs[iArg++] = pszSupHardeningLogArg;
7603
7604 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7605 }
7606#else /* !VBOX_WITH_QTGUI */
7607 if (0)
7608 ;
7609#endif /* VBOX_WITH_QTGUI */
7610
7611 else
7612
7613#ifdef VBOX_WITH_VBOXSDL
7614 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7615 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7616 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7617 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7618 {
7619 strCanonicalName = "GUI/SDL";
7620 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7621 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7622 strcpy(pszNamePart, s_szVBoxSDL_exe);
7623
7624 Utf8Str idStr = mData->mUuid.toString();
7625 const char *apszArgs[] =
7626 {
7627 szPath,
7628 "--comment", mUserData->s.strName.c_str(),
7629 "--startvm", idStr.c_str(),
7630 NULL, /* For "--separate". */
7631 NULL, /* For "--sup-startup-log". */
7632 NULL
7633 };
7634 unsigned iArg = 5;
7635 if (fSeparate)
7636 apszArgs[iArg++] = "--separate";
7637 apszArgs[iArg++] = pszSupHardeningLogArg;
7638
7639 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7640 }
7641#else /* !VBOX_WITH_VBOXSDL */
7642 if (0)
7643 ;
7644#endif /* !VBOX_WITH_VBOXSDL */
7645
7646 else
7647
7648#ifdef VBOX_WITH_HEADLESS
7649 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7650 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7651 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7652 )
7653 {
7654 strCanonicalName = "headless";
7655 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7656 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7657 * and a VM works even if the server has not been installed.
7658 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7659 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7660 * differently in 4.0 and 3.x.
7661 */
7662 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7663 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7664 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7665
7666 Utf8Str idStr = mData->mUuid.toString();
7667 const char *apszArgs[] =
7668 {
7669 szPath,
7670 "--comment", mUserData->s.strName.c_str(),
7671 "--startvm", idStr.c_str(),
7672 "--vrde", "config",
7673 NULL, /* For "--capture". */
7674 NULL, /* For "--sup-startup-log". */
7675 NULL
7676 };
7677 unsigned iArg = 7;
7678 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7679 apszArgs[iArg++] = "--capture";
7680 apszArgs[iArg++] = pszSupHardeningLogArg;
7681
7682# ifdef RT_OS_WINDOWS
7683 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7684# else
7685 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7686# endif
7687 }
7688#else /* !VBOX_WITH_HEADLESS */
7689 if (0)
7690 ;
7691#endif /* !VBOX_WITH_HEADLESS */
7692 else
7693 {
7694 RTEnvDestroy(env);
7695 return setError(E_INVALIDARG,
7696 tr("Invalid frontend name: '%s'"),
7697 strFrontend.c_str());
7698 }
7699
7700 RTEnvDestroy(env);
7701
7702 if (RT_FAILURE(vrc))
7703 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7704 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7705 mUserData->s.strName.c_str(), vrc);
7706
7707 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7708
7709 if (!fSeparate)
7710 {
7711 /*
7712 * Note that we don't release the lock here before calling the client,
7713 * because it doesn't need to call us back if called with a NULL argument.
7714 * Releasing the lock here is dangerous because we didn't prepare the
7715 * launch data yet, but the client we've just started may happen to be
7716 * too fast and call LockMachine() that will fail (because of PID, etc.),
7717 * so that the Machine will never get out of the Spawning session state.
7718 */
7719
7720 /* inform the session that it will be a remote one */
7721 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7722#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7723 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7724#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7725 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7726#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7727 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7728
7729 if (FAILED(rc))
7730 {
7731 /* restore the session state */
7732 mData->mSession.mState = SessionState_Unlocked;
7733 alock.release();
7734 mParent->i_addProcessToReap(pid);
7735 /* The failure may occur w/o any error info (from RPC), so provide one */
7736 return setError(VBOX_E_VM_ERROR,
7737 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7738 }
7739
7740 /* attach launch data to the machine */
7741 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7742 mData->mSession.mRemoteControls.push_back(aControl);
7743 mData->mSession.mProgress = aProgress;
7744 mData->mSession.mPID = pid;
7745 mData->mSession.mState = SessionState_Spawning;
7746 Assert(strCanonicalName.isNotEmpty());
7747 mData->mSession.mName = strCanonicalName;
7748 }
7749 else
7750 {
7751 /* For separate UI process we declare the launch as completed instantly, as the
7752 * actual headless VM start may or may not come. No point in remembering anything
7753 * yet, as what matters for us is when the headless VM gets started. */
7754 aProgress->i_notifyComplete(S_OK);
7755 }
7756
7757 alock.release();
7758 mParent->i_addProcessToReap(pid);
7759
7760 LogFlowThisFuncLeave();
7761 return S_OK;
7762}
7763
7764/**
7765 * Returns @c true if the given session machine instance has an open direct
7766 * session (and optionally also for direct sessions which are closing) and
7767 * returns the session control machine instance if so.
7768 *
7769 * Note that when the method returns @c false, the arguments remain unchanged.
7770 *
7771 * @param aMachine Session machine object.
7772 * @param aControl Direct session control object (optional).
7773 * @param aRequireVM If true then only allow VM sessions.
7774 * @param aAllowClosing If true then additionally a session which is currently
7775 * being closed will also be allowed.
7776 *
7777 * @note locks this object for reading.
7778 */
7779bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7780 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7781 bool aRequireVM /*= false*/,
7782 bool aAllowClosing /*= false*/)
7783{
7784 AutoLimitedCaller autoCaller(this);
7785 AssertComRCReturn(autoCaller.rc(), false);
7786
7787 /* just return false for inaccessible machines */
7788 if (getObjectState().getState() != ObjectState::Ready)
7789 return false;
7790
7791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7792
7793 if ( ( mData->mSession.mState == SessionState_Locked
7794 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7795 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7796 )
7797 {
7798 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7799
7800 aMachine = mData->mSession.mMachine;
7801
7802 if (aControl != NULL)
7803 *aControl = mData->mSession.mDirectControl;
7804
7805 return true;
7806 }
7807
7808 return false;
7809}
7810
7811/**
7812 * Returns @c true if the given machine has an spawning direct session.
7813 *
7814 * @note locks this object for reading.
7815 */
7816bool Machine::i_isSessionSpawning()
7817{
7818 AutoLimitedCaller autoCaller(this);
7819 AssertComRCReturn(autoCaller.rc(), false);
7820
7821 /* just return false for inaccessible machines */
7822 if (getObjectState().getState() != ObjectState::Ready)
7823 return false;
7824
7825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7826
7827 if (mData->mSession.mState == SessionState_Spawning)
7828 return true;
7829
7830 return false;
7831}
7832
7833/**
7834 * Called from the client watcher thread to check for unexpected client process
7835 * death during Session_Spawning state (e.g. before it successfully opened a
7836 * direct session).
7837 *
7838 * On Win32 and on OS/2, this method is called only when we've got the
7839 * direct client's process termination notification, so it always returns @c
7840 * true.
7841 *
7842 * On other platforms, this method returns @c true if the client process is
7843 * terminated and @c false if it's still alive.
7844 *
7845 * @note Locks this object for writing.
7846 */
7847bool Machine::i_checkForSpawnFailure()
7848{
7849 AutoCaller autoCaller(this);
7850 if (!autoCaller.isOk())
7851 {
7852 /* nothing to do */
7853 LogFlowThisFunc(("Already uninitialized!\n"));
7854 return true;
7855 }
7856
7857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7858
7859 if (mData->mSession.mState != SessionState_Spawning)
7860 {
7861 /* nothing to do */
7862 LogFlowThisFunc(("Not spawning any more!\n"));
7863 return true;
7864 }
7865
7866 HRESULT rc = S_OK;
7867
7868 /* PID not yet initialized, skip check. */
7869 if (mData->mSession.mPID == NIL_RTPROCESS)
7870 return false;
7871
7872 RTPROCSTATUS status;
7873 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7874
7875 if (vrc != VERR_PROCESS_RUNNING)
7876 {
7877 Utf8Str strExtraInfo;
7878
7879#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7880 /* If the startup logfile exists and is of non-zero length, tell the
7881 user to look there for more details to encourage them to attach it
7882 when reporting startup issues. */
7883 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7884 uint64_t cbStartupLogFile = 0;
7885 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7886 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7887 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7888#endif
7889
7890 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7891 rc = setError(E_FAIL,
7892 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7893 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7894 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7895 rc = setError(E_FAIL,
7896 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7897 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7898 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7899 rc = setError(E_FAIL,
7900 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7901 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7902 else
7903 rc = setErrorBoth(E_FAIL, vrc,
7904 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7905 i_getName().c_str(), vrc, strExtraInfo.c_str());
7906 }
7907
7908 if (FAILED(rc))
7909 {
7910 /* Close the remote session, remove the remote control from the list
7911 * and reset session state to Closed (@note keep the code in sync with
7912 * the relevant part in LockMachine()). */
7913
7914 Assert(mData->mSession.mRemoteControls.size() == 1);
7915 if (mData->mSession.mRemoteControls.size() == 1)
7916 {
7917 ErrorInfoKeeper eik;
7918 mData->mSession.mRemoteControls.front()->Uninitialize();
7919 }
7920
7921 mData->mSession.mRemoteControls.clear();
7922 mData->mSession.mState = SessionState_Unlocked;
7923
7924 /* finalize the progress after setting the state */
7925 if (!mData->mSession.mProgress.isNull())
7926 {
7927 mData->mSession.mProgress->notifyComplete(rc);
7928 mData->mSession.mProgress.setNull();
7929 }
7930
7931 mData->mSession.mPID = NIL_RTPROCESS;
7932
7933 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7934 return true;
7935 }
7936
7937 return false;
7938}
7939
7940/**
7941 * Checks whether the machine can be registered. If so, commits and saves
7942 * all settings.
7943 *
7944 * @note Must be called from mParent's write lock. Locks this object and
7945 * children for writing.
7946 */
7947HRESULT Machine::i_prepareRegister()
7948{
7949 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7950
7951 AutoLimitedCaller autoCaller(this);
7952 AssertComRCReturnRC(autoCaller.rc());
7953
7954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7955
7956 /* wait for state dependents to drop to zero */
7957 i_ensureNoStateDependencies();
7958
7959 if (!mData->mAccessible)
7960 return setError(VBOX_E_INVALID_OBJECT_STATE,
7961 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7962 mUserData->s.strName.c_str(),
7963 mData->mUuid.toString().c_str());
7964
7965 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7966
7967 if (mData->mRegistered)
7968 return setError(VBOX_E_INVALID_OBJECT_STATE,
7969 tr("The machine '%s' with UUID {%s} is already registered"),
7970 mUserData->s.strName.c_str(),
7971 mData->mUuid.toString().c_str());
7972
7973 HRESULT rc = S_OK;
7974
7975 // Ensure the settings are saved. If we are going to be registered and
7976 // no config file exists yet, create it by calling i_saveSettings() too.
7977 if ( (mData->flModifications)
7978 || (!mData->pMachineConfigFile->fileExists())
7979 )
7980 {
7981 rc = i_saveSettings(NULL);
7982 // no need to check whether VirtualBox.xml needs saving too since
7983 // we can't have a machine XML file rename pending
7984 if (FAILED(rc)) return rc;
7985 }
7986
7987 /* more config checking goes here */
7988
7989 if (SUCCEEDED(rc))
7990 {
7991 /* we may have had implicit modifications we want to fix on success */
7992 i_commit();
7993
7994 mData->mRegistered = true;
7995 }
7996 else
7997 {
7998 /* we may have had implicit modifications we want to cancel on failure*/
7999 i_rollback(false /* aNotify */);
8000 }
8001
8002 return rc;
8003}
8004
8005/**
8006 * Increases the number of objects dependent on the machine state or on the
8007 * registered state. Guarantees that these two states will not change at least
8008 * until #i_releaseStateDependency() is called.
8009 *
8010 * Depending on the @a aDepType value, additional state checks may be made.
8011 * These checks will set extended error info on failure. See
8012 * #i_checkStateDependency() for more info.
8013 *
8014 * If this method returns a failure, the dependency is not added and the caller
8015 * is not allowed to rely on any particular machine state or registration state
8016 * value and may return the failed result code to the upper level.
8017 *
8018 * @param aDepType Dependency type to add.
8019 * @param aState Current machine state (NULL if not interested).
8020 * @param aRegistered Current registered state (NULL if not interested).
8021 *
8022 * @note Locks this object for writing.
8023 */
8024HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8025 MachineState_T *aState /* = NULL */,
8026 BOOL *aRegistered /* = NULL */)
8027{
8028 AutoCaller autoCaller(this);
8029 AssertComRCReturnRC(autoCaller.rc());
8030
8031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8032
8033 HRESULT rc = i_checkStateDependency(aDepType);
8034 if (FAILED(rc)) return rc;
8035
8036 {
8037 if (mData->mMachineStateChangePending != 0)
8038 {
8039 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8040 * drop to zero so don't add more. It may make sense to wait a bit
8041 * and retry before reporting an error (since the pending state
8042 * transition should be really quick) but let's just assert for
8043 * now to see if it ever happens on practice. */
8044
8045 AssertFailed();
8046
8047 return setError(E_ACCESSDENIED,
8048 tr("Machine state change is in progress. Please retry the operation later."));
8049 }
8050
8051 ++mData->mMachineStateDeps;
8052 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8053 }
8054
8055 if (aState)
8056 *aState = mData->mMachineState;
8057 if (aRegistered)
8058 *aRegistered = mData->mRegistered;
8059
8060 return S_OK;
8061}
8062
8063/**
8064 * Decreases the number of objects dependent on the machine state.
8065 * Must always complete the #i_addStateDependency() call after the state
8066 * dependency is no more necessary.
8067 */
8068void Machine::i_releaseStateDependency()
8069{
8070 AutoCaller autoCaller(this);
8071 AssertComRCReturnVoid(autoCaller.rc());
8072
8073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8074
8075 /* releaseStateDependency() w/o addStateDependency()? */
8076 AssertReturnVoid(mData->mMachineStateDeps != 0);
8077 -- mData->mMachineStateDeps;
8078
8079 if (mData->mMachineStateDeps == 0)
8080 {
8081 /* inform i_ensureNoStateDependencies() that there are no more deps */
8082 if (mData->mMachineStateChangePending != 0)
8083 {
8084 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8085 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8086 }
8087 }
8088}
8089
8090Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8091{
8092 /* start with nothing found */
8093 Utf8Str strResult("");
8094
8095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8096
8097 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8098 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8099 // found:
8100 strResult = it->second; // source is a Utf8Str
8101
8102 return strResult;
8103}
8104
8105// protected methods
8106/////////////////////////////////////////////////////////////////////////////
8107
8108/**
8109 * Performs machine state checks based on the @a aDepType value. If a check
8110 * fails, this method will set extended error info, otherwise it will return
8111 * S_OK. It is supposed, that on failure, the caller will immediately return
8112 * the return value of this method to the upper level.
8113 *
8114 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8115 *
8116 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8117 * current state of this machine object allows to change settings of the
8118 * machine (i.e. the machine is not registered, or registered but not running
8119 * and not saved). It is useful to call this method from Machine setters
8120 * before performing any change.
8121 *
8122 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8123 * as for MutableStateDep except that if the machine is saved, S_OK is also
8124 * returned. This is useful in setters which allow changing machine
8125 * properties when it is in the saved state.
8126 *
8127 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8128 * if the current state of this machine object allows to change runtime
8129 * changeable settings of the machine (i.e. the machine is not registered, or
8130 * registered but either running or not running and not saved). It is useful
8131 * to call this method from Machine setters before performing any changes to
8132 * runtime changeable settings.
8133 *
8134 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8135 * the same as for MutableOrRunningStateDep except that if the machine is
8136 * saved, S_OK is also returned. This is useful in setters which allow
8137 * changing runtime and saved state changeable machine properties.
8138 *
8139 * @param aDepType Dependency type to check.
8140 *
8141 * @note Non Machine based classes should use #i_addStateDependency() and
8142 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8143 * template.
8144 *
8145 * @note This method must be called from under this object's read or write
8146 * lock.
8147 */
8148HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8149{
8150 switch (aDepType)
8151 {
8152 case AnyStateDep:
8153 {
8154 break;
8155 }
8156 case MutableStateDep:
8157 {
8158 if ( mData->mRegistered
8159 && ( !i_isSessionMachine()
8160 || ( mData->mMachineState != MachineState_Aborted
8161 && mData->mMachineState != MachineState_Teleported
8162 && mData->mMachineState != MachineState_PoweredOff
8163 )
8164 )
8165 )
8166 return setError(VBOX_E_INVALID_VM_STATE,
8167 tr("The machine is not mutable (state is %s)"),
8168 Global::stringifyMachineState(mData->mMachineState));
8169 break;
8170 }
8171 case MutableOrSavedStateDep:
8172 {
8173 if ( mData->mRegistered
8174 && ( !i_isSessionMachine()
8175 || ( mData->mMachineState != MachineState_Aborted
8176 && mData->mMachineState != MachineState_Teleported
8177 && mData->mMachineState != MachineState_Saved
8178 && mData->mMachineState != MachineState_PoweredOff
8179 )
8180 )
8181 )
8182 return setError(VBOX_E_INVALID_VM_STATE,
8183 tr("The machine is not mutable or saved (state is %s)"),
8184 Global::stringifyMachineState(mData->mMachineState));
8185 break;
8186 }
8187 case MutableOrRunningStateDep:
8188 {
8189 if ( mData->mRegistered
8190 && ( !i_isSessionMachine()
8191 || ( mData->mMachineState != MachineState_Aborted
8192 && mData->mMachineState != MachineState_Teleported
8193 && mData->mMachineState != MachineState_PoweredOff
8194 && !Global::IsOnline(mData->mMachineState)
8195 )
8196 )
8197 )
8198 return setError(VBOX_E_INVALID_VM_STATE,
8199 tr("The machine is not mutable or running (state is %s)"),
8200 Global::stringifyMachineState(mData->mMachineState));
8201 break;
8202 }
8203 case MutableOrSavedOrRunningStateDep:
8204 {
8205 if ( mData->mRegistered
8206 && ( !i_isSessionMachine()
8207 || ( mData->mMachineState != MachineState_Aborted
8208 && mData->mMachineState != MachineState_Teleported
8209 && mData->mMachineState != MachineState_Saved
8210 && mData->mMachineState != MachineState_PoweredOff
8211 && !Global::IsOnline(mData->mMachineState)
8212 )
8213 )
8214 )
8215 return setError(VBOX_E_INVALID_VM_STATE,
8216 tr("The machine is not mutable, saved or running (state is %s)"),
8217 Global::stringifyMachineState(mData->mMachineState));
8218 break;
8219 }
8220 }
8221
8222 return S_OK;
8223}
8224
8225/**
8226 * Helper to initialize all associated child objects and allocate data
8227 * structures.
8228 *
8229 * This method must be called as a part of the object's initialization procedure
8230 * (usually done in the #init() method).
8231 *
8232 * @note Must be called only from #init() or from #i_registeredInit().
8233 */
8234HRESULT Machine::initDataAndChildObjects()
8235{
8236 AutoCaller autoCaller(this);
8237 AssertComRCReturnRC(autoCaller.rc());
8238 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8239 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8240
8241 AssertReturn(!mData->mAccessible, E_FAIL);
8242
8243 /* allocate data structures */
8244 mSSData.allocate();
8245 mUserData.allocate();
8246 mHWData.allocate();
8247 mMediumAttachments.allocate();
8248 mStorageControllers.allocate();
8249 mUSBControllers.allocate();
8250
8251 /* initialize mOSTypeId */
8252 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8253
8254/** @todo r=bird: init() methods never fails, right? Why don't we make them
8255 * return void then! */
8256
8257 /* create associated BIOS settings object */
8258 unconst(mBIOSSettings).createObject();
8259 mBIOSSettings->init(this);
8260
8261 /* create associated capture settings object */
8262 unconst(mCaptureSettings).createObject();
8263 mCaptureSettings->init(this);
8264
8265 /* create an associated VRDE object (default is disabled) */
8266 unconst(mVRDEServer).createObject();
8267 mVRDEServer->init(this);
8268
8269 /* create associated serial port objects */
8270 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8271 {
8272 unconst(mSerialPorts[slot]).createObject();
8273 mSerialPorts[slot]->init(this, slot);
8274 }
8275
8276 /* create associated parallel port objects */
8277 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8278 {
8279 unconst(mParallelPorts[slot]).createObject();
8280 mParallelPorts[slot]->init(this, slot);
8281 }
8282
8283 /* create the audio adapter object (always present, default is disabled) */
8284 unconst(mAudioAdapter).createObject();
8285 mAudioAdapter->init(this);
8286
8287 /* create the USB device filters object (always present) */
8288 unconst(mUSBDeviceFilters).createObject();
8289 mUSBDeviceFilters->init(this);
8290
8291 /* create associated network adapter objects */
8292 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8293 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8294 {
8295 unconst(mNetworkAdapters[slot]).createObject();
8296 mNetworkAdapters[slot]->init(this, slot);
8297 }
8298
8299 /* create the bandwidth control */
8300 unconst(mBandwidthControl).createObject();
8301 mBandwidthControl->init(this);
8302
8303 return S_OK;
8304}
8305
8306/**
8307 * Helper to uninitialize all associated child objects and to free all data
8308 * structures.
8309 *
8310 * This method must be called as a part of the object's uninitialization
8311 * procedure (usually done in the #uninit() method).
8312 *
8313 * @note Must be called only from #uninit() or from #i_registeredInit().
8314 */
8315void Machine::uninitDataAndChildObjects()
8316{
8317 AutoCaller autoCaller(this);
8318 AssertComRCReturnVoid(autoCaller.rc());
8319 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8320 || getObjectState().getState() == ObjectState::Limited);
8321
8322 /* tell all our other child objects we've been uninitialized */
8323 if (mBandwidthControl)
8324 {
8325 mBandwidthControl->uninit();
8326 unconst(mBandwidthControl).setNull();
8327 }
8328
8329 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8330 {
8331 if (mNetworkAdapters[slot])
8332 {
8333 mNetworkAdapters[slot]->uninit();
8334 unconst(mNetworkAdapters[slot]).setNull();
8335 }
8336 }
8337
8338 if (mUSBDeviceFilters)
8339 {
8340 mUSBDeviceFilters->uninit();
8341 unconst(mUSBDeviceFilters).setNull();
8342 }
8343
8344 if (mAudioAdapter)
8345 {
8346 mAudioAdapter->uninit();
8347 unconst(mAudioAdapter).setNull();
8348 }
8349
8350 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8351 {
8352 if (mParallelPorts[slot])
8353 {
8354 mParallelPorts[slot]->uninit();
8355 unconst(mParallelPorts[slot]).setNull();
8356 }
8357 }
8358
8359 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8360 {
8361 if (mSerialPorts[slot])
8362 {
8363 mSerialPorts[slot]->uninit();
8364 unconst(mSerialPorts[slot]).setNull();
8365 }
8366 }
8367
8368 if (mVRDEServer)
8369 {
8370 mVRDEServer->uninit();
8371 unconst(mVRDEServer).setNull();
8372 }
8373
8374 if (mBIOSSettings)
8375 {
8376 mBIOSSettings->uninit();
8377 unconst(mBIOSSettings).setNull();
8378 }
8379
8380 if (mCaptureSettings)
8381 {
8382 mCaptureSettings->uninit();
8383 unconst(mCaptureSettings).setNull();
8384 }
8385
8386 /* Deassociate media (only when a real Machine or a SnapshotMachine
8387 * instance is uninitialized; SessionMachine instances refer to real
8388 * Machine media). This is necessary for a clean re-initialization of
8389 * the VM after successfully re-checking the accessibility state. Note
8390 * that in case of normal Machine or SnapshotMachine uninitialization (as
8391 * a result of unregistering or deleting the snapshot), outdated media
8392 * attachments will already be uninitialized and deleted, so this
8393 * code will not affect them. */
8394 if ( !mMediumAttachments.isNull()
8395 && !i_isSessionMachine()
8396 )
8397 {
8398 for (MediumAttachmentList::const_iterator
8399 it = mMediumAttachments->begin();
8400 it != mMediumAttachments->end();
8401 ++it)
8402 {
8403 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8404 if (pMedium.isNull())
8405 continue;
8406 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8407 AssertComRC(rc);
8408 }
8409 }
8410
8411 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8412 {
8413 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8414 if (mData->mFirstSnapshot)
8415 {
8416 // snapshots tree is protected by machine write lock; strictly
8417 // this isn't necessary here since we're deleting the entire
8418 // machine, but otherwise we assert in Snapshot::uninit()
8419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8420 mData->mFirstSnapshot->uninit();
8421 mData->mFirstSnapshot.setNull();
8422 }
8423
8424 mData->mCurrentSnapshot.setNull();
8425 }
8426
8427 /* free data structures (the essential mData structure is not freed here
8428 * since it may be still in use) */
8429 mMediumAttachments.free();
8430 mStorageControllers.free();
8431 mUSBControllers.free();
8432 mHWData.free();
8433 mUserData.free();
8434 mSSData.free();
8435}
8436
8437/**
8438 * Returns a pointer to the Machine object for this machine that acts like a
8439 * parent for complex machine data objects such as shared folders, etc.
8440 *
8441 * For primary Machine objects and for SnapshotMachine objects, returns this
8442 * object's pointer itself. For SessionMachine objects, returns the peer
8443 * (primary) machine pointer.
8444 */
8445Machine *Machine::i_getMachine()
8446{
8447 if (i_isSessionMachine())
8448 return (Machine*)mPeer;
8449 return this;
8450}
8451
8452/**
8453 * Makes sure that there are no machine state dependents. If necessary, waits
8454 * for the number of dependents to drop to zero.
8455 *
8456 * Make sure this method is called from under this object's write lock to
8457 * guarantee that no new dependents may be added when this method returns
8458 * control to the caller.
8459 *
8460 * @note Locks this object for writing. The lock will be released while waiting
8461 * (if necessary).
8462 *
8463 * @warning To be used only in methods that change the machine state!
8464 */
8465void Machine::i_ensureNoStateDependencies()
8466{
8467 AssertReturnVoid(isWriteLockOnCurrentThread());
8468
8469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8470
8471 /* Wait for all state dependents if necessary */
8472 if (mData->mMachineStateDeps != 0)
8473 {
8474 /* lazy semaphore creation */
8475 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8476 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8477
8478 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8479 mData->mMachineStateDeps));
8480
8481 ++mData->mMachineStateChangePending;
8482
8483 /* reset the semaphore before waiting, the last dependent will signal
8484 * it */
8485 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8486
8487 alock.release();
8488
8489 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8490
8491 alock.acquire();
8492
8493 -- mData->mMachineStateChangePending;
8494 }
8495}
8496
8497/**
8498 * Changes the machine state and informs callbacks.
8499 *
8500 * This method is not intended to fail so it either returns S_OK or asserts (and
8501 * returns a failure).
8502 *
8503 * @note Locks this object for writing.
8504 */
8505HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8506{
8507 LogFlowThisFuncEnter();
8508 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8509 Assert(aMachineState != MachineState_Null);
8510
8511 AutoCaller autoCaller(this);
8512 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8513
8514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8515
8516 /* wait for state dependents to drop to zero */
8517 i_ensureNoStateDependencies();
8518
8519 MachineState_T const enmOldState = mData->mMachineState;
8520 if (enmOldState != aMachineState)
8521 {
8522 mData->mMachineState = aMachineState;
8523 RTTimeNow(&mData->mLastStateChange);
8524
8525#ifdef VBOX_WITH_DTRACE_R3_MAIN
8526 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8527#endif
8528 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8529 }
8530
8531 LogFlowThisFuncLeave();
8532 return S_OK;
8533}
8534
8535/**
8536 * Searches for a shared folder with the given logical name
8537 * in the collection of shared folders.
8538 *
8539 * @param aName logical name of the shared folder
8540 * @param aSharedFolder where to return the found object
8541 * @param aSetError whether to set the error info if the folder is
8542 * not found
8543 * @return
8544 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8545 *
8546 * @note
8547 * must be called from under the object's lock!
8548 */
8549HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8550 ComObjPtr<SharedFolder> &aSharedFolder,
8551 bool aSetError /* = false */)
8552{
8553 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8554 for (HWData::SharedFolderList::const_iterator
8555 it = mHWData->mSharedFolders.begin();
8556 it != mHWData->mSharedFolders.end();
8557 ++it)
8558 {
8559 SharedFolder *pSF = *it;
8560 AutoCaller autoCaller(pSF);
8561 if (pSF->i_getName() == aName)
8562 {
8563 aSharedFolder = pSF;
8564 rc = S_OK;
8565 break;
8566 }
8567 }
8568
8569 if (aSetError && FAILED(rc))
8570 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8571
8572 return rc;
8573}
8574
8575/**
8576 * Initializes all machine instance data from the given settings structures
8577 * from XML. The exception is the machine UUID which needs special handling
8578 * depending on the caller's use case, so the caller needs to set that herself.
8579 *
8580 * This gets called in several contexts during machine initialization:
8581 *
8582 * -- When machine XML exists on disk already and needs to be loaded into memory,
8583 * for example, from #i_registeredInit() to load all registered machines on
8584 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8585 * attached to the machine should be part of some media registry already.
8586 *
8587 * -- During OVF import, when a machine config has been constructed from an
8588 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8589 * ensure that the media listed as attachments in the config (which have
8590 * been imported from the OVF) receive the correct registry ID.
8591 *
8592 * -- During VM cloning.
8593 *
8594 * @param config Machine settings from XML.
8595 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8596 * for each attached medium in the config.
8597 * @return
8598 */
8599HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8600 const Guid *puuidRegistry)
8601{
8602 // copy name, description, OS type, teleporter, UTC etc.
8603 mUserData->s = config.machineUserData;
8604
8605 // look up the object by Id to check it is valid
8606 ComObjPtr<GuestOSType> pGuestOSType;
8607 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8608 if (!pGuestOSType.isNull())
8609 mUserData->s.strOsType = pGuestOSType->i_id();
8610
8611 // stateFile (optional)
8612 if (config.strStateFile.isEmpty())
8613 mSSData->strStateFilePath.setNull();
8614 else
8615 {
8616 Utf8Str stateFilePathFull(config.strStateFile);
8617 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8618 if (RT_FAILURE(vrc))
8619 return setErrorBoth(E_FAIL, vrc,
8620 tr("Invalid saved state file path '%s' (%Rrc)"),
8621 config.strStateFile.c_str(),
8622 vrc);
8623 mSSData->strStateFilePath = stateFilePathFull;
8624 }
8625
8626 // snapshot folder needs special processing so set it again
8627 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8628 if (FAILED(rc)) return rc;
8629
8630 /* Copy the extra data items (config may or may not be the same as
8631 * mData->pMachineConfigFile) if necessary. When loading the XML files
8632 * from disk they are the same, but not for OVF import. */
8633 if (mData->pMachineConfigFile != &config)
8634 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8635
8636 /* currentStateModified (optional, default is true) */
8637 mData->mCurrentStateModified = config.fCurrentStateModified;
8638
8639 mData->mLastStateChange = config.timeLastStateChange;
8640
8641 /*
8642 * note: all mUserData members must be assigned prior this point because
8643 * we need to commit changes in order to let mUserData be shared by all
8644 * snapshot machine instances.
8645 */
8646 mUserData.commitCopy();
8647
8648 // machine registry, if present (must be loaded before snapshots)
8649 if (config.canHaveOwnMediaRegistry())
8650 {
8651 // determine machine folder
8652 Utf8Str strMachineFolder = i_getSettingsFileFull();
8653 strMachineFolder.stripFilename();
8654 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8655 config.mediaRegistry,
8656 strMachineFolder);
8657 if (FAILED(rc)) return rc;
8658 }
8659
8660 /* Snapshot node (optional) */
8661 size_t cRootSnapshots;
8662 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8663 {
8664 // there must be only one root snapshot
8665 Assert(cRootSnapshots == 1);
8666
8667 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8668
8669 rc = i_loadSnapshot(snap,
8670 config.uuidCurrentSnapshot,
8671 NULL); // no parent == first snapshot
8672 if (FAILED(rc)) return rc;
8673 }
8674
8675 // hardware data
8676 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8677 if (FAILED(rc)) return rc;
8678
8679 /*
8680 * NOTE: the assignment below must be the last thing to do,
8681 * otherwise it will be not possible to change the settings
8682 * somewhere in the code above because all setters will be
8683 * blocked by i_checkStateDependency(MutableStateDep).
8684 */
8685
8686 /* set the machine state to Aborted or Saved when appropriate */
8687 if (config.fAborted)
8688 {
8689 mSSData->strStateFilePath.setNull();
8690
8691 /* no need to use i_setMachineState() during init() */
8692 mData->mMachineState = MachineState_Aborted;
8693 }
8694 else if (!mSSData->strStateFilePath.isEmpty())
8695 {
8696 /* no need to use i_setMachineState() during init() */
8697 mData->mMachineState = MachineState_Saved;
8698 }
8699
8700 // after loading settings, we are no longer different from the XML on disk
8701 mData->flModifications = 0;
8702
8703 return S_OK;
8704}
8705
8706/**
8707 * Recursively loads all snapshots starting from the given.
8708 *
8709 * @param data snapshot settings.
8710 * @param aCurSnapshotId Current snapshot ID from the settings file.
8711 * @param aParentSnapshot Parent snapshot.
8712 */
8713HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8714 const Guid &aCurSnapshotId,
8715 Snapshot *aParentSnapshot)
8716{
8717 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8718 AssertReturn(!i_isSessionMachine(), E_FAIL);
8719
8720 HRESULT rc = S_OK;
8721
8722 Utf8Str strStateFile;
8723 if (!data.strStateFile.isEmpty())
8724 {
8725 /* optional */
8726 strStateFile = data.strStateFile;
8727 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8728 if (RT_FAILURE(vrc))
8729 return setErrorBoth(E_FAIL, vrc,
8730 tr("Invalid saved state file path '%s' (%Rrc)"),
8731 strStateFile.c_str(),
8732 vrc);
8733 }
8734
8735 /* create a snapshot machine object */
8736 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8737 pSnapshotMachine.createObject();
8738 rc = pSnapshotMachine->initFromSettings(this,
8739 data.hardware,
8740 &data.debugging,
8741 &data.autostart,
8742 data.uuid.ref(),
8743 strStateFile);
8744 if (FAILED(rc)) return rc;
8745
8746 /* create a snapshot object */
8747 ComObjPtr<Snapshot> pSnapshot;
8748 pSnapshot.createObject();
8749 /* initialize the snapshot */
8750 rc = pSnapshot->init(mParent, // VirtualBox object
8751 data.uuid,
8752 data.strName,
8753 data.strDescription,
8754 data.timestamp,
8755 pSnapshotMachine,
8756 aParentSnapshot);
8757 if (FAILED(rc)) return rc;
8758
8759 /* memorize the first snapshot if necessary */
8760 if (!mData->mFirstSnapshot)
8761 mData->mFirstSnapshot = pSnapshot;
8762
8763 /* memorize the current snapshot when appropriate */
8764 if ( !mData->mCurrentSnapshot
8765 && pSnapshot->i_getId() == aCurSnapshotId
8766 )
8767 mData->mCurrentSnapshot = pSnapshot;
8768
8769 // now create the children
8770 for (settings::SnapshotsList::const_iterator
8771 it = data.llChildSnapshots.begin();
8772 it != data.llChildSnapshots.end();
8773 ++it)
8774 {
8775 const settings::Snapshot &childData = *it;
8776 // recurse
8777 rc = i_loadSnapshot(childData,
8778 aCurSnapshotId,
8779 pSnapshot); // parent = the one we created above
8780 if (FAILED(rc)) return rc;
8781 }
8782
8783 return rc;
8784}
8785
8786/**
8787 * Loads settings into mHWData.
8788 *
8789 * @param puuidRegistry Registry ID.
8790 * @param puuidSnapshot Snapshot ID
8791 * @param data Reference to the hardware settings.
8792 * @param pDbg Pointer to the debugging settings.
8793 * @param pAutostart Pointer to the autostart settings.
8794 */
8795HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8796 const Guid *puuidSnapshot,
8797 const settings::Hardware &data,
8798 const settings::Debugging *pDbg,
8799 const settings::Autostart *pAutostart)
8800{
8801 AssertReturn(!i_isSessionMachine(), E_FAIL);
8802
8803 HRESULT rc = S_OK;
8804
8805 try
8806 {
8807 ComObjPtr<GuestOSType> pGuestOSType;
8808 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8809
8810 /* The hardware version attribute (optional). */
8811 mHWData->mHWVersion = data.strVersion;
8812 mHWData->mHardwareUUID = data.uuid;
8813
8814 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8815 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8816 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8817 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8818 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8819 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8820 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8821 mHWData->mPAEEnabled = data.fPAE;
8822 mHWData->mLongMode = data.enmLongMode;
8823 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8824 mHWData->mAPIC = data.fAPIC;
8825 mHWData->mX2APIC = data.fX2APIC;
8826 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8827 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8828 mHWData->mSpecCtrl = data.fSpecCtrl;
8829 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8830 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8831 mHWData->mCPUCount = data.cCPUs;
8832 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8833 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8834 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8835 mHWData->mCpuProfile = data.strCpuProfile;
8836
8837 // cpu
8838 if (mHWData->mCPUHotPlugEnabled)
8839 {
8840 for (settings::CpuList::const_iterator
8841 it = data.llCpus.begin();
8842 it != data.llCpus.end();
8843 ++it)
8844 {
8845 const settings::Cpu &cpu = *it;
8846
8847 mHWData->mCPUAttached[cpu.ulId] = true;
8848 }
8849 }
8850
8851 // cpuid leafs
8852 for (settings::CpuIdLeafsList::const_iterator
8853 it = data.llCpuIdLeafs.begin();
8854 it != data.llCpuIdLeafs.end();
8855 ++it)
8856 {
8857 const settings::CpuIdLeaf &rLeaf= *it;
8858 if ( rLeaf.idx < UINT32_C(0x20)
8859 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8860 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8861 mHWData->mCpuIdLeafList.push_back(rLeaf);
8862 /* else: just ignore */
8863 }
8864
8865 mHWData->mMemorySize = data.ulMemorySizeMB;
8866 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8867
8868 // boot order
8869 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8870 {
8871 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8872 if (it == data.mapBootOrder.end())
8873 mHWData->mBootOrder[i] = DeviceType_Null;
8874 else
8875 mHWData->mBootOrder[i] = it->second;
8876 }
8877
8878 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8879 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8880 mHWData->mMonitorCount = data.cMonitors;
8881 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8882 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8883 mHWData->mFirmwareType = data.firmwareType;
8884 mHWData->mPointingHIDType = data.pointingHIDType;
8885 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8886 mHWData->mChipsetType = data.chipsetType;
8887 mHWData->mParavirtProvider = data.paravirtProvider;
8888 mHWData->mParavirtDebug = data.strParavirtDebug;
8889 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8890 mHWData->mHPETEnabled = data.fHPETEnabled;
8891
8892 /* VRDEServer */
8893 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8894 if (FAILED(rc)) return rc;
8895
8896 /* BIOS */
8897 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8898 if (FAILED(rc)) return rc;
8899
8900 /* Capture settings */
8901 rc = mCaptureSettings->i_loadSettings(data.captureSettings);
8902 if (FAILED(rc)) return rc;
8903
8904 // Bandwidth control (must come before network adapters)
8905 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8906 if (FAILED(rc)) return rc;
8907
8908 /* USB controllers */
8909 for (settings::USBControllerList::const_iterator
8910 it = data.usbSettings.llUSBControllers.begin();
8911 it != data.usbSettings.llUSBControllers.end();
8912 ++it)
8913 {
8914 const settings::USBController &settingsCtrl = *it;
8915 ComObjPtr<USBController> newCtrl;
8916
8917 newCtrl.createObject();
8918 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8919 mUSBControllers->push_back(newCtrl);
8920 }
8921
8922 /* USB device filters */
8923 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8924 if (FAILED(rc)) return rc;
8925
8926 // network adapters (establish array size first and apply defaults, to
8927 // ensure reading the same settings as we saved, since the list skips
8928 // adapters having defaults)
8929 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8930 size_t oldCount = mNetworkAdapters.size();
8931 if (newCount > oldCount)
8932 {
8933 mNetworkAdapters.resize(newCount);
8934 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8935 {
8936 unconst(mNetworkAdapters[slot]).createObject();
8937 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8938 }
8939 }
8940 else if (newCount < oldCount)
8941 mNetworkAdapters.resize(newCount);
8942 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8943 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8944 for (settings::NetworkAdaptersList::const_iterator
8945 it = data.llNetworkAdapters.begin();
8946 it != data.llNetworkAdapters.end();
8947 ++it)
8948 {
8949 const settings::NetworkAdapter &nic = *it;
8950
8951 /* slot uniqueness is guaranteed by XML Schema */
8952 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8953 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8954 if (FAILED(rc)) return rc;
8955 }
8956
8957 // serial ports (establish defaults first, to ensure reading the same
8958 // settings as we saved, since the list skips ports having defaults)
8959 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8960 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8961 for (settings::SerialPortsList::const_iterator
8962 it = data.llSerialPorts.begin();
8963 it != data.llSerialPorts.end();
8964 ++it)
8965 {
8966 const settings::SerialPort &s = *it;
8967
8968 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8969 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8970 if (FAILED(rc)) return rc;
8971 }
8972
8973 // parallel ports (establish defaults first, to ensure reading the same
8974 // settings as we saved, since the list skips ports having defaults)
8975 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8976 mParallelPorts[i]->i_applyDefaults();
8977 for (settings::ParallelPortsList::const_iterator
8978 it = data.llParallelPorts.begin();
8979 it != data.llParallelPorts.end();
8980 ++it)
8981 {
8982 const settings::ParallelPort &p = *it;
8983
8984 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8985 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8986 if (FAILED(rc)) return rc;
8987 }
8988
8989 /* AudioAdapter */
8990 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8991 if (FAILED(rc)) return rc;
8992
8993 /* storage controllers */
8994 rc = i_loadStorageControllers(data.storage,
8995 puuidRegistry,
8996 puuidSnapshot);
8997 if (FAILED(rc)) return rc;
8998
8999 /* Shared folders */
9000 for (settings::SharedFoldersList::const_iterator
9001 it = data.llSharedFolders.begin();
9002 it != data.llSharedFolders.end();
9003 ++it)
9004 {
9005 const settings::SharedFolder &sf = *it;
9006
9007 ComObjPtr<SharedFolder> sharedFolder;
9008 /* Check for double entries. Not allowed! */
9009 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9010 if (SUCCEEDED(rc))
9011 return setError(VBOX_E_OBJECT_IN_USE,
9012 tr("Shared folder named '%s' already exists"),
9013 sf.strName.c_str());
9014
9015 /* Create the new shared folder. Don't break on error. This will be
9016 * reported when the machine starts. */
9017 sharedFolder.createObject();
9018 rc = sharedFolder->init(i_getMachine(),
9019 sf.strName,
9020 sf.strHostPath,
9021 RT_BOOL(sf.fWritable),
9022 RT_BOOL(sf.fAutoMount),
9023 false /* fFailOnError */);
9024 if (FAILED(rc)) return rc;
9025 mHWData->mSharedFolders.push_back(sharedFolder);
9026 }
9027
9028 // Clipboard
9029 mHWData->mClipboardMode = data.clipboardMode;
9030
9031 // drag'n'drop
9032 mHWData->mDnDMode = data.dndMode;
9033
9034 // guest settings
9035 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9036
9037 // IO settings
9038 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9039 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9040
9041 // Host PCI devices
9042 for (settings::HostPCIDeviceAttachmentList::const_iterator
9043 it = data.pciAttachments.begin();
9044 it != data.pciAttachments.end();
9045 ++it)
9046 {
9047 const settings::HostPCIDeviceAttachment &hpda = *it;
9048 ComObjPtr<PCIDeviceAttachment> pda;
9049
9050 pda.createObject();
9051 pda->i_loadSettings(this, hpda);
9052 mHWData->mPCIDeviceAssignments.push_back(pda);
9053 }
9054
9055 /*
9056 * (The following isn't really real hardware, but it lives in HWData
9057 * for reasons of convenience.)
9058 */
9059
9060#ifdef VBOX_WITH_GUEST_PROPS
9061 /* Guest properties (optional) */
9062
9063 /* Only load transient guest properties for configs which have saved
9064 * state, because there shouldn't be any for powered off VMs. The same
9065 * logic applies for snapshots, as offline snapshots shouldn't have
9066 * any such properties. They confuse the code in various places.
9067 * Note: can't rely on the machine state, as it isn't set yet. */
9068 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9069 /* apologies for the hacky unconst() usage, but this needs hacking
9070 * actually inconsistent settings into consistency, otherwise there
9071 * will be some corner cases where the inconsistency survives
9072 * surprisingly long without getting fixed, especially for snapshots
9073 * as there are no config changes. */
9074 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9075 for (settings::GuestPropertiesList::iterator
9076 it = llGuestProperties.begin();
9077 it != llGuestProperties.end();
9078 /*nothing*/)
9079 {
9080 const settings::GuestProperty &prop = *it;
9081 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9082 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9083 if ( fSkipTransientGuestProperties
9084 && ( fFlags & GUEST_PROP_F_TRANSIENT
9085 || fFlags & GUEST_PROP_F_TRANSRESET))
9086 {
9087 it = llGuestProperties.erase(it);
9088 continue;
9089 }
9090 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9091 mHWData->mGuestProperties[prop.strName] = property;
9092 ++it;
9093 }
9094#endif /* VBOX_WITH_GUEST_PROPS defined */
9095
9096 rc = i_loadDebugging(pDbg);
9097 if (FAILED(rc))
9098 return rc;
9099
9100 mHWData->mAutostart = *pAutostart;
9101
9102 /* default frontend */
9103 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9104 }
9105 catch (std::bad_alloc &)
9106 {
9107 return E_OUTOFMEMORY;
9108 }
9109
9110 AssertComRC(rc);
9111 return rc;
9112}
9113
9114/**
9115 * Called from i_loadHardware() to load the debugging settings of the
9116 * machine.
9117 *
9118 * @param pDbg Pointer to the settings.
9119 */
9120HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9121{
9122 mHWData->mDebugging = *pDbg;
9123 /* no more processing currently required, this will probably change. */
9124 return S_OK;
9125}
9126
9127/**
9128 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9129 *
9130 * @param data storage settings.
9131 * @param puuidRegistry media registry ID to set media to or NULL;
9132 * see Machine::i_loadMachineDataFromSettings()
9133 * @param puuidSnapshot snapshot ID
9134 * @return
9135 */
9136HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9137 const Guid *puuidRegistry,
9138 const Guid *puuidSnapshot)
9139{
9140 AssertReturn(!i_isSessionMachine(), E_FAIL);
9141
9142 HRESULT rc = S_OK;
9143
9144 for (settings::StorageControllersList::const_iterator
9145 it = data.llStorageControllers.begin();
9146 it != data.llStorageControllers.end();
9147 ++it)
9148 {
9149 const settings::StorageController &ctlData = *it;
9150
9151 ComObjPtr<StorageController> pCtl;
9152 /* Try to find one with the name first. */
9153 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9154 if (SUCCEEDED(rc))
9155 return setError(VBOX_E_OBJECT_IN_USE,
9156 tr("Storage controller named '%s' already exists"),
9157 ctlData.strName.c_str());
9158
9159 pCtl.createObject();
9160 rc = pCtl->init(this,
9161 ctlData.strName,
9162 ctlData.storageBus,
9163 ctlData.ulInstance,
9164 ctlData.fBootable);
9165 if (FAILED(rc)) return rc;
9166
9167 mStorageControllers->push_back(pCtl);
9168
9169 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9170 if (FAILED(rc)) return rc;
9171
9172 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9173 if (FAILED(rc)) return rc;
9174
9175 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9176 if (FAILED(rc)) return rc;
9177
9178 /* Load the attached devices now. */
9179 rc = i_loadStorageDevices(pCtl,
9180 ctlData,
9181 puuidRegistry,
9182 puuidSnapshot);
9183 if (FAILED(rc)) return rc;
9184 }
9185
9186 return S_OK;
9187}
9188
9189/**
9190 * Called from i_loadStorageControllers for a controller's devices.
9191 *
9192 * @param aStorageController
9193 * @param data
9194 * @param puuidRegistry media registry ID to set media to or NULL; see
9195 * Machine::i_loadMachineDataFromSettings()
9196 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9197 * @return
9198 */
9199HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9200 const settings::StorageController &data,
9201 const Guid *puuidRegistry,
9202 const Guid *puuidSnapshot)
9203{
9204 HRESULT rc = S_OK;
9205
9206 /* paranoia: detect duplicate attachments */
9207 for (settings::AttachedDevicesList::const_iterator
9208 it = data.llAttachedDevices.begin();
9209 it != data.llAttachedDevices.end();
9210 ++it)
9211 {
9212 const settings::AttachedDevice &ad = *it;
9213
9214 for (settings::AttachedDevicesList::const_iterator it2 = it;
9215 it2 != data.llAttachedDevices.end();
9216 ++it2)
9217 {
9218 if (it == it2)
9219 continue;
9220
9221 const settings::AttachedDevice &ad2 = *it2;
9222
9223 if ( ad.lPort == ad2.lPort
9224 && ad.lDevice == ad2.lDevice)
9225 {
9226 return setError(E_FAIL,
9227 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9228 aStorageController->i_getName().c_str(),
9229 ad.lPort,
9230 ad.lDevice,
9231 mUserData->s.strName.c_str());
9232 }
9233 }
9234 }
9235
9236 for (settings::AttachedDevicesList::const_iterator
9237 it = data.llAttachedDevices.begin();
9238 it != data.llAttachedDevices.end();
9239 ++it)
9240 {
9241 const settings::AttachedDevice &dev = *it;
9242 ComObjPtr<Medium> medium;
9243
9244 switch (dev.deviceType)
9245 {
9246 case DeviceType_Floppy:
9247 case DeviceType_DVD:
9248 if (dev.strHostDriveSrc.isNotEmpty())
9249 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9250 false /* fRefresh */, medium);
9251 else
9252 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9253 dev.uuid,
9254 false /* fRefresh */,
9255 false /* aSetError */,
9256 medium);
9257 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9258 // This is not an error. The host drive or UUID might have vanished, so just go
9259 // ahead without this removeable medium attachment
9260 rc = S_OK;
9261 break;
9262
9263 case DeviceType_HardDisk:
9264 {
9265 /* find a hard disk by UUID */
9266 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9267 if (FAILED(rc))
9268 {
9269 if (i_isSnapshotMachine())
9270 {
9271 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9272 // so the user knows that the bad disk is in a snapshot somewhere
9273 com::ErrorInfo info;
9274 return setError(E_FAIL,
9275 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9276 puuidSnapshot->raw(),
9277 info.getText().raw());
9278 }
9279 else
9280 return rc;
9281 }
9282
9283 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9284
9285 if (medium->i_getType() == MediumType_Immutable)
9286 {
9287 if (i_isSnapshotMachine())
9288 return setError(E_FAIL,
9289 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9290 "of the virtual machine '%s' ('%s')"),
9291 medium->i_getLocationFull().c_str(),
9292 dev.uuid.raw(),
9293 puuidSnapshot->raw(),
9294 mUserData->s.strName.c_str(),
9295 mData->m_strConfigFileFull.c_str());
9296
9297 return setError(E_FAIL,
9298 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9299 medium->i_getLocationFull().c_str(),
9300 dev.uuid.raw(),
9301 mUserData->s.strName.c_str(),
9302 mData->m_strConfigFileFull.c_str());
9303 }
9304
9305 if (medium->i_getType() == MediumType_MultiAttach)
9306 {
9307 if (i_isSnapshotMachine())
9308 return setError(E_FAIL,
9309 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9310 "of the virtual machine '%s' ('%s')"),
9311 medium->i_getLocationFull().c_str(),
9312 dev.uuid.raw(),
9313 puuidSnapshot->raw(),
9314 mUserData->s.strName.c_str(),
9315 mData->m_strConfigFileFull.c_str());
9316
9317 return setError(E_FAIL,
9318 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9319 medium->i_getLocationFull().c_str(),
9320 dev.uuid.raw(),
9321 mUserData->s.strName.c_str(),
9322 mData->m_strConfigFileFull.c_str());
9323 }
9324
9325 if ( !i_isSnapshotMachine()
9326 && medium->i_getChildren().size() != 0
9327 )
9328 return setError(E_FAIL,
9329 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9330 "because it has %d differencing child hard disks"),
9331 medium->i_getLocationFull().c_str(),
9332 dev.uuid.raw(),
9333 mUserData->s.strName.c_str(),
9334 mData->m_strConfigFileFull.c_str(),
9335 medium->i_getChildren().size());
9336
9337 if (i_findAttachment(*mMediumAttachments.data(),
9338 medium))
9339 return setError(E_FAIL,
9340 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9341 medium->i_getLocationFull().c_str(),
9342 dev.uuid.raw(),
9343 mUserData->s.strName.c_str(),
9344 mData->m_strConfigFileFull.c_str());
9345
9346 break;
9347 }
9348
9349 default:
9350 return setError(E_FAIL,
9351 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9352 medium->i_getLocationFull().c_str(),
9353 mUserData->s.strName.c_str(),
9354 mData->m_strConfigFileFull.c_str());
9355 }
9356
9357 if (FAILED(rc))
9358 break;
9359
9360 /* Bandwidth groups are loaded at this point. */
9361 ComObjPtr<BandwidthGroup> pBwGroup;
9362
9363 if (!dev.strBwGroup.isEmpty())
9364 {
9365 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9366 if (FAILED(rc))
9367 return setError(E_FAIL,
9368 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9369 medium->i_getLocationFull().c_str(),
9370 dev.strBwGroup.c_str(),
9371 mUserData->s.strName.c_str(),
9372 mData->m_strConfigFileFull.c_str());
9373 pBwGroup->i_reference();
9374 }
9375
9376 const Utf8Str controllerName = aStorageController->i_getName();
9377 ComObjPtr<MediumAttachment> pAttachment;
9378 pAttachment.createObject();
9379 rc = pAttachment->init(this,
9380 medium,
9381 controllerName,
9382 dev.lPort,
9383 dev.lDevice,
9384 dev.deviceType,
9385 false,
9386 dev.fPassThrough,
9387 dev.fTempEject,
9388 dev.fNonRotational,
9389 dev.fDiscard,
9390 dev.fHotPluggable,
9391 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9392 if (FAILED(rc)) break;
9393
9394 /* associate the medium with this machine and snapshot */
9395 if (!medium.isNull())
9396 {
9397 AutoCaller medCaller(medium);
9398 if (FAILED(medCaller.rc())) return medCaller.rc();
9399 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9400
9401 if (i_isSnapshotMachine())
9402 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9403 else
9404 rc = medium->i_addBackReference(mData->mUuid);
9405 /* If the medium->addBackReference fails it sets an appropriate
9406 * error message, so no need to do any guesswork here. */
9407
9408 if (puuidRegistry)
9409 // caller wants registry ID to be set on all attached media (OVF import case)
9410 medium->i_addRegistry(*puuidRegistry);
9411 }
9412
9413 if (FAILED(rc))
9414 break;
9415
9416 /* back up mMediumAttachments to let registeredInit() properly rollback
9417 * on failure (= limited accessibility) */
9418 i_setModified(IsModified_Storage);
9419 mMediumAttachments.backup();
9420 mMediumAttachments->push_back(pAttachment);
9421 }
9422
9423 return rc;
9424}
9425
9426/**
9427 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9428 *
9429 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9430 * @param aSnapshot where to return the found snapshot
9431 * @param aSetError true to set extended error info on failure
9432 */
9433HRESULT Machine::i_findSnapshotById(const Guid &aId,
9434 ComObjPtr<Snapshot> &aSnapshot,
9435 bool aSetError /* = false */)
9436{
9437 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9438
9439 if (!mData->mFirstSnapshot)
9440 {
9441 if (aSetError)
9442 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9443 return E_FAIL;
9444 }
9445
9446 if (aId.isZero())
9447 aSnapshot = mData->mFirstSnapshot;
9448 else
9449 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9450
9451 if (!aSnapshot)
9452 {
9453 if (aSetError)
9454 return setError(E_FAIL,
9455 tr("Could not find a snapshot with UUID {%s}"),
9456 aId.toString().c_str());
9457 return E_FAIL;
9458 }
9459
9460 return S_OK;
9461}
9462
9463/**
9464 * Returns the snapshot with the given name or fails of no such snapshot.
9465 *
9466 * @param strName snapshot name to find
9467 * @param aSnapshot where to return the found snapshot
9468 * @param aSetError true to set extended error info on failure
9469 */
9470HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9471 ComObjPtr<Snapshot> &aSnapshot,
9472 bool aSetError /* = false */)
9473{
9474 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9475
9476 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9477
9478 if (!mData->mFirstSnapshot)
9479 {
9480 if (aSetError)
9481 return setError(VBOX_E_OBJECT_NOT_FOUND,
9482 tr("This machine does not have any snapshots"));
9483 return VBOX_E_OBJECT_NOT_FOUND;
9484 }
9485
9486 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9487
9488 if (!aSnapshot)
9489 {
9490 if (aSetError)
9491 return setError(VBOX_E_OBJECT_NOT_FOUND,
9492 tr("Could not find a snapshot named '%s'"), strName.c_str());
9493 return VBOX_E_OBJECT_NOT_FOUND;
9494 }
9495
9496 return S_OK;
9497}
9498
9499/**
9500 * Returns a storage controller object with the given name.
9501 *
9502 * @param aName storage controller name to find
9503 * @param aStorageController where to return the found storage controller
9504 * @param aSetError true to set extended error info on failure
9505 */
9506HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9507 ComObjPtr<StorageController> &aStorageController,
9508 bool aSetError /* = false */)
9509{
9510 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9511
9512 for (StorageControllerList::const_iterator
9513 it = mStorageControllers->begin();
9514 it != mStorageControllers->end();
9515 ++it)
9516 {
9517 if ((*it)->i_getName() == aName)
9518 {
9519 aStorageController = (*it);
9520 return S_OK;
9521 }
9522 }
9523
9524 if (aSetError)
9525 return setError(VBOX_E_OBJECT_NOT_FOUND,
9526 tr("Could not find a storage controller named '%s'"),
9527 aName.c_str());
9528 return VBOX_E_OBJECT_NOT_FOUND;
9529}
9530
9531/**
9532 * Returns a USB controller object with the given name.
9533 *
9534 * @param aName USB controller name to find
9535 * @param aUSBController where to return the found USB controller
9536 * @param aSetError true to set extended error info on failure
9537 */
9538HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9539 ComObjPtr<USBController> &aUSBController,
9540 bool aSetError /* = false */)
9541{
9542 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9543
9544 for (USBControllerList::const_iterator
9545 it = mUSBControllers->begin();
9546 it != mUSBControllers->end();
9547 ++it)
9548 {
9549 if ((*it)->i_getName() == aName)
9550 {
9551 aUSBController = (*it);
9552 return S_OK;
9553 }
9554 }
9555
9556 if (aSetError)
9557 return setError(VBOX_E_OBJECT_NOT_FOUND,
9558 tr("Could not find a storage controller named '%s'"),
9559 aName.c_str());
9560 return VBOX_E_OBJECT_NOT_FOUND;
9561}
9562
9563/**
9564 * Returns the number of USB controller instance of the given type.
9565 *
9566 * @param enmType USB controller type.
9567 */
9568ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9569{
9570 ULONG cCtrls = 0;
9571
9572 for (USBControllerList::const_iterator
9573 it = mUSBControllers->begin();
9574 it != mUSBControllers->end();
9575 ++it)
9576 {
9577 if ((*it)->i_getControllerType() == enmType)
9578 cCtrls++;
9579 }
9580
9581 return cCtrls;
9582}
9583
9584HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9585 MediumAttachmentList &atts)
9586{
9587 AutoCaller autoCaller(this);
9588 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9589
9590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9591
9592 for (MediumAttachmentList::const_iterator
9593 it = mMediumAttachments->begin();
9594 it != mMediumAttachments->end();
9595 ++it)
9596 {
9597 const ComObjPtr<MediumAttachment> &pAtt = *it;
9598 // should never happen, but deal with NULL pointers in the list.
9599 AssertContinue(!pAtt.isNull());
9600
9601 // getControllerName() needs caller+read lock
9602 AutoCaller autoAttCaller(pAtt);
9603 if (FAILED(autoAttCaller.rc()))
9604 {
9605 atts.clear();
9606 return autoAttCaller.rc();
9607 }
9608 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9609
9610 if (pAtt->i_getControllerName() == aName)
9611 atts.push_back(pAtt);
9612 }
9613
9614 return S_OK;
9615}
9616
9617
9618/**
9619 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9620 * file if the machine name was changed and about creating a new settings file
9621 * if this is a new machine.
9622 *
9623 * @note Must be never called directly but only from #saveSettings().
9624 */
9625HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9626{
9627 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9628
9629 HRESULT rc = S_OK;
9630
9631 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9632
9633 /// @todo need to handle primary group change, too
9634
9635 /* attempt to rename the settings file if machine name is changed */
9636 if ( mUserData->s.fNameSync
9637 && mUserData.isBackedUp()
9638 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9639 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9640 )
9641 {
9642 bool dirRenamed = false;
9643 bool fileRenamed = false;
9644
9645 Utf8Str configFile, newConfigFile;
9646 Utf8Str configFilePrev, newConfigFilePrev;
9647 Utf8Str configDir, newConfigDir;
9648
9649 do
9650 {
9651 int vrc = VINF_SUCCESS;
9652
9653 Utf8Str name = mUserData.backedUpData()->s.strName;
9654 Utf8Str newName = mUserData->s.strName;
9655 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9656 if (group == "/")
9657 group.setNull();
9658 Utf8Str newGroup = mUserData->s.llGroups.front();
9659 if (newGroup == "/")
9660 newGroup.setNull();
9661
9662 configFile = mData->m_strConfigFileFull;
9663
9664 /* first, rename the directory if it matches the group and machine name */
9665 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9666 group.c_str(), RTPATH_DELIMITER, name.c_str());
9667 /** @todo hack, make somehow use of ComposeMachineFilename */
9668 if (mUserData->s.fDirectoryIncludesUUID)
9669 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9670 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9671 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9672 /** @todo hack, make somehow use of ComposeMachineFilename */
9673 if (mUserData->s.fDirectoryIncludesUUID)
9674 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9675 configDir = configFile;
9676 configDir.stripFilename();
9677 newConfigDir = configDir;
9678 if ( configDir.length() >= groupPlusName.length()
9679 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9680 groupPlusName.c_str()))
9681 {
9682 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9683 Utf8Str newConfigBaseDir(newConfigDir);
9684 newConfigDir.append(newGroupPlusName);
9685 /* consistency: use \ if appropriate on the platform */
9686 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9687 /* new dir and old dir cannot be equal here because of 'if'
9688 * above and because name != newName */
9689 Assert(configDir != newConfigDir);
9690 if (!fSettingsFileIsNew)
9691 {
9692 /* perform real rename only if the machine is not new */
9693 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9694 if ( vrc == VERR_FILE_NOT_FOUND
9695 || vrc == VERR_PATH_NOT_FOUND)
9696 {
9697 /* create the parent directory, then retry renaming */
9698 Utf8Str parent(newConfigDir);
9699 parent.stripFilename();
9700 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9701 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9702 }
9703 if (RT_FAILURE(vrc))
9704 {
9705 rc = setErrorBoth(E_FAIL, vrc,
9706 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9707 configDir.c_str(),
9708 newConfigDir.c_str(),
9709 vrc);
9710 break;
9711 }
9712 /* delete subdirectories which are no longer needed */
9713 Utf8Str dir(configDir);
9714 dir.stripFilename();
9715 while (dir != newConfigBaseDir && dir != ".")
9716 {
9717 vrc = RTDirRemove(dir.c_str());
9718 if (RT_FAILURE(vrc))
9719 break;
9720 dir.stripFilename();
9721 }
9722 dirRenamed = true;
9723 }
9724 }
9725
9726 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9727 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9728
9729 /* then try to rename the settings file itself */
9730 if (newConfigFile != configFile)
9731 {
9732 /* get the path to old settings file in renamed directory */
9733 configFile = Utf8StrFmt("%s%c%s",
9734 newConfigDir.c_str(),
9735 RTPATH_DELIMITER,
9736 RTPathFilename(configFile.c_str()));
9737 if (!fSettingsFileIsNew)
9738 {
9739 /* perform real rename only if the machine is not new */
9740 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9741 if (RT_FAILURE(vrc))
9742 {
9743 rc = setErrorBoth(E_FAIL, vrc,
9744 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9745 configFile.c_str(),
9746 newConfigFile.c_str(),
9747 vrc);
9748 break;
9749 }
9750 fileRenamed = true;
9751 configFilePrev = configFile;
9752 configFilePrev += "-prev";
9753 newConfigFilePrev = newConfigFile;
9754 newConfigFilePrev += "-prev";
9755 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9756 }
9757 }
9758
9759 // update m_strConfigFileFull amd mConfigFile
9760 mData->m_strConfigFileFull = newConfigFile;
9761 // compute the relative path too
9762 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9763
9764 // store the old and new so that VirtualBox::i_saveSettings() can update
9765 // the media registry
9766 if ( mData->mRegistered
9767 && (configDir != newConfigDir || configFile != newConfigFile))
9768 {
9769 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9770
9771 if (pfNeedsGlobalSaveSettings)
9772 *pfNeedsGlobalSaveSettings = true;
9773 }
9774
9775 // in the saved state file path, replace the old directory with the new directory
9776 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9777 {
9778 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9779 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9780 }
9781
9782 // and do the same thing for the saved state file paths of all the online snapshots
9783 if (mData->mFirstSnapshot)
9784 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9785 newConfigDir.c_str());
9786 }
9787 while (0);
9788
9789 if (FAILED(rc))
9790 {
9791 /* silently try to rename everything back */
9792 if (fileRenamed)
9793 {
9794 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9795 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9796 }
9797 if (dirRenamed)
9798 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9799 }
9800
9801 if (FAILED(rc)) return rc;
9802 }
9803
9804 if (fSettingsFileIsNew)
9805 {
9806 /* create a virgin config file */
9807 int vrc = VINF_SUCCESS;
9808
9809 /* ensure the settings directory exists */
9810 Utf8Str path(mData->m_strConfigFileFull);
9811 path.stripFilename();
9812 if (!RTDirExists(path.c_str()))
9813 {
9814 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9815 if (RT_FAILURE(vrc))
9816 {
9817 return setErrorBoth(E_FAIL, vrc,
9818 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9819 path.c_str(),
9820 vrc);
9821 }
9822 }
9823
9824 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9825 path = Utf8Str(mData->m_strConfigFileFull);
9826 RTFILE f = NIL_RTFILE;
9827 vrc = RTFileOpen(&f, path.c_str(),
9828 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9829 if (RT_FAILURE(vrc))
9830 return setErrorBoth(E_FAIL, vrc,
9831 tr("Could not create the settings file '%s' (%Rrc)"),
9832 path.c_str(),
9833 vrc);
9834 RTFileClose(f);
9835 }
9836
9837 return rc;
9838}
9839
9840/**
9841 * Saves and commits machine data, user data and hardware data.
9842 *
9843 * Note that on failure, the data remains uncommitted.
9844 *
9845 * @a aFlags may combine the following flags:
9846 *
9847 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9848 * Used when saving settings after an operation that makes them 100%
9849 * correspond to the settings from the current snapshot.
9850 * - SaveS_Force: settings will be saved without doing a deep compare of the
9851 * settings structures. This is used when this is called because snapshots
9852 * have changed to avoid the overhead of the deep compare.
9853 *
9854 * @note Must be called from under this object's write lock. Locks children for
9855 * writing.
9856 *
9857 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9858 * initialized to false and that will be set to true by this function if
9859 * the caller must invoke VirtualBox::i_saveSettings() because the global
9860 * settings have changed. This will happen if a machine rename has been
9861 * saved and the global machine and media registries will therefore need
9862 * updating.
9863 * @param aFlags Flags.
9864 */
9865HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9866 int aFlags /*= 0*/)
9867{
9868 LogFlowThisFuncEnter();
9869
9870 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9871
9872 /* make sure child objects are unable to modify the settings while we are
9873 * saving them */
9874 i_ensureNoStateDependencies();
9875
9876 AssertReturn(!i_isSnapshotMachine(),
9877 E_FAIL);
9878
9879 HRESULT rc = S_OK;
9880 bool fNeedsWrite = false;
9881
9882 /* First, prepare to save settings. It will care about renaming the
9883 * settings directory and file if the machine name was changed and about
9884 * creating a new settings file if this is a new machine. */
9885 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9886 if (FAILED(rc)) return rc;
9887
9888 // keep a pointer to the current settings structures
9889 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9890 settings::MachineConfigFile *pNewConfig = NULL;
9891
9892 try
9893 {
9894 // make a fresh one to have everyone write stuff into
9895 pNewConfig = new settings::MachineConfigFile(NULL);
9896 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9897
9898 // now go and copy all the settings data from COM to the settings structures
9899 // (this calls i_saveSettings() on all the COM objects in the machine)
9900 i_copyMachineDataToSettings(*pNewConfig);
9901
9902 if (aFlags & SaveS_ResetCurStateModified)
9903 {
9904 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9905 mData->mCurrentStateModified = FALSE;
9906 fNeedsWrite = true; // always, no need to compare
9907 }
9908 else if (aFlags & SaveS_Force)
9909 {
9910 fNeedsWrite = true; // always, no need to compare
9911 }
9912 else
9913 {
9914 if (!mData->mCurrentStateModified)
9915 {
9916 // do a deep compare of the settings that we just saved with the settings
9917 // previously stored in the config file; this invokes MachineConfigFile::operator==
9918 // which does a deep compare of all the settings, which is expensive but less expensive
9919 // than writing out XML in vain
9920 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9921
9922 // could still be modified if any settings changed
9923 mData->mCurrentStateModified = fAnySettingsChanged;
9924
9925 fNeedsWrite = fAnySettingsChanged;
9926 }
9927 else
9928 fNeedsWrite = true;
9929 }
9930
9931 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9932
9933 if (fNeedsWrite)
9934 // now spit it all out!
9935 pNewConfig->write(mData->m_strConfigFileFull);
9936
9937 mData->pMachineConfigFile = pNewConfig;
9938 delete pOldConfig;
9939 i_commit();
9940
9941 // after saving settings, we are no longer different from the XML on disk
9942 mData->flModifications = 0;
9943 }
9944 catch (HRESULT err)
9945 {
9946 // we assume that error info is set by the thrower
9947 rc = err;
9948
9949 // restore old config
9950 delete pNewConfig;
9951 mData->pMachineConfigFile = pOldConfig;
9952 }
9953 catch (...)
9954 {
9955 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9956 }
9957
9958 if (fNeedsWrite)
9959 {
9960 /* Fire the data change event, even on failure (since we've already
9961 * committed all data). This is done only for SessionMachines because
9962 * mutable Machine instances are always not registered (i.e. private
9963 * to the client process that creates them) and thus don't need to
9964 * inform callbacks. */
9965 if (i_isSessionMachine())
9966 mParent->i_onMachineDataChange(mData->mUuid);
9967 }
9968
9969 LogFlowThisFunc(("rc=%08X\n", rc));
9970 LogFlowThisFuncLeave();
9971 return rc;
9972}
9973
9974/**
9975 * Implementation for saving the machine settings into the given
9976 * settings::MachineConfigFile instance. This copies machine extradata
9977 * from the previous machine config file in the instance data, if any.
9978 *
9979 * This gets called from two locations:
9980 *
9981 * -- Machine::i_saveSettings(), during the regular XML writing;
9982 *
9983 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9984 * exported to OVF and we write the VirtualBox proprietary XML
9985 * into a <vbox:Machine> tag.
9986 *
9987 * This routine fills all the fields in there, including snapshots, *except*
9988 * for the following:
9989 *
9990 * -- fCurrentStateModified. There is some special logic associated with that.
9991 *
9992 * The caller can then call MachineConfigFile::write() or do something else
9993 * with it.
9994 *
9995 * Caller must hold the machine lock!
9996 *
9997 * This throws XML errors and HRESULT, so the caller must have a catch block!
9998 */
9999void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10000{
10001 // deep copy extradata, being extra careful with self assignment (the STL
10002 // map assignment on Mac OS X clang based Xcode isn't checking)
10003 if (&config != mData->pMachineConfigFile)
10004 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10005
10006 config.uuid = mData->mUuid;
10007
10008 // copy name, description, OS type, teleport, UTC etc.
10009 config.machineUserData = mUserData->s;
10010
10011 if ( mData->mMachineState == MachineState_Saved
10012 || mData->mMachineState == MachineState_Restoring
10013 // when doing certain snapshot operations we may or may not have
10014 // a saved state in the current state, so keep everything as is
10015 || ( ( mData->mMachineState == MachineState_Snapshotting
10016 || mData->mMachineState == MachineState_DeletingSnapshot
10017 || mData->mMachineState == MachineState_RestoringSnapshot)
10018 && (!mSSData->strStateFilePath.isEmpty())
10019 )
10020 )
10021 {
10022 Assert(!mSSData->strStateFilePath.isEmpty());
10023 /* try to make the file name relative to the settings file dir */
10024 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10025 }
10026 else
10027 {
10028 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10029 config.strStateFile.setNull();
10030 }
10031
10032 if (mData->mCurrentSnapshot)
10033 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10034 else
10035 config.uuidCurrentSnapshot.clear();
10036
10037 config.timeLastStateChange = mData->mLastStateChange;
10038 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10039 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10040
10041 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10042 if (FAILED(rc)) throw rc;
10043
10044 // save machine's media registry if this is VirtualBox 4.0 or later
10045 if (config.canHaveOwnMediaRegistry())
10046 {
10047 // determine machine folder
10048 Utf8Str strMachineFolder = i_getSettingsFileFull();
10049 strMachineFolder.stripFilename();
10050 mParent->i_saveMediaRegistry(config.mediaRegistry,
10051 i_getId(), // only media with registry ID == machine UUID
10052 strMachineFolder);
10053 // this throws HRESULT
10054 }
10055
10056 // save snapshots
10057 rc = i_saveAllSnapshots(config);
10058 if (FAILED(rc)) throw rc;
10059}
10060
10061/**
10062 * Saves all snapshots of the machine into the given machine config file. Called
10063 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10064 * @param config
10065 * @return
10066 */
10067HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10068{
10069 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10070
10071 HRESULT rc = S_OK;
10072
10073 try
10074 {
10075 config.llFirstSnapshot.clear();
10076
10077 if (mData->mFirstSnapshot)
10078 {
10079 // the settings use a list for "the first snapshot"
10080 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10081
10082 // get reference to the snapshot on the list and work on that
10083 // element straight in the list to avoid excessive copying later
10084 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10085 if (FAILED(rc)) throw rc;
10086 }
10087
10088// if (mType == IsSessionMachine)
10089// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10090
10091 }
10092 catch (HRESULT err)
10093 {
10094 /* we assume that error info is set by the thrower */
10095 rc = err;
10096 }
10097 catch (...)
10098 {
10099 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10100 }
10101
10102 return rc;
10103}
10104
10105/**
10106 * Saves the VM hardware configuration. It is assumed that the
10107 * given node is empty.
10108 *
10109 * @param data Reference to the settings object for the hardware config.
10110 * @param pDbg Pointer to the settings object for the debugging config
10111 * which happens to live in mHWData.
10112 * @param pAutostart Pointer to the settings object for the autostart config
10113 * which happens to live in mHWData.
10114 */
10115HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10116 settings::Autostart *pAutostart)
10117{
10118 HRESULT rc = S_OK;
10119
10120 try
10121 {
10122 /* The hardware version attribute (optional).
10123 Automatically upgrade from 1 to current default hardware version
10124 when there is no saved state. (ugly!) */
10125 if ( mHWData->mHWVersion == "1"
10126 && mSSData->strStateFilePath.isEmpty()
10127 )
10128 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10129
10130 data.strVersion = mHWData->mHWVersion;
10131 data.uuid = mHWData->mHardwareUUID;
10132
10133 // CPU
10134 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10135 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10136 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10137 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10138 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10139 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10140 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10141 data.fPAE = !!mHWData->mPAEEnabled;
10142 data.enmLongMode = mHWData->mLongMode;
10143 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10144 data.fAPIC = !!mHWData->mAPIC;
10145 data.fX2APIC = !!mHWData->mX2APIC;
10146 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10147 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10148 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10149 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10150 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10151 data.cCPUs = mHWData->mCPUCount;
10152 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10153 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10154 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10155 data.strCpuProfile = mHWData->mCpuProfile;
10156
10157 data.llCpus.clear();
10158 if (data.fCpuHotPlug)
10159 {
10160 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10161 {
10162 if (mHWData->mCPUAttached[idx])
10163 {
10164 settings::Cpu cpu;
10165 cpu.ulId = idx;
10166 data.llCpus.push_back(cpu);
10167 }
10168 }
10169 }
10170
10171 /* Standard and Extended CPUID leafs. */
10172 data.llCpuIdLeafs.clear();
10173 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10174
10175 // memory
10176 data.ulMemorySizeMB = mHWData->mMemorySize;
10177 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10178
10179 // firmware
10180 data.firmwareType = mHWData->mFirmwareType;
10181
10182 // HID
10183 data.pointingHIDType = mHWData->mPointingHIDType;
10184 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10185
10186 // chipset
10187 data.chipsetType = mHWData->mChipsetType;
10188
10189 // paravirt
10190 data.paravirtProvider = mHWData->mParavirtProvider;
10191 data.strParavirtDebug = mHWData->mParavirtDebug;
10192
10193 // emulated USB card reader
10194 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10195
10196 // HPET
10197 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10198
10199 // boot order
10200 data.mapBootOrder.clear();
10201 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10202 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10203
10204 // display
10205 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10206 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10207 data.cMonitors = mHWData->mMonitorCount;
10208 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10209 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10210
10211 /* VRDEServer settings (optional) */
10212 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10213 if (FAILED(rc)) throw rc;
10214
10215 /* BIOS settings (required) */
10216 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10217 if (FAILED(rc)) throw rc;
10218
10219 /* Capture settings (required) */
10220 rc = mCaptureSettings->i_saveSettings(data.captureSettings);
10221 if (FAILED(rc)) throw rc;
10222
10223 /* USB Controller (required) */
10224 data.usbSettings.llUSBControllers.clear();
10225 for (USBControllerList::const_iterator
10226 it = mUSBControllers->begin();
10227 it != mUSBControllers->end();
10228 ++it)
10229 {
10230 ComObjPtr<USBController> ctrl = *it;
10231 settings::USBController settingsCtrl;
10232
10233 settingsCtrl.strName = ctrl->i_getName();
10234 settingsCtrl.enmType = ctrl->i_getControllerType();
10235
10236 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10237 }
10238
10239 /* USB device filters (required) */
10240 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10241 if (FAILED(rc)) throw rc;
10242
10243 /* Network adapters (required) */
10244 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10245 data.llNetworkAdapters.clear();
10246 /* Write out only the nominal number of network adapters for this
10247 * chipset type. Since Machine::commit() hasn't been called there
10248 * may be extra NIC settings in the vector. */
10249 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10250 {
10251 settings::NetworkAdapter nic;
10252 nic.ulSlot = (uint32_t)slot;
10253 /* paranoia check... must not be NULL, but must not crash either. */
10254 if (mNetworkAdapters[slot])
10255 {
10256 if (mNetworkAdapters[slot]->i_hasDefaults())
10257 continue;
10258
10259 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10260 if (FAILED(rc)) throw rc;
10261
10262 data.llNetworkAdapters.push_back(nic);
10263 }
10264 }
10265
10266 /* Serial ports */
10267 data.llSerialPorts.clear();
10268 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10269 {
10270 if (mSerialPorts[slot]->i_hasDefaults())
10271 continue;
10272
10273 settings::SerialPort s;
10274 s.ulSlot = slot;
10275 rc = mSerialPorts[slot]->i_saveSettings(s);
10276 if (FAILED(rc)) return rc;
10277
10278 data.llSerialPorts.push_back(s);
10279 }
10280
10281 /* Parallel ports */
10282 data.llParallelPorts.clear();
10283 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10284 {
10285 if (mParallelPorts[slot]->i_hasDefaults())
10286 continue;
10287
10288 settings::ParallelPort p;
10289 p.ulSlot = slot;
10290 rc = mParallelPorts[slot]->i_saveSettings(p);
10291 if (FAILED(rc)) return rc;
10292
10293 data.llParallelPorts.push_back(p);
10294 }
10295
10296 /* Audio adapter */
10297 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10298 if (FAILED(rc)) return rc;
10299
10300 rc = i_saveStorageControllers(data.storage);
10301 if (FAILED(rc)) return rc;
10302
10303 /* Shared folders */
10304 data.llSharedFolders.clear();
10305 for (HWData::SharedFolderList::const_iterator
10306 it = mHWData->mSharedFolders.begin();
10307 it != mHWData->mSharedFolders.end();
10308 ++it)
10309 {
10310 SharedFolder *pSF = *it;
10311 AutoCaller sfCaller(pSF);
10312 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10313 settings::SharedFolder sf;
10314 sf.strName = pSF->i_getName();
10315 sf.strHostPath = pSF->i_getHostPath();
10316 sf.fWritable = !!pSF->i_isWritable();
10317 sf.fAutoMount = !!pSF->i_isAutoMounted();
10318
10319 data.llSharedFolders.push_back(sf);
10320 }
10321
10322 // clipboard
10323 data.clipboardMode = mHWData->mClipboardMode;
10324
10325 // drag'n'drop
10326 data.dndMode = mHWData->mDnDMode;
10327
10328 /* Guest */
10329 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10330
10331 // IO settings
10332 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10333 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10334
10335 /* BandwidthControl (required) */
10336 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10337 if (FAILED(rc)) throw rc;
10338
10339 /* Host PCI devices */
10340 data.pciAttachments.clear();
10341 for (HWData::PCIDeviceAssignmentList::const_iterator
10342 it = mHWData->mPCIDeviceAssignments.begin();
10343 it != mHWData->mPCIDeviceAssignments.end();
10344 ++it)
10345 {
10346 ComObjPtr<PCIDeviceAttachment> pda = *it;
10347 settings::HostPCIDeviceAttachment hpda;
10348
10349 rc = pda->i_saveSettings(hpda);
10350 if (FAILED(rc)) throw rc;
10351
10352 data.pciAttachments.push_back(hpda);
10353 }
10354
10355 // guest properties
10356 data.llGuestProperties.clear();
10357#ifdef VBOX_WITH_GUEST_PROPS
10358 for (HWData::GuestPropertyMap::const_iterator
10359 it = mHWData->mGuestProperties.begin();
10360 it != mHWData->mGuestProperties.end();
10361 ++it)
10362 {
10363 HWData::GuestProperty property = it->second;
10364
10365 /* Remove transient guest properties at shutdown unless we
10366 * are saving state. Note that restoring snapshot intentionally
10367 * keeps them, they will be removed if appropriate once the final
10368 * machine state is set (as crashes etc. need to work). */
10369 if ( ( mData->mMachineState == MachineState_PoweredOff
10370 || mData->mMachineState == MachineState_Aborted
10371 || mData->mMachineState == MachineState_Teleported)
10372 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10373 continue;
10374 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10375 prop.strName = it->first;
10376 prop.strValue = property.strValue;
10377 prop.timestamp = property.mTimestamp;
10378 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10379 GuestPropWriteFlags(property.mFlags, szFlags);
10380 prop.strFlags = szFlags;
10381
10382 data.llGuestProperties.push_back(prop);
10383 }
10384
10385 /* I presume this doesn't require a backup(). */
10386 mData->mGuestPropertiesModified = FALSE;
10387#endif /* VBOX_WITH_GUEST_PROPS defined */
10388
10389 *pDbg = mHWData->mDebugging;
10390 *pAutostart = mHWData->mAutostart;
10391
10392 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10393 }
10394 catch (std::bad_alloc &)
10395 {
10396 return E_OUTOFMEMORY;
10397 }
10398
10399 AssertComRC(rc);
10400 return rc;
10401}
10402
10403/**
10404 * Saves the storage controller configuration.
10405 *
10406 * @param data storage settings.
10407 */
10408HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10409{
10410 data.llStorageControllers.clear();
10411
10412 for (StorageControllerList::const_iterator
10413 it = mStorageControllers->begin();
10414 it != mStorageControllers->end();
10415 ++it)
10416 {
10417 HRESULT rc;
10418 ComObjPtr<StorageController> pCtl = *it;
10419
10420 settings::StorageController ctl;
10421 ctl.strName = pCtl->i_getName();
10422 ctl.controllerType = pCtl->i_getControllerType();
10423 ctl.storageBus = pCtl->i_getStorageBus();
10424 ctl.ulInstance = pCtl->i_getInstance();
10425 ctl.fBootable = pCtl->i_getBootable();
10426
10427 /* Save the port count. */
10428 ULONG portCount;
10429 rc = pCtl->COMGETTER(PortCount)(&portCount);
10430 ComAssertComRCRet(rc, rc);
10431 ctl.ulPortCount = portCount;
10432
10433 /* Save fUseHostIOCache */
10434 BOOL fUseHostIOCache;
10435 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10436 ComAssertComRCRet(rc, rc);
10437 ctl.fUseHostIOCache = !!fUseHostIOCache;
10438
10439 /* save the devices now. */
10440 rc = i_saveStorageDevices(pCtl, ctl);
10441 ComAssertComRCRet(rc, rc);
10442
10443 data.llStorageControllers.push_back(ctl);
10444 }
10445
10446 return S_OK;
10447}
10448
10449/**
10450 * Saves the hard disk configuration.
10451 */
10452HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10453 settings::StorageController &data)
10454{
10455 MediumAttachmentList atts;
10456
10457 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10458 if (FAILED(rc)) return rc;
10459
10460 data.llAttachedDevices.clear();
10461 for (MediumAttachmentList::const_iterator
10462 it = atts.begin();
10463 it != atts.end();
10464 ++it)
10465 {
10466 settings::AttachedDevice dev;
10467 IMediumAttachment *iA = *it;
10468 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10469 Medium *pMedium = pAttach->i_getMedium();
10470
10471 dev.deviceType = pAttach->i_getType();
10472 dev.lPort = pAttach->i_getPort();
10473 dev.lDevice = pAttach->i_getDevice();
10474 dev.fPassThrough = pAttach->i_getPassthrough();
10475 dev.fHotPluggable = pAttach->i_getHotPluggable();
10476 if (pMedium)
10477 {
10478 if (pMedium->i_isHostDrive())
10479 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10480 else
10481 dev.uuid = pMedium->i_getId();
10482 dev.fTempEject = pAttach->i_getTempEject();
10483 dev.fNonRotational = pAttach->i_getNonRotational();
10484 dev.fDiscard = pAttach->i_getDiscard();
10485 }
10486
10487 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10488
10489 data.llAttachedDevices.push_back(dev);
10490 }
10491
10492 return S_OK;
10493}
10494
10495/**
10496 * Saves machine state settings as defined by aFlags
10497 * (SaveSTS_* values).
10498 *
10499 * @param aFlags Combination of SaveSTS_* flags.
10500 *
10501 * @note Locks objects for writing.
10502 */
10503HRESULT Machine::i_saveStateSettings(int aFlags)
10504{
10505 if (aFlags == 0)
10506 return S_OK;
10507
10508 AutoCaller autoCaller(this);
10509 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10510
10511 /* This object's write lock is also necessary to serialize file access
10512 * (prevent concurrent reads and writes) */
10513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10514
10515 HRESULT rc = S_OK;
10516
10517 Assert(mData->pMachineConfigFile);
10518
10519 try
10520 {
10521 if (aFlags & SaveSTS_CurStateModified)
10522 mData->pMachineConfigFile->fCurrentStateModified = true;
10523
10524 if (aFlags & SaveSTS_StateFilePath)
10525 {
10526 if (!mSSData->strStateFilePath.isEmpty())
10527 /* try to make the file name relative to the settings file dir */
10528 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10529 else
10530 mData->pMachineConfigFile->strStateFile.setNull();
10531 }
10532
10533 if (aFlags & SaveSTS_StateTimeStamp)
10534 {
10535 Assert( mData->mMachineState != MachineState_Aborted
10536 || mSSData->strStateFilePath.isEmpty());
10537
10538 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10539
10540 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10541/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10542 }
10543
10544 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10545 }
10546 catch (...)
10547 {
10548 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10549 }
10550
10551 return rc;
10552}
10553
10554/**
10555 * Ensures that the given medium is added to a media registry. If this machine
10556 * was created with 4.0 or later, then the machine registry is used. Otherwise
10557 * the global VirtualBox media registry is used.
10558 *
10559 * Caller must NOT hold machine lock, media tree or any medium locks!
10560 *
10561 * @param pMedium
10562 */
10563void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10564{
10565 /* Paranoia checks: do not hold machine or media tree locks. */
10566 AssertReturnVoid(!isWriteLockOnCurrentThread());
10567 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10568
10569 ComObjPtr<Medium> pBase;
10570 {
10571 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10572 pBase = pMedium->i_getBase();
10573 }
10574
10575 /* Paranoia checks: do not hold medium locks. */
10576 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10577 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10578
10579 // decide which medium registry to use now that the medium is attached:
10580 Guid uuid;
10581 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10582 if (fCanHaveOwnMediaRegistry)
10583 // machine XML is VirtualBox 4.0 or higher:
10584 uuid = i_getId(); // machine UUID
10585 else
10586 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10587
10588 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10589 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10590 if (pMedium->i_addRegistry(uuid))
10591 mParent->i_markRegistryModified(uuid);
10592
10593 /* For more complex hard disk structures it can happen that the base
10594 * medium isn't yet associated with any medium registry. Do that now. */
10595 if (pMedium != pBase)
10596 {
10597 /* Tree lock needed by Medium::addRegistry when recursing. */
10598 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10599 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10600 {
10601 treeLock.release();
10602 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10603 treeLock.acquire();
10604 }
10605 if (pBase->i_addRegistryRecursive(uuid))
10606 {
10607 treeLock.release();
10608 mParent->i_markRegistryModified(uuid);
10609 }
10610 }
10611}
10612
10613/**
10614 * Creates differencing hard disks for all normal hard disks attached to this
10615 * machine and a new set of attachments to refer to created disks.
10616 *
10617 * Used when taking a snapshot or when deleting the current state. Gets called
10618 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10619 *
10620 * This method assumes that mMediumAttachments contains the original hard disk
10621 * attachments it needs to create diffs for. On success, these attachments will
10622 * be replaced with the created diffs.
10623 *
10624 * Attachments with non-normal hard disks are left as is.
10625 *
10626 * If @a aOnline is @c false then the original hard disks that require implicit
10627 * diffs will be locked for reading. Otherwise it is assumed that they are
10628 * already locked for writing (when the VM was started). Note that in the latter
10629 * case it is responsibility of the caller to lock the newly created diffs for
10630 * writing if this method succeeds.
10631 *
10632 * @param aProgress Progress object to run (must contain at least as
10633 * many operations left as the number of hard disks
10634 * attached).
10635 * @param aWeight Weight of this operation.
10636 * @param aOnline Whether the VM was online prior to this operation.
10637 *
10638 * @note The progress object is not marked as completed, neither on success nor
10639 * on failure. This is a responsibility of the caller.
10640 *
10641 * @note Locks this object and the media tree for writing.
10642 */
10643HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10644 ULONG aWeight,
10645 bool aOnline)
10646{
10647 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10648
10649 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10650 AssertReturn(!!pProgressControl, E_INVALIDARG);
10651
10652 AutoCaller autoCaller(this);
10653 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10654
10655 AutoMultiWriteLock2 alock(this->lockHandle(),
10656 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10657
10658 /* must be in a protective state because we release the lock below */
10659 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10660 || mData->mMachineState == MachineState_OnlineSnapshotting
10661 || mData->mMachineState == MachineState_LiveSnapshotting
10662 || mData->mMachineState == MachineState_RestoringSnapshot
10663 || mData->mMachineState == MachineState_DeletingSnapshot
10664 , E_FAIL);
10665
10666 HRESULT rc = S_OK;
10667
10668 // use appropriate locked media map (online or offline)
10669 MediumLockListMap lockedMediaOffline;
10670 MediumLockListMap *lockedMediaMap;
10671 if (aOnline)
10672 lockedMediaMap = &mData->mSession.mLockedMedia;
10673 else
10674 lockedMediaMap = &lockedMediaOffline;
10675
10676 try
10677 {
10678 if (!aOnline)
10679 {
10680 /* lock all attached hard disks early to detect "in use"
10681 * situations before creating actual diffs */
10682 for (MediumAttachmentList::const_iterator
10683 it = mMediumAttachments->begin();
10684 it != mMediumAttachments->end();
10685 ++it)
10686 {
10687 MediumAttachment *pAtt = *it;
10688 if (pAtt->i_getType() == DeviceType_HardDisk)
10689 {
10690 Medium *pMedium = pAtt->i_getMedium();
10691 Assert(pMedium);
10692
10693 MediumLockList *pMediumLockList(new MediumLockList());
10694 alock.release();
10695 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10696 NULL /* pToLockWrite */,
10697 false /* fMediumLockWriteAll */,
10698 NULL,
10699 *pMediumLockList);
10700 alock.acquire();
10701 if (FAILED(rc))
10702 {
10703 delete pMediumLockList;
10704 throw rc;
10705 }
10706 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10707 if (FAILED(rc))
10708 {
10709 throw setError(rc,
10710 tr("Collecting locking information for all attached media failed"));
10711 }
10712 }
10713 }
10714
10715 /* Now lock all media. If this fails, nothing is locked. */
10716 alock.release();
10717 rc = lockedMediaMap->Lock();
10718 alock.acquire();
10719 if (FAILED(rc))
10720 {
10721 throw setError(rc,
10722 tr("Locking of attached media failed"));
10723 }
10724 }
10725
10726 /* remember the current list (note that we don't use backup() since
10727 * mMediumAttachments may be already backed up) */
10728 MediumAttachmentList atts = *mMediumAttachments.data();
10729
10730 /* start from scratch */
10731 mMediumAttachments->clear();
10732
10733 /* go through remembered attachments and create diffs for normal hard
10734 * disks and attach them */
10735 for (MediumAttachmentList::const_iterator
10736 it = atts.begin();
10737 it != atts.end();
10738 ++it)
10739 {
10740 MediumAttachment *pAtt = *it;
10741
10742 DeviceType_T devType = pAtt->i_getType();
10743 Medium *pMedium = pAtt->i_getMedium();
10744
10745 if ( devType != DeviceType_HardDisk
10746 || pMedium == NULL
10747 || pMedium->i_getType() != MediumType_Normal)
10748 {
10749 /* copy the attachment as is */
10750
10751 /** @todo the progress object created in SessionMachine::TakeSnaphot
10752 * only expects operations for hard disks. Later other
10753 * device types need to show up in the progress as well. */
10754 if (devType == DeviceType_HardDisk)
10755 {
10756 if (pMedium == NULL)
10757 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10758 aWeight); // weight
10759 else
10760 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10761 pMedium->i_getBase()->i_getName().c_str()).raw(),
10762 aWeight); // weight
10763 }
10764
10765 mMediumAttachments->push_back(pAtt);
10766 continue;
10767 }
10768
10769 /* need a diff */
10770 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10771 pMedium->i_getBase()->i_getName().c_str()).raw(),
10772 aWeight); // weight
10773
10774 Utf8Str strFullSnapshotFolder;
10775 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10776
10777 ComObjPtr<Medium> diff;
10778 diff.createObject();
10779 // store the diff in the same registry as the parent
10780 // (this cannot fail here because we can't create implicit diffs for
10781 // unregistered images)
10782 Guid uuidRegistryParent;
10783 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10784 Assert(fInRegistry); NOREF(fInRegistry);
10785 rc = diff->init(mParent,
10786 pMedium->i_getPreferredDiffFormat(),
10787 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10788 uuidRegistryParent,
10789 DeviceType_HardDisk);
10790 if (FAILED(rc)) throw rc;
10791
10792 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10793 * the push_back? Looks like we're going to release medium with the
10794 * wrong kind of lock (general issue with if we fail anywhere at all)
10795 * and an orphaned VDI in the snapshots folder. */
10796
10797 /* update the appropriate lock list */
10798 MediumLockList *pMediumLockList;
10799 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10800 AssertComRCThrowRC(rc);
10801 if (aOnline)
10802 {
10803 alock.release();
10804 /* The currently attached medium will be read-only, change
10805 * the lock type to read. */
10806 rc = pMediumLockList->Update(pMedium, false);
10807 alock.acquire();
10808 AssertComRCThrowRC(rc);
10809 }
10810
10811 /* release the locks before the potentially lengthy operation */
10812 alock.release();
10813 rc = pMedium->i_createDiffStorage(diff,
10814 pMedium->i_getPreferredDiffVariant(),
10815 pMediumLockList,
10816 NULL /* aProgress */,
10817 true /* aWait */);
10818 alock.acquire();
10819 if (FAILED(rc)) throw rc;
10820
10821 /* actual lock list update is done in Machine::i_commitMedia */
10822
10823 rc = diff->i_addBackReference(mData->mUuid);
10824 AssertComRCThrowRC(rc);
10825
10826 /* add a new attachment */
10827 ComObjPtr<MediumAttachment> attachment;
10828 attachment.createObject();
10829 rc = attachment->init(this,
10830 diff,
10831 pAtt->i_getControllerName(),
10832 pAtt->i_getPort(),
10833 pAtt->i_getDevice(),
10834 DeviceType_HardDisk,
10835 true /* aImplicit */,
10836 false /* aPassthrough */,
10837 false /* aTempEject */,
10838 pAtt->i_getNonRotational(),
10839 pAtt->i_getDiscard(),
10840 pAtt->i_getHotPluggable(),
10841 pAtt->i_getBandwidthGroup());
10842 if (FAILED(rc)) throw rc;
10843
10844 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10845 AssertComRCThrowRC(rc);
10846 mMediumAttachments->push_back(attachment);
10847 }
10848 }
10849 catch (HRESULT aRC) { rc = aRC; }
10850
10851 /* unlock all hard disks we locked when there is no VM */
10852 if (!aOnline)
10853 {
10854 ErrorInfoKeeper eik;
10855
10856 HRESULT rc1 = lockedMediaMap->Clear();
10857 AssertComRC(rc1);
10858 }
10859
10860 return rc;
10861}
10862
10863/**
10864 * Deletes implicit differencing hard disks created either by
10865 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10866 * mMediumAttachments.
10867 *
10868 * Note that to delete hard disks created by #attachDevice() this method is
10869 * called from #i_rollbackMedia() when the changes are rolled back.
10870 *
10871 * @note Locks this object and the media tree for writing.
10872 */
10873HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10874{
10875 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10876
10877 AutoCaller autoCaller(this);
10878 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10879
10880 AutoMultiWriteLock2 alock(this->lockHandle(),
10881 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10882
10883 /* We absolutely must have backed up state. */
10884 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10885
10886 /* Check if there are any implicitly created diff images. */
10887 bool fImplicitDiffs = false;
10888 for (MediumAttachmentList::const_iterator
10889 it = mMediumAttachments->begin();
10890 it != mMediumAttachments->end();
10891 ++it)
10892 {
10893 const ComObjPtr<MediumAttachment> &pAtt = *it;
10894 if (pAtt->i_isImplicit())
10895 {
10896 fImplicitDiffs = true;
10897 break;
10898 }
10899 }
10900 /* If there is nothing to do, leave early. This saves lots of image locking
10901 * effort. It also avoids a MachineStateChanged event without real reason.
10902 * This is important e.g. when loading a VM config, because there should be
10903 * no events. Otherwise API clients can become thoroughly confused for
10904 * inaccessible VMs (the code for loading VM configs uses this method for
10905 * cleanup if the config makes no sense), as they take such events as an
10906 * indication that the VM is alive, and they would force the VM config to
10907 * be reread, leading to an endless loop. */
10908 if (!fImplicitDiffs)
10909 return S_OK;
10910
10911 HRESULT rc = S_OK;
10912 MachineState_T oldState = mData->mMachineState;
10913
10914 /* will release the lock before the potentially lengthy operation,
10915 * so protect with the special state (unless already protected) */
10916 if ( oldState != MachineState_Snapshotting
10917 && oldState != MachineState_OnlineSnapshotting
10918 && oldState != MachineState_LiveSnapshotting
10919 && oldState != MachineState_RestoringSnapshot
10920 && oldState != MachineState_DeletingSnapshot
10921 && oldState != MachineState_DeletingSnapshotOnline
10922 && oldState != MachineState_DeletingSnapshotPaused
10923 )
10924 i_setMachineState(MachineState_SettingUp);
10925
10926 // use appropriate locked media map (online or offline)
10927 MediumLockListMap lockedMediaOffline;
10928 MediumLockListMap *lockedMediaMap;
10929 if (aOnline)
10930 lockedMediaMap = &mData->mSession.mLockedMedia;
10931 else
10932 lockedMediaMap = &lockedMediaOffline;
10933
10934 try
10935 {
10936 if (!aOnline)
10937 {
10938 /* lock all attached hard disks early to detect "in use"
10939 * situations before deleting actual diffs */
10940 for (MediumAttachmentList::const_iterator
10941 it = mMediumAttachments->begin();
10942 it != mMediumAttachments->end();
10943 ++it)
10944 {
10945 MediumAttachment *pAtt = *it;
10946 if (pAtt->i_getType() == DeviceType_HardDisk)
10947 {
10948 Medium *pMedium = pAtt->i_getMedium();
10949 Assert(pMedium);
10950
10951 MediumLockList *pMediumLockList(new MediumLockList());
10952 alock.release();
10953 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10954 NULL /* pToLockWrite */,
10955 false /* fMediumLockWriteAll */,
10956 NULL,
10957 *pMediumLockList);
10958 alock.acquire();
10959
10960 if (FAILED(rc))
10961 {
10962 delete pMediumLockList;
10963 throw rc;
10964 }
10965
10966 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10967 if (FAILED(rc))
10968 throw rc;
10969 }
10970 }
10971
10972 if (FAILED(rc))
10973 throw rc;
10974 } // end of offline
10975
10976 /* Lock lists are now up to date and include implicitly created media */
10977
10978 /* Go through remembered attachments and delete all implicitly created
10979 * diffs and fix up the attachment information */
10980 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10981 MediumAttachmentList implicitAtts;
10982 for (MediumAttachmentList::const_iterator
10983 it = mMediumAttachments->begin();
10984 it != mMediumAttachments->end();
10985 ++it)
10986 {
10987 ComObjPtr<MediumAttachment> pAtt = *it;
10988 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10989 if (pMedium.isNull())
10990 continue;
10991
10992 // Implicit attachments go on the list for deletion and back references are removed.
10993 if (pAtt->i_isImplicit())
10994 {
10995 /* Deassociate and mark for deletion */
10996 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10997 rc = pMedium->i_removeBackReference(mData->mUuid);
10998 if (FAILED(rc))
10999 throw rc;
11000 implicitAtts.push_back(pAtt);
11001 continue;
11002 }
11003
11004 /* Was this medium attached before? */
11005 if (!i_findAttachment(oldAtts, pMedium))
11006 {
11007 /* no: de-associate */
11008 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11009 rc = pMedium->i_removeBackReference(mData->mUuid);
11010 if (FAILED(rc))
11011 throw rc;
11012 continue;
11013 }
11014 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11015 }
11016
11017 /* If there are implicit attachments to delete, throw away the lock
11018 * map contents (which will unlock all media) since the medium
11019 * attachments will be rolled back. Below we need to completely
11020 * recreate the lock map anyway since it is infinitely complex to
11021 * do this incrementally (would need reconstructing each attachment
11022 * change, which would be extremely hairy). */
11023 if (implicitAtts.size() != 0)
11024 {
11025 ErrorInfoKeeper eik;
11026
11027 HRESULT rc1 = lockedMediaMap->Clear();
11028 AssertComRC(rc1);
11029 }
11030
11031 /* rollback hard disk changes */
11032 mMediumAttachments.rollback();
11033
11034 MultiResult mrc(S_OK);
11035
11036 // Delete unused implicit diffs.
11037 if (implicitAtts.size() != 0)
11038 {
11039 alock.release();
11040
11041 for (MediumAttachmentList::const_iterator
11042 it = implicitAtts.begin();
11043 it != implicitAtts.end();
11044 ++it)
11045 {
11046 // Remove medium associated with this attachment.
11047 ComObjPtr<MediumAttachment> pAtt = *it;
11048 Assert(pAtt);
11049 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11050 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11051 Assert(pMedium);
11052
11053 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11054 // continue on delete failure, just collect error messages
11055 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11056 pMedium->i_getLocationFull().c_str() ));
11057 mrc = rc;
11058 }
11059 // Clear the list of deleted implicit attachments now, while not
11060 // holding the lock, as it will ultimately trigger Medium::uninit()
11061 // calls which assume that the media tree lock isn't held.
11062 implicitAtts.clear();
11063
11064 alock.acquire();
11065
11066 /* if there is a VM recreate media lock map as mentioned above,
11067 * otherwise it is a waste of time and we leave things unlocked */
11068 if (aOnline)
11069 {
11070 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11071 /* must never be NULL, but better safe than sorry */
11072 if (!pMachine.isNull())
11073 {
11074 alock.release();
11075 rc = mData->mSession.mMachine->i_lockMedia();
11076 alock.acquire();
11077 if (FAILED(rc))
11078 throw rc;
11079 }
11080 }
11081 }
11082 }
11083 catch (HRESULT aRC) {rc = aRC;}
11084
11085 if (mData->mMachineState == MachineState_SettingUp)
11086 i_setMachineState(oldState);
11087
11088 /* unlock all hard disks we locked when there is no VM */
11089 if (!aOnline)
11090 {
11091 ErrorInfoKeeper eik;
11092
11093 HRESULT rc1 = lockedMediaMap->Clear();
11094 AssertComRC(rc1);
11095 }
11096
11097 return rc;
11098}
11099
11100
11101/**
11102 * Looks through the given list of media attachments for one with the given parameters
11103 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11104 * can be searched as well if needed.
11105 *
11106 * @param ll
11107 * @param aControllerName
11108 * @param aControllerPort
11109 * @param aDevice
11110 * @return
11111 */
11112MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11113 const Utf8Str &aControllerName,
11114 LONG aControllerPort,
11115 LONG aDevice)
11116{
11117 for (MediumAttachmentList::const_iterator
11118 it = ll.begin();
11119 it != ll.end();
11120 ++it)
11121 {
11122 MediumAttachment *pAttach = *it;
11123 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11124 return pAttach;
11125 }
11126
11127 return NULL;
11128}
11129
11130/**
11131 * Looks through the given list of media attachments for one with the given parameters
11132 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11133 * can be searched as well if needed.
11134 *
11135 * @param ll
11136 * @param pMedium
11137 * @return
11138 */
11139MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11140 ComObjPtr<Medium> pMedium)
11141{
11142 for (MediumAttachmentList::const_iterator
11143 it = ll.begin();
11144 it != ll.end();
11145 ++it)
11146 {
11147 MediumAttachment *pAttach = *it;
11148 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11149 if (pMediumThis == pMedium)
11150 return pAttach;
11151 }
11152
11153 return NULL;
11154}
11155
11156/**
11157 * Looks through the given list of media attachments for one with the given parameters
11158 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11159 * can be searched as well if needed.
11160 *
11161 * @param ll
11162 * @param id
11163 * @return
11164 */
11165MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11166 Guid &id)
11167{
11168 for (MediumAttachmentList::const_iterator
11169 it = ll.begin();
11170 it != ll.end();
11171 ++it)
11172 {
11173 MediumAttachment *pAttach = *it;
11174 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11175 if (pMediumThis->i_getId() == id)
11176 return pAttach;
11177 }
11178
11179 return NULL;
11180}
11181
11182/**
11183 * Main implementation for Machine::DetachDevice. This also gets called
11184 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11185 *
11186 * @param pAttach Medium attachment to detach.
11187 * @param writeLock Machine write lock which the caller must have locked once.
11188 * This may be released temporarily in here.
11189 * @param pSnapshot If NULL, then the detachment is for the current machine.
11190 * Otherwise this is for a SnapshotMachine, and this must be
11191 * its snapshot.
11192 * @return
11193 */
11194HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11195 AutoWriteLock &writeLock,
11196 Snapshot *pSnapshot)
11197{
11198 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11199 DeviceType_T mediumType = pAttach->i_getType();
11200
11201 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11202
11203 if (pAttach->i_isImplicit())
11204 {
11205 /* attempt to implicitly delete the implicitly created diff */
11206
11207 /// @todo move the implicit flag from MediumAttachment to Medium
11208 /// and forbid any hard disk operation when it is implicit. Or maybe
11209 /// a special media state for it to make it even more simple.
11210
11211 Assert(mMediumAttachments.isBackedUp());
11212
11213 /* will release the lock before the potentially lengthy operation, so
11214 * protect with the special state */
11215 MachineState_T oldState = mData->mMachineState;
11216 i_setMachineState(MachineState_SettingUp);
11217
11218 writeLock.release();
11219
11220 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11221 true /*aWait*/);
11222
11223 writeLock.acquire();
11224
11225 i_setMachineState(oldState);
11226
11227 if (FAILED(rc)) return rc;
11228 }
11229
11230 i_setModified(IsModified_Storage);
11231 mMediumAttachments.backup();
11232 mMediumAttachments->remove(pAttach);
11233
11234 if (!oldmedium.isNull())
11235 {
11236 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11237 if (pSnapshot)
11238 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11239 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11240 else if (mediumType != DeviceType_HardDisk)
11241 oldmedium->i_removeBackReference(mData->mUuid);
11242 }
11243
11244 return S_OK;
11245}
11246
11247/**
11248 * Goes thru all media of the given list and
11249 *
11250 * 1) calls i_detachDevice() on each of them for this machine and
11251 * 2) adds all Medium objects found in the process to the given list,
11252 * depending on cleanupMode.
11253 *
11254 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11255 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11256 * media to the list.
11257 *
11258 * This gets called from Machine::Unregister, both for the actual Machine and
11259 * the SnapshotMachine objects that might be found in the snapshots.
11260 *
11261 * Requires caller and locking. The machine lock must be passed in because it
11262 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11263 *
11264 * @param writeLock Machine lock from top-level caller; this gets passed to
11265 * i_detachDevice.
11266 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11267 * object if called for a SnapshotMachine.
11268 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11269 * added to llMedia; if Full, then all media get added;
11270 * otherwise no media get added.
11271 * @param llMedia Caller's list to receive Medium objects which got detached so
11272 * caller can close() them, depending on cleanupMode.
11273 * @return
11274 */
11275HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11276 Snapshot *pSnapshot,
11277 CleanupMode_T cleanupMode,
11278 MediaList &llMedia)
11279{
11280 Assert(isWriteLockOnCurrentThread());
11281
11282 HRESULT rc;
11283
11284 // make a temporary list because i_detachDevice invalidates iterators into
11285 // mMediumAttachments
11286 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11287
11288 for (MediumAttachmentList::iterator
11289 it = llAttachments2.begin();
11290 it != llAttachments2.end();
11291 ++it)
11292 {
11293 ComObjPtr<MediumAttachment> &pAttach = *it;
11294 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11295
11296 if (!pMedium.isNull())
11297 {
11298 AutoCaller mac(pMedium);
11299 if (FAILED(mac.rc())) return mac.rc();
11300 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11301 DeviceType_T devType = pMedium->i_getDeviceType();
11302 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11303 && devType == DeviceType_HardDisk)
11304 || (cleanupMode == CleanupMode_Full)
11305 )
11306 {
11307 llMedia.push_back(pMedium);
11308 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11309 /* Not allowed to keep this lock as below we need the parent
11310 * medium lock, and the lock order is parent to child. */
11311 lock.release();
11312 /*
11313 * Search for medias which are not attached to any machine, but
11314 * in the chain to an attached disk. Mediums are only consided
11315 * if they are:
11316 * - have only one child
11317 * - no references to any machines
11318 * - are of normal medium type
11319 */
11320 while (!pParent.isNull())
11321 {
11322 AutoCaller mac1(pParent);
11323 if (FAILED(mac1.rc())) return mac1.rc();
11324 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11325 if (pParent->i_getChildren().size() == 1)
11326 {
11327 if ( pParent->i_getMachineBackRefCount() == 0
11328 && pParent->i_getType() == MediumType_Normal
11329 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11330 llMedia.push_back(pParent);
11331 }
11332 else
11333 break;
11334 pParent = pParent->i_getParent();
11335 }
11336 }
11337 }
11338
11339 // real machine: then we need to use the proper method
11340 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11341
11342 if (FAILED(rc))
11343 return rc;
11344 }
11345
11346 return S_OK;
11347}
11348
11349/**
11350 * Perform deferred hard disk detachments.
11351 *
11352 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11353 * changed (not backed up).
11354 *
11355 * If @a aOnline is @c true then this method will also unlock the old hard
11356 * disks for which the new implicit diffs were created and will lock these new
11357 * diffs for writing.
11358 *
11359 * @param aOnline Whether the VM was online prior to this operation.
11360 *
11361 * @note Locks this object for writing!
11362 */
11363void Machine::i_commitMedia(bool aOnline /*= false*/)
11364{
11365 AutoCaller autoCaller(this);
11366 AssertComRCReturnVoid(autoCaller.rc());
11367
11368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11369
11370 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11371
11372 HRESULT rc = S_OK;
11373
11374 /* no attach/detach operations -- nothing to do */
11375 if (!mMediumAttachments.isBackedUp())
11376 return;
11377
11378 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11379 bool fMediaNeedsLocking = false;
11380
11381 /* enumerate new attachments */
11382 for (MediumAttachmentList::const_iterator
11383 it = mMediumAttachments->begin();
11384 it != mMediumAttachments->end();
11385 ++it)
11386 {
11387 MediumAttachment *pAttach = *it;
11388
11389 pAttach->i_commit();
11390
11391 Medium *pMedium = pAttach->i_getMedium();
11392 bool fImplicit = pAttach->i_isImplicit();
11393
11394 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11395 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11396 fImplicit));
11397
11398 /** @todo convert all this Machine-based voodoo to MediumAttachment
11399 * based commit logic. */
11400 if (fImplicit)
11401 {
11402 /* convert implicit attachment to normal */
11403 pAttach->i_setImplicit(false);
11404
11405 if ( aOnline
11406 && pMedium
11407 && pAttach->i_getType() == DeviceType_HardDisk
11408 )
11409 {
11410 /* update the appropriate lock list */
11411 MediumLockList *pMediumLockList;
11412 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11413 AssertComRC(rc);
11414 if (pMediumLockList)
11415 {
11416 /* unlock if there's a need to change the locking */
11417 if (!fMediaNeedsLocking)
11418 {
11419 rc = mData->mSession.mLockedMedia.Unlock();
11420 AssertComRC(rc);
11421 fMediaNeedsLocking = true;
11422 }
11423 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11424 AssertComRC(rc);
11425 rc = pMediumLockList->Append(pMedium, true);
11426 AssertComRC(rc);
11427 }
11428 }
11429
11430 continue;
11431 }
11432
11433 if (pMedium)
11434 {
11435 /* was this medium attached before? */
11436 for (MediumAttachmentList::iterator
11437 oldIt = oldAtts.begin();
11438 oldIt != oldAtts.end();
11439 ++oldIt)
11440 {
11441 MediumAttachment *pOldAttach = *oldIt;
11442 if (pOldAttach->i_getMedium() == pMedium)
11443 {
11444 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11445
11446 /* yes: remove from old to avoid de-association */
11447 oldAtts.erase(oldIt);
11448 break;
11449 }
11450 }
11451 }
11452 }
11453
11454 /* enumerate remaining old attachments and de-associate from the
11455 * current machine state */
11456 for (MediumAttachmentList::const_iterator
11457 it = oldAtts.begin();
11458 it != oldAtts.end();
11459 ++it)
11460 {
11461 MediumAttachment *pAttach = *it;
11462 Medium *pMedium = pAttach->i_getMedium();
11463
11464 /* Detach only hard disks, since DVD/floppy media is detached
11465 * instantly in MountMedium. */
11466 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11467 {
11468 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11469
11470 /* now de-associate from the current machine state */
11471 rc = pMedium->i_removeBackReference(mData->mUuid);
11472 AssertComRC(rc);
11473
11474 if (aOnline)
11475 {
11476 /* unlock since medium is not used anymore */
11477 MediumLockList *pMediumLockList;
11478 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11479 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11480 {
11481 /* this happens for online snapshots, there the attachment
11482 * is changing, but only to a diff image created under
11483 * the old one, so there is no separate lock list */
11484 Assert(!pMediumLockList);
11485 }
11486 else
11487 {
11488 AssertComRC(rc);
11489 if (pMediumLockList)
11490 {
11491 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11492 AssertComRC(rc);
11493 }
11494 }
11495 }
11496 }
11497 }
11498
11499 /* take media locks again so that the locking state is consistent */
11500 if (fMediaNeedsLocking)
11501 {
11502 Assert(aOnline);
11503 rc = mData->mSession.mLockedMedia.Lock();
11504 AssertComRC(rc);
11505 }
11506
11507 /* commit the hard disk changes */
11508 mMediumAttachments.commit();
11509
11510 if (i_isSessionMachine())
11511 {
11512 /*
11513 * Update the parent machine to point to the new owner.
11514 * This is necessary because the stored parent will point to the
11515 * session machine otherwise and cause crashes or errors later
11516 * when the session machine gets invalid.
11517 */
11518 /** @todo Change the MediumAttachment class to behave like any other
11519 * class in this regard by creating peer MediumAttachment
11520 * objects for session machines and share the data with the peer
11521 * machine.
11522 */
11523 for (MediumAttachmentList::const_iterator
11524 it = mMediumAttachments->begin();
11525 it != mMediumAttachments->end();
11526 ++it)
11527 (*it)->i_updateParentMachine(mPeer);
11528
11529 /* attach new data to the primary machine and reshare it */
11530 mPeer->mMediumAttachments.attach(mMediumAttachments);
11531 }
11532
11533 return;
11534}
11535
11536/**
11537 * Perform deferred deletion of implicitly created diffs.
11538 *
11539 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11540 * changed (not backed up).
11541 *
11542 * @note Locks this object for writing!
11543 */
11544void Machine::i_rollbackMedia()
11545{
11546 AutoCaller autoCaller(this);
11547 AssertComRCReturnVoid(autoCaller.rc());
11548
11549 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11550 LogFlowThisFunc(("Entering rollbackMedia\n"));
11551
11552 HRESULT rc = S_OK;
11553
11554 /* no attach/detach operations -- nothing to do */
11555 if (!mMediumAttachments.isBackedUp())
11556 return;
11557
11558 /* enumerate new attachments */
11559 for (MediumAttachmentList::const_iterator
11560 it = mMediumAttachments->begin();
11561 it != mMediumAttachments->end();
11562 ++it)
11563 {
11564 MediumAttachment *pAttach = *it;
11565 /* Fix up the backrefs for DVD/floppy media. */
11566 if (pAttach->i_getType() != DeviceType_HardDisk)
11567 {
11568 Medium *pMedium = pAttach->i_getMedium();
11569 if (pMedium)
11570 {
11571 rc = pMedium->i_removeBackReference(mData->mUuid);
11572 AssertComRC(rc);
11573 }
11574 }
11575
11576 (*it)->i_rollback();
11577
11578 pAttach = *it;
11579 /* Fix up the backrefs for DVD/floppy media. */
11580 if (pAttach->i_getType() != DeviceType_HardDisk)
11581 {
11582 Medium *pMedium = pAttach->i_getMedium();
11583 if (pMedium)
11584 {
11585 rc = pMedium->i_addBackReference(mData->mUuid);
11586 AssertComRC(rc);
11587 }
11588 }
11589 }
11590
11591 /** @todo convert all this Machine-based voodoo to MediumAttachment
11592 * based rollback logic. */
11593 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11594
11595 return;
11596}
11597
11598/**
11599 * Returns true if the settings file is located in the directory named exactly
11600 * as the machine; this means, among other things, that the machine directory
11601 * should be auto-renamed.
11602 *
11603 * @param aSettingsDir if not NULL, the full machine settings file directory
11604 * name will be assigned there.
11605 *
11606 * @note Doesn't lock anything.
11607 * @note Not thread safe (must be called from this object's lock).
11608 */
11609bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11610{
11611 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11612 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11613 if (aSettingsDir)
11614 *aSettingsDir = strMachineDirName;
11615 strMachineDirName.stripPath(); // vmname
11616 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11617 strConfigFileOnly.stripPath() // vmname.vbox
11618 .stripSuffix(); // vmname
11619 /** @todo hack, make somehow use of ComposeMachineFilename */
11620 if (mUserData->s.fDirectoryIncludesUUID)
11621 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11622
11623 AssertReturn(!strMachineDirName.isEmpty(), false);
11624 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11625
11626 return strMachineDirName == strConfigFileOnly;
11627}
11628
11629/**
11630 * Discards all changes to machine settings.
11631 *
11632 * @param aNotify Whether to notify the direct session about changes or not.
11633 *
11634 * @note Locks objects for writing!
11635 */
11636void Machine::i_rollback(bool aNotify)
11637{
11638 AutoCaller autoCaller(this);
11639 AssertComRCReturn(autoCaller.rc(), (void)0);
11640
11641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11642
11643 if (!mStorageControllers.isNull())
11644 {
11645 if (mStorageControllers.isBackedUp())
11646 {
11647 /* unitialize all new devices (absent in the backed up list). */
11648 StorageControllerList *backedList = mStorageControllers.backedUpData();
11649 for (StorageControllerList::const_iterator
11650 it = mStorageControllers->begin();
11651 it != mStorageControllers->end();
11652 ++it)
11653 {
11654 if ( std::find(backedList->begin(), backedList->end(), *it)
11655 == backedList->end()
11656 )
11657 {
11658 (*it)->uninit();
11659 }
11660 }
11661
11662 /* restore the list */
11663 mStorageControllers.rollback();
11664 }
11665
11666 /* rollback any changes to devices after restoring the list */
11667 if (mData->flModifications & IsModified_Storage)
11668 {
11669 for (StorageControllerList::const_iterator
11670 it = mStorageControllers->begin();
11671 it != mStorageControllers->end();
11672 ++it)
11673 {
11674 (*it)->i_rollback();
11675 }
11676 }
11677 }
11678
11679 if (!mUSBControllers.isNull())
11680 {
11681 if (mUSBControllers.isBackedUp())
11682 {
11683 /* unitialize all new devices (absent in the backed up list). */
11684 USBControllerList *backedList = mUSBControllers.backedUpData();
11685 for (USBControllerList::const_iterator
11686 it = mUSBControllers->begin();
11687 it != mUSBControllers->end();
11688 ++it)
11689 {
11690 if ( std::find(backedList->begin(), backedList->end(), *it)
11691 == backedList->end()
11692 )
11693 {
11694 (*it)->uninit();
11695 }
11696 }
11697
11698 /* restore the list */
11699 mUSBControllers.rollback();
11700 }
11701
11702 /* rollback any changes to devices after restoring the list */
11703 if (mData->flModifications & IsModified_USB)
11704 {
11705 for (USBControllerList::const_iterator
11706 it = mUSBControllers->begin();
11707 it != mUSBControllers->end();
11708 ++it)
11709 {
11710 (*it)->i_rollback();
11711 }
11712 }
11713 }
11714
11715 mUserData.rollback();
11716
11717 mHWData.rollback();
11718
11719 if (mData->flModifications & IsModified_Storage)
11720 i_rollbackMedia();
11721
11722 if (mBIOSSettings)
11723 mBIOSSettings->i_rollback();
11724
11725 if (mCaptureSettings && (mData->flModifications & IsModified_Capture))
11726 mCaptureSettings->i_rollback();
11727
11728 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11729 mVRDEServer->i_rollback();
11730
11731 if (mAudioAdapter)
11732 mAudioAdapter->i_rollback();
11733
11734 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11735 mUSBDeviceFilters->i_rollback();
11736
11737 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11738 mBandwidthControl->i_rollback();
11739
11740 if (!mHWData.isNull())
11741 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11742 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11743 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11744 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11745
11746 if (mData->flModifications & IsModified_NetworkAdapters)
11747 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11748 if ( mNetworkAdapters[slot]
11749 && mNetworkAdapters[slot]->i_isModified())
11750 {
11751 mNetworkAdapters[slot]->i_rollback();
11752 networkAdapters[slot] = mNetworkAdapters[slot];
11753 }
11754
11755 if (mData->flModifications & IsModified_SerialPorts)
11756 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11757 if ( mSerialPorts[slot]
11758 && mSerialPorts[slot]->i_isModified())
11759 {
11760 mSerialPorts[slot]->i_rollback();
11761 serialPorts[slot] = mSerialPorts[slot];
11762 }
11763
11764 if (mData->flModifications & IsModified_ParallelPorts)
11765 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11766 if ( mParallelPorts[slot]
11767 && mParallelPorts[slot]->i_isModified())
11768 {
11769 mParallelPorts[slot]->i_rollback();
11770 parallelPorts[slot] = mParallelPorts[slot];
11771 }
11772
11773 if (aNotify)
11774 {
11775 /* inform the direct session about changes */
11776
11777 ComObjPtr<Machine> that = this;
11778 uint32_t flModifications = mData->flModifications;
11779 alock.release();
11780
11781 if (flModifications & IsModified_SharedFolders)
11782 that->i_onSharedFolderChange();
11783
11784 if (flModifications & IsModified_VRDEServer)
11785 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11786 if (flModifications & IsModified_USB)
11787 that->i_onUSBControllerChange();
11788
11789 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11790 if (networkAdapters[slot])
11791 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11792 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11793 if (serialPorts[slot])
11794 that->i_onSerialPortChange(serialPorts[slot]);
11795 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11796 if (parallelPorts[slot])
11797 that->i_onParallelPortChange(parallelPorts[slot]);
11798
11799 if (flModifications & IsModified_Storage)
11800 that->i_onStorageControllerChange();
11801
11802#if 0
11803 if (flModifications & IsModified_BandwidthControl)
11804 that->onBandwidthControlChange();
11805#endif
11806 }
11807}
11808
11809/**
11810 * Commits all the changes to machine settings.
11811 *
11812 * Note that this operation is supposed to never fail.
11813 *
11814 * @note Locks this object and children for writing.
11815 */
11816void Machine::i_commit()
11817{
11818 AutoCaller autoCaller(this);
11819 AssertComRCReturnVoid(autoCaller.rc());
11820
11821 AutoCaller peerCaller(mPeer);
11822 AssertComRCReturnVoid(peerCaller.rc());
11823
11824 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11825
11826 /*
11827 * use safe commit to ensure Snapshot machines (that share mUserData)
11828 * will still refer to a valid memory location
11829 */
11830 mUserData.commitCopy();
11831
11832 mHWData.commit();
11833
11834 if (mMediumAttachments.isBackedUp())
11835 i_commitMedia(Global::IsOnline(mData->mMachineState));
11836
11837 mBIOSSettings->i_commit();
11838 mCaptureSettings->i_commit();
11839 mVRDEServer->i_commit();
11840 mAudioAdapter->i_commit();
11841 mUSBDeviceFilters->i_commit();
11842 mBandwidthControl->i_commit();
11843
11844 /* Since mNetworkAdapters is a list which might have been changed (resized)
11845 * without using the Backupable<> template we need to handle the copying
11846 * of the list entries manually, including the creation of peers for the
11847 * new objects. */
11848 bool commitNetworkAdapters = false;
11849 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11850 if (mPeer)
11851 {
11852 /* commit everything, even the ones which will go away */
11853 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11854 mNetworkAdapters[slot]->i_commit();
11855 /* copy over the new entries, creating a peer and uninit the original */
11856 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11857 for (size_t slot = 0; slot < newSize; slot++)
11858 {
11859 /* look if this adapter has a peer device */
11860 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11861 if (!peer)
11862 {
11863 /* no peer means the adapter is a newly created one;
11864 * create a peer owning data this data share it with */
11865 peer.createObject();
11866 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11867 }
11868 mPeer->mNetworkAdapters[slot] = peer;
11869 }
11870 /* uninit any no longer needed network adapters */
11871 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11872 mNetworkAdapters[slot]->uninit();
11873 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11874 {
11875 if (mPeer->mNetworkAdapters[slot])
11876 mPeer->mNetworkAdapters[slot]->uninit();
11877 }
11878 /* Keep the original network adapter count until this point, so that
11879 * discarding a chipset type change will not lose settings. */
11880 mNetworkAdapters.resize(newSize);
11881 mPeer->mNetworkAdapters.resize(newSize);
11882 }
11883 else
11884 {
11885 /* we have no peer (our parent is the newly created machine);
11886 * just commit changes to the network adapters */
11887 commitNetworkAdapters = true;
11888 }
11889 if (commitNetworkAdapters)
11890 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11891 mNetworkAdapters[slot]->i_commit();
11892
11893 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11894 mSerialPorts[slot]->i_commit();
11895 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11896 mParallelPorts[slot]->i_commit();
11897
11898 bool commitStorageControllers = false;
11899
11900 if (mStorageControllers.isBackedUp())
11901 {
11902 mStorageControllers.commit();
11903
11904 if (mPeer)
11905 {
11906 /* Commit all changes to new controllers (this will reshare data with
11907 * peers for those who have peers) */
11908 StorageControllerList *newList = new StorageControllerList();
11909 for (StorageControllerList::const_iterator
11910 it = mStorageControllers->begin();
11911 it != mStorageControllers->end();
11912 ++it)
11913 {
11914 (*it)->i_commit();
11915
11916 /* look if this controller has a peer device */
11917 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11918 if (!peer)
11919 {
11920 /* no peer means the device is a newly created one;
11921 * create a peer owning data this device share it with */
11922 peer.createObject();
11923 peer->init(mPeer, *it, true /* aReshare */);
11924 }
11925 else
11926 {
11927 /* remove peer from the old list */
11928 mPeer->mStorageControllers->remove(peer);
11929 }
11930 /* and add it to the new list */
11931 newList->push_back(peer);
11932 }
11933
11934 /* uninit old peer's controllers that are left */
11935 for (StorageControllerList::const_iterator
11936 it = mPeer->mStorageControllers->begin();
11937 it != mPeer->mStorageControllers->end();
11938 ++it)
11939 {
11940 (*it)->uninit();
11941 }
11942
11943 /* attach new list of controllers to our peer */
11944 mPeer->mStorageControllers.attach(newList);
11945 }
11946 else
11947 {
11948 /* we have no peer (our parent is the newly created machine);
11949 * just commit changes to devices */
11950 commitStorageControllers = true;
11951 }
11952 }
11953 else
11954 {
11955 /* the list of controllers itself is not changed,
11956 * just commit changes to controllers themselves */
11957 commitStorageControllers = true;
11958 }
11959
11960 if (commitStorageControllers)
11961 {
11962 for (StorageControllerList::const_iterator
11963 it = mStorageControllers->begin();
11964 it != mStorageControllers->end();
11965 ++it)
11966 {
11967 (*it)->i_commit();
11968 }
11969 }
11970
11971 bool commitUSBControllers = false;
11972
11973 if (mUSBControllers.isBackedUp())
11974 {
11975 mUSBControllers.commit();
11976
11977 if (mPeer)
11978 {
11979 /* Commit all changes to new controllers (this will reshare data with
11980 * peers for those who have peers) */
11981 USBControllerList *newList = new USBControllerList();
11982 for (USBControllerList::const_iterator
11983 it = mUSBControllers->begin();
11984 it != mUSBControllers->end();
11985 ++it)
11986 {
11987 (*it)->i_commit();
11988
11989 /* look if this controller has a peer device */
11990 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11991 if (!peer)
11992 {
11993 /* no peer means the device is a newly created one;
11994 * create a peer owning data this device share it with */
11995 peer.createObject();
11996 peer->init(mPeer, *it, true /* aReshare */);
11997 }
11998 else
11999 {
12000 /* remove peer from the old list */
12001 mPeer->mUSBControllers->remove(peer);
12002 }
12003 /* and add it to the new list */
12004 newList->push_back(peer);
12005 }
12006
12007 /* uninit old peer's controllers that are left */
12008 for (USBControllerList::const_iterator
12009 it = mPeer->mUSBControllers->begin();
12010 it != mPeer->mUSBControllers->end();
12011 ++it)
12012 {
12013 (*it)->uninit();
12014 }
12015
12016 /* attach new list of controllers to our peer */
12017 mPeer->mUSBControllers.attach(newList);
12018 }
12019 else
12020 {
12021 /* we have no peer (our parent is the newly created machine);
12022 * just commit changes to devices */
12023 commitUSBControllers = true;
12024 }
12025 }
12026 else
12027 {
12028 /* the list of controllers itself is not changed,
12029 * just commit changes to controllers themselves */
12030 commitUSBControllers = true;
12031 }
12032
12033 if (commitUSBControllers)
12034 {
12035 for (USBControllerList::const_iterator
12036 it = mUSBControllers->begin();
12037 it != mUSBControllers->end();
12038 ++it)
12039 {
12040 (*it)->i_commit();
12041 }
12042 }
12043
12044 if (i_isSessionMachine())
12045 {
12046 /* attach new data to the primary machine and reshare it */
12047 mPeer->mUserData.attach(mUserData);
12048 mPeer->mHWData.attach(mHWData);
12049 /* mmMediumAttachments is reshared by fixupMedia */
12050 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12051 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12052 }
12053}
12054
12055/**
12056 * Copies all the hardware data from the given machine.
12057 *
12058 * Currently, only called when the VM is being restored from a snapshot. In
12059 * particular, this implies that the VM is not running during this method's
12060 * call.
12061 *
12062 * @note This method must be called from under this object's lock.
12063 *
12064 * @note This method doesn't call #i_commit(), so all data remains backed up and
12065 * unsaved.
12066 */
12067void Machine::i_copyFrom(Machine *aThat)
12068{
12069 AssertReturnVoid(!i_isSnapshotMachine());
12070 AssertReturnVoid(aThat->i_isSnapshotMachine());
12071
12072 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12073
12074 mHWData.assignCopy(aThat->mHWData);
12075
12076 // create copies of all shared folders (mHWData after attaching a copy
12077 // contains just references to original objects)
12078 for (HWData::SharedFolderList::iterator
12079 it = mHWData->mSharedFolders.begin();
12080 it != mHWData->mSharedFolders.end();
12081 ++it)
12082 {
12083 ComObjPtr<SharedFolder> folder;
12084 folder.createObject();
12085 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12086 AssertComRC(rc);
12087 *it = folder;
12088 }
12089
12090 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12091 mCaptureSettings->i_copyFrom(aThat->mCaptureSettings);
12092 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12093 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12094 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12095 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12096
12097 /* create private copies of all controllers */
12098 mStorageControllers.backup();
12099 mStorageControllers->clear();
12100 for (StorageControllerList::const_iterator
12101 it = aThat->mStorageControllers->begin();
12102 it != aThat->mStorageControllers->end();
12103 ++it)
12104 {
12105 ComObjPtr<StorageController> ctrl;
12106 ctrl.createObject();
12107 ctrl->initCopy(this, *it);
12108 mStorageControllers->push_back(ctrl);
12109 }
12110
12111 /* create private copies of all USB controllers */
12112 mUSBControllers.backup();
12113 mUSBControllers->clear();
12114 for (USBControllerList::const_iterator
12115 it = aThat->mUSBControllers->begin();
12116 it != aThat->mUSBControllers->end();
12117 ++it)
12118 {
12119 ComObjPtr<USBController> ctrl;
12120 ctrl.createObject();
12121 ctrl->initCopy(this, *it);
12122 mUSBControllers->push_back(ctrl);
12123 }
12124
12125 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12126 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12127 {
12128 if (mNetworkAdapters[slot].isNotNull())
12129 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12130 else
12131 {
12132 unconst(mNetworkAdapters[slot]).createObject();
12133 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12134 }
12135 }
12136 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12137 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12138 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12139 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12140}
12141
12142/**
12143 * Returns whether the given storage controller is hotplug capable.
12144 *
12145 * @returns true if the controller supports hotplugging
12146 * false otherwise.
12147 * @param enmCtrlType The controller type to check for.
12148 */
12149bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12150{
12151 ComPtr<ISystemProperties> systemProperties;
12152 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12153 if (FAILED(rc))
12154 return false;
12155
12156 BOOL aHotplugCapable = FALSE;
12157 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12158
12159 return RT_BOOL(aHotplugCapable);
12160}
12161
12162#ifdef VBOX_WITH_RESOURCE_USAGE_API
12163
12164void Machine::i_getDiskList(MediaList &list)
12165{
12166 for (MediumAttachmentList::const_iterator
12167 it = mMediumAttachments->begin();
12168 it != mMediumAttachments->end();
12169 ++it)
12170 {
12171 MediumAttachment *pAttach = *it;
12172 /* just in case */
12173 AssertContinue(pAttach);
12174
12175 AutoCaller localAutoCallerA(pAttach);
12176 if (FAILED(localAutoCallerA.rc())) continue;
12177
12178 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12179
12180 if (pAttach->i_getType() == DeviceType_HardDisk)
12181 list.push_back(pAttach->i_getMedium());
12182 }
12183}
12184
12185void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12186{
12187 AssertReturnVoid(isWriteLockOnCurrentThread());
12188 AssertPtrReturnVoid(aCollector);
12189
12190 pm::CollectorHAL *hal = aCollector->getHAL();
12191 /* Create sub metrics */
12192 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12193 "Percentage of processor time spent in user mode by the VM process.");
12194 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12195 "Percentage of processor time spent in kernel mode by the VM process.");
12196 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12197 "Size of resident portion of VM process in memory.");
12198 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12199 "Actual size of all VM disks combined.");
12200 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12201 "Network receive rate.");
12202 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12203 "Network transmit rate.");
12204 /* Create and register base metrics */
12205 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12206 cpuLoadUser, cpuLoadKernel);
12207 aCollector->registerBaseMetric(cpuLoad);
12208 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12209 ramUsageUsed);
12210 aCollector->registerBaseMetric(ramUsage);
12211 MediaList disks;
12212 i_getDiskList(disks);
12213 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12214 diskUsageUsed);
12215 aCollector->registerBaseMetric(diskUsage);
12216
12217 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12218 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12219 new pm::AggregateAvg()));
12220 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12221 new pm::AggregateMin()));
12222 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12223 new pm::AggregateMax()));
12224 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12225 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12226 new pm::AggregateAvg()));
12227 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12228 new pm::AggregateMin()));
12229 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12230 new pm::AggregateMax()));
12231
12232 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12233 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12234 new pm::AggregateAvg()));
12235 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12236 new pm::AggregateMin()));
12237 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12238 new pm::AggregateMax()));
12239
12240 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12241 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12242 new pm::AggregateAvg()));
12243 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12244 new pm::AggregateMin()));
12245 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12246 new pm::AggregateMax()));
12247
12248
12249 /* Guest metrics collector */
12250 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12251 aCollector->registerGuest(mCollectorGuest);
12252 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12253
12254 /* Create sub metrics */
12255 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12256 "Percentage of processor time spent in user mode as seen by the guest.");
12257 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12258 "Percentage of processor time spent in kernel mode as seen by the guest.");
12259 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12260 "Percentage of processor time spent idling as seen by the guest.");
12261
12262 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12263 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12264 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12265 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12266 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12267 pm::SubMetric *guestMemCache = new pm::SubMetric(
12268 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12269
12270 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12271 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12272
12273 /* Create and register base metrics */
12274 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12275 machineNetRx, machineNetTx);
12276 aCollector->registerBaseMetric(machineNetRate);
12277
12278 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12279 guestLoadUser, guestLoadKernel, guestLoadIdle);
12280 aCollector->registerBaseMetric(guestCpuLoad);
12281
12282 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12283 guestMemTotal, guestMemFree,
12284 guestMemBalloon, guestMemShared,
12285 guestMemCache, guestPagedTotal);
12286 aCollector->registerBaseMetric(guestCpuMem);
12287
12288 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12289 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12290 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12291 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12292
12293 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12294 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12295 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12296 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12297
12298 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12299 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12300 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12301 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12302
12303 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12304 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12305 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12306 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12307
12308 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12309 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12310 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12311 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12312
12313 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12314 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12316 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12317
12318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12319 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12321 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12322
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12324 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12327
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12329 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12332
12333 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12334 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12335 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12336 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12337
12338 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12339 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12340 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12342}
12343
12344void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12345{
12346 AssertReturnVoid(isWriteLockOnCurrentThread());
12347
12348 if (aCollector)
12349 {
12350 aCollector->unregisterMetricsFor(aMachine);
12351 aCollector->unregisterBaseMetricsFor(aMachine);
12352 }
12353}
12354
12355#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12356
12357
12358////////////////////////////////////////////////////////////////////////////////
12359
12360DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12361
12362HRESULT SessionMachine::FinalConstruct()
12363{
12364 LogFlowThisFunc(("\n"));
12365
12366 mClientToken = NULL;
12367
12368 return BaseFinalConstruct();
12369}
12370
12371void SessionMachine::FinalRelease()
12372{
12373 LogFlowThisFunc(("\n"));
12374
12375 Assert(!mClientToken);
12376 /* paranoia, should not hang around any more */
12377 if (mClientToken)
12378 {
12379 delete mClientToken;
12380 mClientToken = NULL;
12381 }
12382
12383 uninit(Uninit::Unexpected);
12384
12385 BaseFinalRelease();
12386}
12387
12388/**
12389 * @note Must be called only by Machine::LockMachine() from its own write lock.
12390 */
12391HRESULT SessionMachine::init(Machine *aMachine)
12392{
12393 LogFlowThisFuncEnter();
12394 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12395
12396 AssertReturn(aMachine, E_INVALIDARG);
12397
12398 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12399
12400 /* Enclose the state transition NotReady->InInit->Ready */
12401 AutoInitSpan autoInitSpan(this);
12402 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12403
12404 HRESULT rc = S_OK;
12405
12406 RT_ZERO(mAuthLibCtx);
12407
12408 /* create the machine client token */
12409 try
12410 {
12411 mClientToken = new ClientToken(aMachine, this);
12412 if (!mClientToken->isReady())
12413 {
12414 delete mClientToken;
12415 mClientToken = NULL;
12416 rc = E_FAIL;
12417 }
12418 }
12419 catch (std::bad_alloc &)
12420 {
12421 rc = E_OUTOFMEMORY;
12422 }
12423 if (FAILED(rc))
12424 return rc;
12425
12426 /* memorize the peer Machine */
12427 unconst(mPeer) = aMachine;
12428 /* share the parent pointer */
12429 unconst(mParent) = aMachine->mParent;
12430
12431 /* take the pointers to data to share */
12432 mData.share(aMachine->mData);
12433 mSSData.share(aMachine->mSSData);
12434
12435 mUserData.share(aMachine->mUserData);
12436 mHWData.share(aMachine->mHWData);
12437 mMediumAttachments.share(aMachine->mMediumAttachments);
12438
12439 mStorageControllers.allocate();
12440 for (StorageControllerList::const_iterator
12441 it = aMachine->mStorageControllers->begin();
12442 it != aMachine->mStorageControllers->end();
12443 ++it)
12444 {
12445 ComObjPtr<StorageController> ctl;
12446 ctl.createObject();
12447 ctl->init(this, *it);
12448 mStorageControllers->push_back(ctl);
12449 }
12450
12451 mUSBControllers.allocate();
12452 for (USBControllerList::const_iterator
12453 it = aMachine->mUSBControllers->begin();
12454 it != aMachine->mUSBControllers->end();
12455 ++it)
12456 {
12457 ComObjPtr<USBController> ctl;
12458 ctl.createObject();
12459 ctl->init(this, *it);
12460 mUSBControllers->push_back(ctl);
12461 }
12462
12463 unconst(mBIOSSettings).createObject();
12464 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12465 unconst(mCaptureSettings).createObject();
12466 mCaptureSettings->init(this, aMachine->mCaptureSettings);
12467 /* create another VRDEServer object that will be mutable */
12468 unconst(mVRDEServer).createObject();
12469 mVRDEServer->init(this, aMachine->mVRDEServer);
12470 /* create another audio adapter object that will be mutable */
12471 unconst(mAudioAdapter).createObject();
12472 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12473 /* create a list of serial ports that will be mutable */
12474 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12475 {
12476 unconst(mSerialPorts[slot]).createObject();
12477 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12478 }
12479 /* create a list of parallel ports that will be mutable */
12480 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12481 {
12482 unconst(mParallelPorts[slot]).createObject();
12483 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12484 }
12485
12486 /* create another USB device filters object that will be mutable */
12487 unconst(mUSBDeviceFilters).createObject();
12488 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12489
12490 /* create a list of network adapters that will be mutable */
12491 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12492 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12493 {
12494 unconst(mNetworkAdapters[slot]).createObject();
12495 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12496 }
12497
12498 /* create another bandwidth control object that will be mutable */
12499 unconst(mBandwidthControl).createObject();
12500 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12501
12502 /* default is to delete saved state on Saved -> PoweredOff transition */
12503 mRemoveSavedState = true;
12504
12505 /* Confirm a successful initialization when it's the case */
12506 autoInitSpan.setSucceeded();
12507
12508 miNATNetworksStarted = 0;
12509
12510 LogFlowThisFuncLeave();
12511 return rc;
12512}
12513
12514/**
12515 * Uninitializes this session object. If the reason is other than
12516 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12517 * or the client watcher code.
12518 *
12519 * @param aReason uninitialization reason
12520 *
12521 * @note Locks mParent + this object for writing.
12522 */
12523void SessionMachine::uninit(Uninit::Reason aReason)
12524{
12525 LogFlowThisFuncEnter();
12526 LogFlowThisFunc(("reason=%d\n", aReason));
12527
12528 /*
12529 * Strongly reference ourselves to prevent this object deletion after
12530 * mData->mSession.mMachine.setNull() below (which can release the last
12531 * reference and call the destructor). Important: this must be done before
12532 * accessing any members (and before AutoUninitSpan that does it as well).
12533 * This self reference will be released as the very last step on return.
12534 */
12535 ComObjPtr<SessionMachine> selfRef;
12536 if (aReason != Uninit::Unexpected)
12537 selfRef = this;
12538
12539 /* Enclose the state transition Ready->InUninit->NotReady */
12540 AutoUninitSpan autoUninitSpan(this);
12541 if (autoUninitSpan.uninitDone())
12542 {
12543 LogFlowThisFunc(("Already uninitialized\n"));
12544 LogFlowThisFuncLeave();
12545 return;
12546 }
12547
12548 if (autoUninitSpan.initFailed())
12549 {
12550 /* We've been called by init() because it's failed. It's not really
12551 * necessary (nor it's safe) to perform the regular uninit sequence
12552 * below, the following is enough.
12553 */
12554 LogFlowThisFunc(("Initialization failed.\n"));
12555 /* destroy the machine client token */
12556 if (mClientToken)
12557 {
12558 delete mClientToken;
12559 mClientToken = NULL;
12560 }
12561 uninitDataAndChildObjects();
12562 mData.free();
12563 unconst(mParent) = NULL;
12564 unconst(mPeer) = NULL;
12565 LogFlowThisFuncLeave();
12566 return;
12567 }
12568
12569 MachineState_T lastState;
12570 {
12571 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12572 lastState = mData->mMachineState;
12573 }
12574 NOREF(lastState);
12575
12576#ifdef VBOX_WITH_USB
12577 // release all captured USB devices, but do this before requesting the locks below
12578 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12579 {
12580 /* Console::captureUSBDevices() is called in the VM process only after
12581 * setting the machine state to Starting or Restoring.
12582 * Console::detachAllUSBDevices() will be called upon successful
12583 * termination. So, we need to release USB devices only if there was
12584 * an abnormal termination of a running VM.
12585 *
12586 * This is identical to SessionMachine::DetachAllUSBDevices except
12587 * for the aAbnormal argument. */
12588 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12589 AssertComRC(rc);
12590 NOREF(rc);
12591
12592 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12593 if (service)
12594 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12595 }
12596#endif /* VBOX_WITH_USB */
12597
12598 // we need to lock this object in uninit() because the lock is shared
12599 // with mPeer (as well as data we modify below). mParent lock is needed
12600 // by several calls to it.
12601 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12602
12603#ifdef VBOX_WITH_RESOURCE_USAGE_API
12604 /*
12605 * It is safe to call Machine::i_unregisterMetrics() here because
12606 * PerformanceCollector::samplerCallback no longer accesses guest methods
12607 * holding the lock.
12608 */
12609 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12610 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12611 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12612 if (mCollectorGuest)
12613 {
12614 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12615 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12616 mCollectorGuest = NULL;
12617 }
12618#endif
12619
12620 if (aReason == Uninit::Abnormal)
12621 {
12622 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12623
12624 /* reset the state to Aborted */
12625 if (mData->mMachineState != MachineState_Aborted)
12626 i_setMachineState(MachineState_Aborted);
12627 }
12628
12629 // any machine settings modified?
12630 if (mData->flModifications)
12631 {
12632 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12633 i_rollback(false /* aNotify */);
12634 }
12635
12636 mData->mSession.mPID = NIL_RTPROCESS;
12637
12638 if (aReason == Uninit::Unexpected)
12639 {
12640 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12641 * client watcher thread to update the set of machines that have open
12642 * sessions. */
12643 mParent->i_updateClientWatcher();
12644 }
12645
12646 /* uninitialize all remote controls */
12647 if (mData->mSession.mRemoteControls.size())
12648 {
12649 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12650 mData->mSession.mRemoteControls.size()));
12651
12652 /* Always restart a the beginning, since the iterator is invalidated
12653 * by using erase(). */
12654 for (Data::Session::RemoteControlList::iterator
12655 it = mData->mSession.mRemoteControls.begin();
12656 it != mData->mSession.mRemoteControls.end();
12657 it = mData->mSession.mRemoteControls.begin())
12658 {
12659 ComPtr<IInternalSessionControl> pControl = *it;
12660 mData->mSession.mRemoteControls.erase(it);
12661 multilock.release();
12662 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12663 HRESULT rc = pControl->Uninitialize();
12664 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12665 if (FAILED(rc))
12666 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12667 multilock.acquire();
12668 }
12669 mData->mSession.mRemoteControls.clear();
12670 }
12671
12672 /* Remove all references to the NAT network service. The service will stop
12673 * if all references (also from other VMs) are removed. */
12674 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12675 {
12676 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12677 {
12678 BOOL enabled;
12679 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12680 if ( FAILED(hrc)
12681 || !enabled)
12682 continue;
12683
12684 NetworkAttachmentType_T type;
12685 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12686 if ( SUCCEEDED(hrc)
12687 && type == NetworkAttachmentType_NATNetwork)
12688 {
12689 Bstr name;
12690 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12691 if (SUCCEEDED(hrc))
12692 {
12693 multilock.release();
12694 Utf8Str strName(name);
12695 LogRel(("VM '%s' stops using NAT network '%s'\n",
12696 mUserData->s.strName.c_str(), strName.c_str()));
12697 mParent->i_natNetworkRefDec(strName);
12698 multilock.acquire();
12699 }
12700 }
12701 }
12702 }
12703
12704 /*
12705 * An expected uninitialization can come only from #i_checkForDeath().
12706 * Otherwise it means that something's gone really wrong (for example,
12707 * the Session implementation has released the VirtualBox reference
12708 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12709 * etc). However, it's also possible, that the client releases the IPC
12710 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12711 * but the VirtualBox release event comes first to the server process.
12712 * This case is practically possible, so we should not assert on an
12713 * unexpected uninit, just log a warning.
12714 */
12715
12716 if (aReason == Uninit::Unexpected)
12717 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12718
12719 if (aReason != Uninit::Normal)
12720 {
12721 mData->mSession.mDirectControl.setNull();
12722 }
12723 else
12724 {
12725 /* this must be null here (see #OnSessionEnd()) */
12726 Assert(mData->mSession.mDirectControl.isNull());
12727 Assert(mData->mSession.mState == SessionState_Unlocking);
12728 Assert(!mData->mSession.mProgress.isNull());
12729 }
12730 if (mData->mSession.mProgress)
12731 {
12732 if (aReason == Uninit::Normal)
12733 mData->mSession.mProgress->i_notifyComplete(S_OK);
12734 else
12735 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12736 COM_IIDOF(ISession),
12737 getComponentName(),
12738 tr("The VM session was aborted"));
12739 mData->mSession.mProgress.setNull();
12740 }
12741
12742 if (mConsoleTaskData.mProgress)
12743 {
12744 Assert(aReason == Uninit::Abnormal);
12745 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12746 COM_IIDOF(ISession),
12747 getComponentName(),
12748 tr("The VM session was aborted"));
12749 mConsoleTaskData.mProgress.setNull();
12750 }
12751
12752 /* remove the association between the peer machine and this session machine */
12753 Assert( (SessionMachine*)mData->mSession.mMachine == this
12754 || aReason == Uninit::Unexpected);
12755
12756 /* reset the rest of session data */
12757 mData->mSession.mLockType = LockType_Null;
12758 mData->mSession.mMachine.setNull();
12759 mData->mSession.mState = SessionState_Unlocked;
12760 mData->mSession.mName.setNull();
12761
12762 /* destroy the machine client token before leaving the exclusive lock */
12763 if (mClientToken)
12764 {
12765 delete mClientToken;
12766 mClientToken = NULL;
12767 }
12768
12769 /* fire an event */
12770 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12771
12772 uninitDataAndChildObjects();
12773
12774 /* free the essential data structure last */
12775 mData.free();
12776
12777 /* release the exclusive lock before setting the below two to NULL */
12778 multilock.release();
12779
12780 unconst(mParent) = NULL;
12781 unconst(mPeer) = NULL;
12782
12783 AuthLibUnload(&mAuthLibCtx);
12784
12785 LogFlowThisFuncLeave();
12786}
12787
12788// util::Lockable interface
12789////////////////////////////////////////////////////////////////////////////////
12790
12791/**
12792 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12793 * with the primary Machine instance (mPeer).
12794 */
12795RWLockHandle *SessionMachine::lockHandle() const
12796{
12797 AssertReturn(mPeer != NULL, NULL);
12798 return mPeer->lockHandle();
12799}
12800
12801// IInternalMachineControl methods
12802////////////////////////////////////////////////////////////////////////////////
12803
12804/**
12805 * Passes collected guest statistics to performance collector object
12806 */
12807HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12808 ULONG aCpuKernel, ULONG aCpuIdle,
12809 ULONG aMemTotal, ULONG aMemFree,
12810 ULONG aMemBalloon, ULONG aMemShared,
12811 ULONG aMemCache, ULONG aPageTotal,
12812 ULONG aAllocVMM, ULONG aFreeVMM,
12813 ULONG aBalloonedVMM, ULONG aSharedVMM,
12814 ULONG aVmNetRx, ULONG aVmNetTx)
12815{
12816#ifdef VBOX_WITH_RESOURCE_USAGE_API
12817 if (mCollectorGuest)
12818 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12819 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12820 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12821 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12822
12823 return S_OK;
12824#else
12825 NOREF(aValidStats);
12826 NOREF(aCpuUser);
12827 NOREF(aCpuKernel);
12828 NOREF(aCpuIdle);
12829 NOREF(aMemTotal);
12830 NOREF(aMemFree);
12831 NOREF(aMemBalloon);
12832 NOREF(aMemShared);
12833 NOREF(aMemCache);
12834 NOREF(aPageTotal);
12835 NOREF(aAllocVMM);
12836 NOREF(aFreeVMM);
12837 NOREF(aBalloonedVMM);
12838 NOREF(aSharedVMM);
12839 NOREF(aVmNetRx);
12840 NOREF(aVmNetTx);
12841 return E_NOTIMPL;
12842#endif
12843}
12844
12845////////////////////////////////////////////////////////////////////////////////
12846//
12847// SessionMachine task records
12848//
12849////////////////////////////////////////////////////////////////////////////////
12850
12851/**
12852 * Task record for saving the machine state.
12853 */
12854class SessionMachine::SaveStateTask
12855 : public Machine::Task
12856{
12857public:
12858 SaveStateTask(SessionMachine *m,
12859 Progress *p,
12860 const Utf8Str &t,
12861 Reason_T enmReason,
12862 const Utf8Str &strStateFilePath)
12863 : Task(m, p, t),
12864 m_enmReason(enmReason),
12865 m_strStateFilePath(strStateFilePath)
12866 {}
12867
12868private:
12869 void handler()
12870 {
12871 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12872 }
12873
12874 Reason_T m_enmReason;
12875 Utf8Str m_strStateFilePath;
12876
12877 friend class SessionMachine;
12878};
12879
12880/**
12881 * Task thread implementation for SessionMachine::SaveState(), called from
12882 * SessionMachine::taskHandler().
12883 *
12884 * @note Locks this object for writing.
12885 *
12886 * @param task
12887 * @return
12888 */
12889void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12890{
12891 LogFlowThisFuncEnter();
12892
12893 AutoCaller autoCaller(this);
12894 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12895 if (FAILED(autoCaller.rc()))
12896 {
12897 /* we might have been uninitialized because the session was accidentally
12898 * closed by the client, so don't assert */
12899 HRESULT rc = setError(E_FAIL,
12900 tr("The session has been accidentally closed"));
12901 task.m_pProgress->i_notifyComplete(rc);
12902 LogFlowThisFuncLeave();
12903 return;
12904 }
12905
12906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12907
12908 HRESULT rc = S_OK;
12909
12910 try
12911 {
12912 ComPtr<IInternalSessionControl> directControl;
12913 if (mData->mSession.mLockType == LockType_VM)
12914 directControl = mData->mSession.mDirectControl;
12915 if (directControl.isNull())
12916 throw setError(VBOX_E_INVALID_VM_STATE,
12917 tr("Trying to save state without a running VM"));
12918 alock.release();
12919 BOOL fSuspendedBySave;
12920 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12921 Assert(!fSuspendedBySave);
12922 alock.acquire();
12923
12924 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12925 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12926 throw E_FAIL);
12927
12928 if (SUCCEEDED(rc))
12929 {
12930 mSSData->strStateFilePath = task.m_strStateFilePath;
12931
12932 /* save all VM settings */
12933 rc = i_saveSettings(NULL);
12934 // no need to check whether VirtualBox.xml needs saving also since
12935 // we can't have a name change pending at this point
12936 }
12937 else
12938 {
12939 // On failure, set the state to the state we had at the beginning.
12940 i_setMachineState(task.m_machineStateBackup);
12941 i_updateMachineStateOnClient();
12942
12943 // Delete the saved state file (might have been already created).
12944 // No need to check whether this is shared with a snapshot here
12945 // because we certainly created a fresh saved state file here.
12946 RTFileDelete(task.m_strStateFilePath.c_str());
12947 }
12948 }
12949 catch (HRESULT aRC) { rc = aRC; }
12950
12951 task.m_pProgress->i_notifyComplete(rc);
12952
12953 LogFlowThisFuncLeave();
12954}
12955
12956/**
12957 * @note Locks this object for writing.
12958 */
12959HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12960{
12961 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12962}
12963
12964HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12965{
12966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12967
12968 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12969 if (FAILED(rc)) return rc;
12970
12971 if ( mData->mMachineState != MachineState_Running
12972 && mData->mMachineState != MachineState_Paused
12973 )
12974 return setError(VBOX_E_INVALID_VM_STATE,
12975 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12976 Global::stringifyMachineState(mData->mMachineState));
12977
12978 ComObjPtr<Progress> pProgress;
12979 pProgress.createObject();
12980 rc = pProgress->init(i_getVirtualBox(),
12981 static_cast<IMachine *>(this) /* aInitiator */,
12982 tr("Saving the execution state of the virtual machine"),
12983 FALSE /* aCancelable */);
12984 if (FAILED(rc))
12985 return rc;
12986
12987 Utf8Str strStateFilePath;
12988 i_composeSavedStateFilename(strStateFilePath);
12989
12990 /* create and start the task on a separate thread (note that it will not
12991 * start working until we release alock) */
12992 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12993 rc = pTask->createThread();
12994 if (FAILED(rc))
12995 return rc;
12996
12997 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12998 i_setMachineState(MachineState_Saving);
12999 i_updateMachineStateOnClient();
13000
13001 pProgress.queryInterfaceTo(aProgress.asOutParam());
13002
13003 return S_OK;
13004}
13005
13006/**
13007 * @note Locks this object for writing.
13008 */
13009HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13010{
13011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13012
13013 HRESULT rc = i_checkStateDependency(MutableStateDep);
13014 if (FAILED(rc)) return rc;
13015
13016 if ( mData->mMachineState != MachineState_PoweredOff
13017 && mData->mMachineState != MachineState_Teleported
13018 && mData->mMachineState != MachineState_Aborted
13019 )
13020 return setError(VBOX_E_INVALID_VM_STATE,
13021 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13022 Global::stringifyMachineState(mData->mMachineState));
13023
13024 com::Utf8Str stateFilePathFull;
13025 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13026 if (RT_FAILURE(vrc))
13027 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13028 tr("Invalid saved state file path '%s' (%Rrc)"),
13029 aSavedStateFile.c_str(),
13030 vrc);
13031
13032 mSSData->strStateFilePath = stateFilePathFull;
13033
13034 /* The below i_setMachineState() will detect the state transition and will
13035 * update the settings file */
13036
13037 return i_setMachineState(MachineState_Saved);
13038}
13039
13040/**
13041 * @note Locks this object for writing.
13042 */
13043HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13044{
13045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13046
13047 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13048 if (FAILED(rc)) return rc;
13049
13050 if (mData->mMachineState != MachineState_Saved)
13051 return setError(VBOX_E_INVALID_VM_STATE,
13052 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13053 Global::stringifyMachineState(mData->mMachineState));
13054
13055 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13056
13057 /*
13058 * Saved -> PoweredOff transition will be detected in the SessionMachine
13059 * and properly handled.
13060 */
13061 rc = i_setMachineState(MachineState_PoweredOff);
13062 return rc;
13063}
13064
13065
13066/**
13067 * @note Locks the same as #i_setMachineState() does.
13068 */
13069HRESULT SessionMachine::updateState(MachineState_T aState)
13070{
13071 return i_setMachineState(aState);
13072}
13073
13074/**
13075 * @note Locks this object for writing.
13076 */
13077HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13078{
13079 IProgress *pProgress(aProgress);
13080
13081 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13082
13083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13084
13085 if (mData->mSession.mState != SessionState_Locked)
13086 return VBOX_E_INVALID_OBJECT_STATE;
13087
13088 if (!mData->mSession.mProgress.isNull())
13089 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13090
13091 /* If we didn't reference the NAT network service yet, add a reference to
13092 * force a start */
13093 if (miNATNetworksStarted < 1)
13094 {
13095 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13096 {
13097 BOOL enabled;
13098 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13099 if ( FAILED(hrc)
13100 || !enabled)
13101 continue;
13102
13103 NetworkAttachmentType_T type;
13104 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13105 if ( SUCCEEDED(hrc)
13106 && type == NetworkAttachmentType_NATNetwork)
13107 {
13108 Bstr name;
13109 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13110 if (SUCCEEDED(hrc))
13111 {
13112 Utf8Str strName(name);
13113 LogRel(("VM '%s' starts using NAT network '%s'\n",
13114 mUserData->s.strName.c_str(), strName.c_str()));
13115 mPeer->lockHandle()->unlockWrite();
13116 mParent->i_natNetworkRefInc(strName);
13117#ifdef RT_LOCK_STRICT
13118 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13119#else
13120 mPeer->lockHandle()->lockWrite();
13121#endif
13122 }
13123 }
13124 }
13125 miNATNetworksStarted++;
13126 }
13127
13128 LogFlowThisFunc(("returns S_OK.\n"));
13129 return S_OK;
13130}
13131
13132/**
13133 * @note Locks this object for writing.
13134 */
13135HRESULT SessionMachine::endPowerUp(LONG aResult)
13136{
13137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13138
13139 if (mData->mSession.mState != SessionState_Locked)
13140 return VBOX_E_INVALID_OBJECT_STATE;
13141
13142 /* Finalize the LaunchVMProcess progress object. */
13143 if (mData->mSession.mProgress)
13144 {
13145 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13146 mData->mSession.mProgress.setNull();
13147 }
13148
13149 if (SUCCEEDED((HRESULT)aResult))
13150 {
13151#ifdef VBOX_WITH_RESOURCE_USAGE_API
13152 /* The VM has been powered up successfully, so it makes sense
13153 * now to offer the performance metrics for a running machine
13154 * object. Doing it earlier wouldn't be safe. */
13155 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13156 mData->mSession.mPID);
13157#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13158 }
13159
13160 return S_OK;
13161}
13162
13163/**
13164 * @note Locks this object for writing.
13165 */
13166HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13167{
13168 LogFlowThisFuncEnter();
13169
13170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13171
13172 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13173 E_FAIL);
13174
13175 /* create a progress object to track operation completion */
13176 ComObjPtr<Progress> pProgress;
13177 pProgress.createObject();
13178 pProgress->init(i_getVirtualBox(),
13179 static_cast<IMachine *>(this) /* aInitiator */,
13180 tr("Stopping the virtual machine"),
13181 FALSE /* aCancelable */);
13182
13183 /* fill in the console task data */
13184 mConsoleTaskData.mLastState = mData->mMachineState;
13185 mConsoleTaskData.mProgress = pProgress;
13186
13187 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13188 i_setMachineState(MachineState_Stopping);
13189
13190 pProgress.queryInterfaceTo(aProgress.asOutParam());
13191
13192 return S_OK;
13193}
13194
13195/**
13196 * @note Locks this object for writing.
13197 */
13198HRESULT SessionMachine::endPoweringDown(LONG aResult,
13199 const com::Utf8Str &aErrMsg)
13200{
13201 LogFlowThisFuncEnter();
13202
13203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13204
13205 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13206 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13207 && mConsoleTaskData.mLastState != MachineState_Null,
13208 E_FAIL);
13209
13210 /*
13211 * On failure, set the state to the state we had when BeginPoweringDown()
13212 * was called (this is expected by Console::PowerDown() and the associated
13213 * task). On success the VM process already changed the state to
13214 * MachineState_PoweredOff, so no need to do anything.
13215 */
13216 if (FAILED(aResult))
13217 i_setMachineState(mConsoleTaskData.mLastState);
13218
13219 /* notify the progress object about operation completion */
13220 Assert(mConsoleTaskData.mProgress);
13221 if (SUCCEEDED(aResult))
13222 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13223 else
13224 {
13225 if (aErrMsg.length())
13226 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13227 COM_IIDOF(ISession),
13228 getComponentName(),
13229 aErrMsg.c_str());
13230 else
13231 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13232 }
13233
13234 /* clear out the temporary saved state data */
13235 mConsoleTaskData.mLastState = MachineState_Null;
13236 mConsoleTaskData.mProgress.setNull();
13237
13238 LogFlowThisFuncLeave();
13239 return S_OK;
13240}
13241
13242
13243/**
13244 * Goes through the USB filters of the given machine to see if the given
13245 * device matches any filter or not.
13246 *
13247 * @note Locks the same as USBController::hasMatchingFilter() does.
13248 */
13249HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13250 BOOL *aMatched,
13251 ULONG *aMaskedInterfaces)
13252{
13253 LogFlowThisFunc(("\n"));
13254
13255#ifdef VBOX_WITH_USB
13256 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13257#else
13258 NOREF(aDevice);
13259 NOREF(aMaskedInterfaces);
13260 *aMatched = FALSE;
13261#endif
13262
13263 return S_OK;
13264}
13265
13266/**
13267 * @note Locks the same as Host::captureUSBDevice() does.
13268 */
13269HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13270{
13271 LogFlowThisFunc(("\n"));
13272
13273#ifdef VBOX_WITH_USB
13274 /* if captureDeviceForVM() fails, it must have set extended error info */
13275 clearError();
13276 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13277 if (FAILED(rc)) return rc;
13278
13279 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13280 AssertReturn(service, E_FAIL);
13281 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13282#else
13283 NOREF(aId);
13284 return E_NOTIMPL;
13285#endif
13286}
13287
13288/**
13289 * @note Locks the same as Host::detachUSBDevice() does.
13290 */
13291HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13292 BOOL aDone)
13293{
13294 LogFlowThisFunc(("\n"));
13295
13296#ifdef VBOX_WITH_USB
13297 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13298 AssertReturn(service, E_FAIL);
13299 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13300#else
13301 NOREF(aId);
13302 NOREF(aDone);
13303 return E_NOTIMPL;
13304#endif
13305}
13306
13307/**
13308 * Inserts all machine filters to the USB proxy service and then calls
13309 * Host::autoCaptureUSBDevices().
13310 *
13311 * Called by Console from the VM process upon VM startup.
13312 *
13313 * @note Locks what called methods lock.
13314 */
13315HRESULT SessionMachine::autoCaptureUSBDevices()
13316{
13317 LogFlowThisFunc(("\n"));
13318
13319#ifdef VBOX_WITH_USB
13320 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13321 AssertComRC(rc);
13322 NOREF(rc);
13323
13324 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13325 AssertReturn(service, E_FAIL);
13326 return service->autoCaptureDevicesForVM(this);
13327#else
13328 return S_OK;
13329#endif
13330}
13331
13332/**
13333 * Removes all machine filters from the USB proxy service and then calls
13334 * Host::detachAllUSBDevices().
13335 *
13336 * Called by Console from the VM process upon normal VM termination or by
13337 * SessionMachine::uninit() upon abnormal VM termination (from under the
13338 * Machine/SessionMachine lock).
13339 *
13340 * @note Locks what called methods lock.
13341 */
13342HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13343{
13344 LogFlowThisFunc(("\n"));
13345
13346#ifdef VBOX_WITH_USB
13347 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13348 AssertComRC(rc);
13349 NOREF(rc);
13350
13351 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13352 AssertReturn(service, E_FAIL);
13353 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13354#else
13355 NOREF(aDone);
13356 return S_OK;
13357#endif
13358}
13359
13360/**
13361 * @note Locks this object for writing.
13362 */
13363HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13364 ComPtr<IProgress> &aProgress)
13365{
13366 LogFlowThisFuncEnter();
13367
13368 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13369 /*
13370 * We don't assert below because it might happen that a non-direct session
13371 * informs us it is closed right after we've been uninitialized -- it's ok.
13372 */
13373
13374 /* get IInternalSessionControl interface */
13375 ComPtr<IInternalSessionControl> control(aSession);
13376
13377 ComAssertRet(!control.isNull(), E_INVALIDARG);
13378
13379 /* Creating a Progress object requires the VirtualBox lock, and
13380 * thus locking it here is required by the lock order rules. */
13381 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13382
13383 if (control == mData->mSession.mDirectControl)
13384 {
13385 /* The direct session is being normally closed by the client process
13386 * ----------------------------------------------------------------- */
13387
13388 /* go to the closing state (essential for all open*Session() calls and
13389 * for #i_checkForDeath()) */
13390 Assert(mData->mSession.mState == SessionState_Locked);
13391 mData->mSession.mState = SessionState_Unlocking;
13392
13393 /* set direct control to NULL to release the remote instance */
13394 mData->mSession.mDirectControl.setNull();
13395 LogFlowThisFunc(("Direct control is set to NULL\n"));
13396
13397 if (mData->mSession.mProgress)
13398 {
13399 /* finalize the progress, someone might wait if a frontend
13400 * closes the session before powering on the VM. */
13401 mData->mSession.mProgress->notifyComplete(E_FAIL,
13402 COM_IIDOF(ISession),
13403 getComponentName(),
13404 tr("The VM session was closed before any attempt to power it on"));
13405 mData->mSession.mProgress.setNull();
13406 }
13407
13408 /* Create the progress object the client will use to wait until
13409 * #i_checkForDeath() is called to uninitialize this session object after
13410 * it releases the IPC semaphore.
13411 * Note! Because we're "reusing" mProgress here, this must be a proxy
13412 * object just like for LaunchVMProcess. */
13413 Assert(mData->mSession.mProgress.isNull());
13414 ComObjPtr<ProgressProxy> progress;
13415 progress.createObject();
13416 ComPtr<IUnknown> pPeer(mPeer);
13417 progress->init(mParent, pPeer,
13418 Bstr(tr("Closing session")).raw(),
13419 FALSE /* aCancelable */);
13420 progress.queryInterfaceTo(aProgress.asOutParam());
13421 mData->mSession.mProgress = progress;
13422 }
13423 else
13424 {
13425 /* the remote session is being normally closed */
13426 bool found = false;
13427 for (Data::Session::RemoteControlList::iterator
13428 it = mData->mSession.mRemoteControls.begin();
13429 it != mData->mSession.mRemoteControls.end();
13430 ++it)
13431 {
13432 if (control == *it)
13433 {
13434 found = true;
13435 // This MUST be erase(it), not remove(*it) as the latter
13436 // triggers a very nasty use after free due to the place where
13437 // the value "lives".
13438 mData->mSession.mRemoteControls.erase(it);
13439 break;
13440 }
13441 }
13442 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13443 E_INVALIDARG);
13444 }
13445
13446 /* signal the client watcher thread, because the client is going away */
13447 mParent->i_updateClientWatcher();
13448
13449 LogFlowThisFuncLeave();
13450 return S_OK;
13451}
13452
13453HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13454 std::vector<com::Utf8Str> &aValues,
13455 std::vector<LONG64> &aTimestamps,
13456 std::vector<com::Utf8Str> &aFlags)
13457{
13458 LogFlowThisFunc(("\n"));
13459
13460#ifdef VBOX_WITH_GUEST_PROPS
13461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13462
13463 size_t cEntries = mHWData->mGuestProperties.size();
13464 aNames.resize(cEntries);
13465 aValues.resize(cEntries);
13466 aTimestamps.resize(cEntries);
13467 aFlags.resize(cEntries);
13468
13469 size_t i = 0;
13470 for (HWData::GuestPropertyMap::const_iterator
13471 it = mHWData->mGuestProperties.begin();
13472 it != mHWData->mGuestProperties.end();
13473 ++it, ++i)
13474 {
13475 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13476 aNames[i] = it->first;
13477 aValues[i] = it->second.strValue;
13478 aTimestamps[i] = it->second.mTimestamp;
13479
13480 /* If it is NULL, keep it NULL. */
13481 if (it->second.mFlags)
13482 {
13483 GuestPropWriteFlags(it->second.mFlags, szFlags);
13484 aFlags[i] = szFlags;
13485 }
13486 else
13487 aFlags[i] = "";
13488 }
13489 return S_OK;
13490#else
13491 ReturnComNotImplemented();
13492#endif
13493}
13494
13495HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13496 const com::Utf8Str &aValue,
13497 LONG64 aTimestamp,
13498 const com::Utf8Str &aFlags)
13499{
13500 LogFlowThisFunc(("\n"));
13501
13502#ifdef VBOX_WITH_GUEST_PROPS
13503 try
13504 {
13505 /*
13506 * Convert input up front.
13507 */
13508 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13509 if (aFlags.length())
13510 {
13511 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13512 AssertRCReturn(vrc, E_INVALIDARG);
13513 }
13514
13515 /*
13516 * Now grab the object lock, validate the state and do the update.
13517 */
13518
13519 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13520
13521 if (!Global::IsOnline(mData->mMachineState))
13522 {
13523 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13524 VBOX_E_INVALID_VM_STATE);
13525 }
13526
13527 i_setModified(IsModified_MachineData);
13528 mHWData.backup();
13529
13530 bool fDelete = !aValue.length();
13531 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13532 if (it != mHWData->mGuestProperties.end())
13533 {
13534 if (!fDelete)
13535 {
13536 it->second.strValue = aValue;
13537 it->second.mTimestamp = aTimestamp;
13538 it->second.mFlags = fFlags;
13539 }
13540 else
13541 mHWData->mGuestProperties.erase(it);
13542
13543 mData->mGuestPropertiesModified = TRUE;
13544 }
13545 else if (!fDelete)
13546 {
13547 HWData::GuestProperty prop;
13548 prop.strValue = aValue;
13549 prop.mTimestamp = aTimestamp;
13550 prop.mFlags = fFlags;
13551
13552 mHWData->mGuestProperties[aName] = prop;
13553 mData->mGuestPropertiesModified = TRUE;
13554 }
13555
13556 alock.release();
13557
13558 mParent->i_onGuestPropertyChange(mData->mUuid,
13559 Bstr(aName).raw(),
13560 Bstr(aValue).raw(),
13561 Bstr(aFlags).raw());
13562 }
13563 catch (...)
13564 {
13565 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13566 }
13567 return S_OK;
13568#else
13569 ReturnComNotImplemented();
13570#endif
13571}
13572
13573
13574HRESULT SessionMachine::lockMedia()
13575{
13576 AutoMultiWriteLock2 alock(this->lockHandle(),
13577 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13578
13579 AssertReturn( mData->mMachineState == MachineState_Starting
13580 || mData->mMachineState == MachineState_Restoring
13581 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13582
13583 clearError();
13584 alock.release();
13585 return i_lockMedia();
13586}
13587
13588HRESULT SessionMachine::unlockMedia()
13589{
13590 HRESULT hrc = i_unlockMedia();
13591 return hrc;
13592}
13593
13594HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13595 ComPtr<IMediumAttachment> &aNewAttachment)
13596{
13597 // request the host lock first, since might be calling Host methods for getting host drives;
13598 // next, protect the media tree all the while we're in here, as well as our member variables
13599 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13600 this->lockHandle(),
13601 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13602
13603 IMediumAttachment *iAttach = aAttachment;
13604 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13605
13606 Utf8Str ctrlName;
13607 LONG lPort;
13608 LONG lDevice;
13609 bool fTempEject;
13610 {
13611 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13612
13613 /* Need to query the details first, as the IMediumAttachment reference
13614 * might be to the original settings, which we are going to change. */
13615 ctrlName = pAttach->i_getControllerName();
13616 lPort = pAttach->i_getPort();
13617 lDevice = pAttach->i_getDevice();
13618 fTempEject = pAttach->i_getTempEject();
13619 }
13620
13621 if (!fTempEject)
13622 {
13623 /* Remember previously mounted medium. The medium before taking the
13624 * backup is not necessarily the same thing. */
13625 ComObjPtr<Medium> oldmedium;
13626 oldmedium = pAttach->i_getMedium();
13627
13628 i_setModified(IsModified_Storage);
13629 mMediumAttachments.backup();
13630
13631 // The backup operation makes the pAttach reference point to the
13632 // old settings. Re-get the correct reference.
13633 pAttach = i_findAttachment(*mMediumAttachments.data(),
13634 ctrlName,
13635 lPort,
13636 lDevice);
13637
13638 {
13639 AutoCaller autoAttachCaller(this);
13640 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13641
13642 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13643 if (!oldmedium.isNull())
13644 oldmedium->i_removeBackReference(mData->mUuid);
13645
13646 pAttach->i_updateMedium(NULL);
13647 pAttach->i_updateEjected();
13648 }
13649
13650 i_setModified(IsModified_Storage);
13651 }
13652 else
13653 {
13654 {
13655 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13656 pAttach->i_updateEjected();
13657 }
13658 }
13659
13660 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13661
13662 return S_OK;
13663}
13664
13665HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13666 com::Utf8Str &aResult)
13667{
13668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13669
13670 HRESULT hr = S_OK;
13671
13672 if (!mAuthLibCtx.hAuthLibrary)
13673 {
13674 /* Load the external authentication library. */
13675 Bstr authLibrary;
13676 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13677
13678 Utf8Str filename = authLibrary;
13679
13680 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13681 if (RT_FAILURE(vrc))
13682 hr = setErrorBoth(E_FAIL, vrc,
13683 tr("Could not load the external authentication library '%s' (%Rrc)"),
13684 filename.c_str(), vrc);
13685 }
13686
13687 /* The auth library might need the machine lock. */
13688 alock.release();
13689
13690 if (FAILED(hr))
13691 return hr;
13692
13693 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13694 {
13695 enum VRDEAuthParams
13696 {
13697 parmUuid = 1,
13698 parmGuestJudgement,
13699 parmUser,
13700 parmPassword,
13701 parmDomain,
13702 parmClientId
13703 };
13704
13705 AuthResult result = AuthResultAccessDenied;
13706
13707 Guid uuid(aAuthParams[parmUuid]);
13708 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13709 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13710
13711 result = AuthLibAuthenticate(&mAuthLibCtx,
13712 uuid.raw(), guestJudgement,
13713 aAuthParams[parmUser].c_str(),
13714 aAuthParams[parmPassword].c_str(),
13715 aAuthParams[parmDomain].c_str(),
13716 u32ClientId);
13717
13718 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13719 size_t cbPassword = aAuthParams[parmPassword].length();
13720 if (cbPassword)
13721 {
13722 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13723 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13724 }
13725
13726 if (result == AuthResultAccessGranted)
13727 aResult = "granted";
13728 else
13729 aResult = "denied";
13730
13731 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13732 aAuthParams[parmUser].c_str(), aResult.c_str()));
13733 }
13734 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13735 {
13736 enum VRDEAuthDisconnectParams
13737 {
13738 parmUuid = 1,
13739 parmClientId
13740 };
13741
13742 Guid uuid(aAuthParams[parmUuid]);
13743 uint32_t u32ClientId = 0;
13744 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13745 }
13746 else
13747 {
13748 hr = E_INVALIDARG;
13749 }
13750
13751 return hr;
13752}
13753
13754// public methods only for internal purposes
13755/////////////////////////////////////////////////////////////////////////////
13756
13757#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13758/**
13759 * Called from the client watcher thread to check for expected or unexpected
13760 * death of the client process that has a direct session to this machine.
13761 *
13762 * On Win32 and on OS/2, this method is called only when we've got the
13763 * mutex (i.e. the client has either died or terminated normally) so it always
13764 * returns @c true (the client is terminated, the session machine is
13765 * uninitialized).
13766 *
13767 * On other platforms, the method returns @c true if the client process has
13768 * terminated normally or abnormally and the session machine was uninitialized,
13769 * and @c false if the client process is still alive.
13770 *
13771 * @note Locks this object for writing.
13772 */
13773bool SessionMachine::i_checkForDeath()
13774{
13775 Uninit::Reason reason;
13776 bool terminated = false;
13777
13778 /* Enclose autoCaller with a block because calling uninit() from under it
13779 * will deadlock. */
13780 {
13781 AutoCaller autoCaller(this);
13782 if (!autoCaller.isOk())
13783 {
13784 /* return true if not ready, to cause the client watcher to exclude
13785 * the corresponding session from watching */
13786 LogFlowThisFunc(("Already uninitialized!\n"));
13787 return true;
13788 }
13789
13790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13791
13792 /* Determine the reason of death: if the session state is Closing here,
13793 * everything is fine. Otherwise it means that the client did not call
13794 * OnSessionEnd() before it released the IPC semaphore. This may happen
13795 * either because the client process has abnormally terminated, or
13796 * because it simply forgot to call ISession::Close() before exiting. We
13797 * threat the latter also as an abnormal termination (see
13798 * Session::uninit() for details). */
13799 reason = mData->mSession.mState == SessionState_Unlocking ?
13800 Uninit::Normal :
13801 Uninit::Abnormal;
13802
13803 if (mClientToken)
13804 terminated = mClientToken->release();
13805 } /* AutoCaller block */
13806
13807 if (terminated)
13808 uninit(reason);
13809
13810 return terminated;
13811}
13812
13813void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13814{
13815 LogFlowThisFunc(("\n"));
13816
13817 strTokenId.setNull();
13818
13819 AutoCaller autoCaller(this);
13820 AssertComRCReturnVoid(autoCaller.rc());
13821
13822 Assert(mClientToken);
13823 if (mClientToken)
13824 mClientToken->getId(strTokenId);
13825}
13826#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13827IToken *SessionMachine::i_getToken()
13828{
13829 LogFlowThisFunc(("\n"));
13830
13831 AutoCaller autoCaller(this);
13832 AssertComRCReturn(autoCaller.rc(), NULL);
13833
13834 Assert(mClientToken);
13835 if (mClientToken)
13836 return mClientToken->getToken();
13837 else
13838 return NULL;
13839}
13840#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13841
13842Machine::ClientToken *SessionMachine::i_getClientToken()
13843{
13844 LogFlowThisFunc(("\n"));
13845
13846 AutoCaller autoCaller(this);
13847 AssertComRCReturn(autoCaller.rc(), NULL);
13848
13849 return mClientToken;
13850}
13851
13852
13853/**
13854 * @note Locks this object for reading.
13855 */
13856HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13857{
13858 LogFlowThisFunc(("\n"));
13859
13860 AutoCaller autoCaller(this);
13861 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13862
13863 ComPtr<IInternalSessionControl> directControl;
13864 {
13865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13866 if (mData->mSession.mLockType == LockType_VM)
13867 directControl = mData->mSession.mDirectControl;
13868 }
13869
13870 /* ignore notifications sent after #OnSessionEnd() is called */
13871 if (!directControl)
13872 return S_OK;
13873
13874 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13875}
13876
13877/**
13878 * @note Locks this object for reading.
13879 */
13880HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13881 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13882 IN_BSTR aGuestIp, LONG aGuestPort)
13883{
13884 LogFlowThisFunc(("\n"));
13885
13886 AutoCaller autoCaller(this);
13887 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13888
13889 ComPtr<IInternalSessionControl> directControl;
13890 {
13891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13892 if (mData->mSession.mLockType == LockType_VM)
13893 directControl = mData->mSession.mDirectControl;
13894 }
13895
13896 /* ignore notifications sent after #OnSessionEnd() is called */
13897 if (!directControl)
13898 return S_OK;
13899 /*
13900 * instead acting like callback we ask IVirtualBox deliver corresponding event
13901 */
13902
13903 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13904 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13905 return S_OK;
13906}
13907
13908/**
13909 * @note Locks this object for reading.
13910 */
13911HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13912{
13913 LogFlowThisFunc(("\n"));
13914
13915 AutoCaller autoCaller(this);
13916 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13917
13918 ComPtr<IInternalSessionControl> directControl;
13919 {
13920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13921 if (mData->mSession.mLockType == LockType_VM)
13922 directControl = mData->mSession.mDirectControl;
13923 }
13924
13925 /* ignore notifications sent after #OnSessionEnd() is called */
13926 if (!directControl)
13927 return S_OK;
13928
13929 return directControl->OnAudioAdapterChange(audioAdapter);
13930}
13931
13932/**
13933 * @note Locks this object for reading.
13934 */
13935HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13936{
13937 LogFlowThisFunc(("\n"));
13938
13939 AutoCaller autoCaller(this);
13940 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13941
13942 ComPtr<IInternalSessionControl> directControl;
13943 {
13944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13945 if (mData->mSession.mLockType == LockType_VM)
13946 directControl = mData->mSession.mDirectControl;
13947 }
13948
13949 /* ignore notifications sent after #OnSessionEnd() is called */
13950 if (!directControl)
13951 return S_OK;
13952
13953 return directControl->OnSerialPortChange(serialPort);
13954}
13955
13956/**
13957 * @note Locks this object for reading.
13958 */
13959HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13960{
13961 LogFlowThisFunc(("\n"));
13962
13963 AutoCaller autoCaller(this);
13964 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13965
13966 ComPtr<IInternalSessionControl> directControl;
13967 {
13968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13969 if (mData->mSession.mLockType == LockType_VM)
13970 directControl = mData->mSession.mDirectControl;
13971 }
13972
13973 /* ignore notifications sent after #OnSessionEnd() is called */
13974 if (!directControl)
13975 return S_OK;
13976
13977 return directControl->OnParallelPortChange(parallelPort);
13978}
13979
13980/**
13981 * @note Locks this object for reading.
13982 */
13983HRESULT SessionMachine::i_onStorageControllerChange()
13984{
13985 LogFlowThisFunc(("\n"));
13986
13987 AutoCaller autoCaller(this);
13988 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13989
13990 ComPtr<IInternalSessionControl> directControl;
13991 {
13992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13993 if (mData->mSession.mLockType == LockType_VM)
13994 directControl = mData->mSession.mDirectControl;
13995 }
13996
13997 /* ignore notifications sent after #OnSessionEnd() is called */
13998 if (!directControl)
13999 return S_OK;
14000
14001 return directControl->OnStorageControllerChange();
14002}
14003
14004/**
14005 * @note Locks this object for reading.
14006 */
14007HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14008{
14009 LogFlowThisFunc(("\n"));
14010
14011 AutoCaller autoCaller(this);
14012 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14013
14014 ComPtr<IInternalSessionControl> directControl;
14015 {
14016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14017 if (mData->mSession.mLockType == LockType_VM)
14018 directControl = mData->mSession.mDirectControl;
14019 }
14020
14021 /* ignore notifications sent after #OnSessionEnd() is called */
14022 if (!directControl)
14023 return S_OK;
14024
14025 return directControl->OnMediumChange(aAttachment, aForce);
14026}
14027
14028/**
14029 * @note Locks this object for reading.
14030 */
14031HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14032{
14033 LogFlowThisFunc(("\n"));
14034
14035 AutoCaller autoCaller(this);
14036 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14037
14038 ComPtr<IInternalSessionControl> directControl;
14039 {
14040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14041 if (mData->mSession.mLockType == LockType_VM)
14042 directControl = mData->mSession.mDirectControl;
14043 }
14044
14045 /* ignore notifications sent after #OnSessionEnd() is called */
14046 if (!directControl)
14047 return S_OK;
14048
14049 return directControl->OnCPUChange(aCPU, aRemove);
14050}
14051
14052HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14053{
14054 LogFlowThisFunc(("\n"));
14055
14056 AutoCaller autoCaller(this);
14057 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14058
14059 ComPtr<IInternalSessionControl> directControl;
14060 {
14061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14062 if (mData->mSession.mLockType == LockType_VM)
14063 directControl = mData->mSession.mDirectControl;
14064 }
14065
14066 /* ignore notifications sent after #OnSessionEnd() is called */
14067 if (!directControl)
14068 return S_OK;
14069
14070 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14071}
14072
14073/**
14074 * @note Locks this object for reading.
14075 */
14076HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14077{
14078 LogFlowThisFunc(("\n"));
14079
14080 AutoCaller autoCaller(this);
14081 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14082
14083 ComPtr<IInternalSessionControl> directControl;
14084 {
14085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14086 if (mData->mSession.mLockType == LockType_VM)
14087 directControl = mData->mSession.mDirectControl;
14088 }
14089
14090 /* ignore notifications sent after #OnSessionEnd() is called */
14091 if (!directControl)
14092 return S_OK;
14093
14094 return directControl->OnVRDEServerChange(aRestart);
14095}
14096
14097/**
14098 * @note Locks this object for reading.
14099 */
14100HRESULT SessionMachine::i_onCaptureChange()
14101{
14102 LogFlowThisFunc(("\n"));
14103
14104 AutoCaller autoCaller(this);
14105 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14106
14107 ComPtr<IInternalSessionControl> directControl;
14108 {
14109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14110 if (mData->mSession.mLockType == LockType_VM)
14111 directControl = mData->mSession.mDirectControl;
14112 }
14113
14114 /* ignore notifications sent after #OnSessionEnd() is called */
14115 if (!directControl)
14116 return S_OK;
14117
14118 return directControl->OnCaptureChange();
14119}
14120
14121/**
14122 * @note Locks this object for reading.
14123 */
14124HRESULT SessionMachine::i_onUSBControllerChange()
14125{
14126 LogFlowThisFunc(("\n"));
14127
14128 AutoCaller autoCaller(this);
14129 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14130
14131 ComPtr<IInternalSessionControl> directControl;
14132 {
14133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14134 if (mData->mSession.mLockType == LockType_VM)
14135 directControl = mData->mSession.mDirectControl;
14136 }
14137
14138 /* ignore notifications sent after #OnSessionEnd() is called */
14139 if (!directControl)
14140 return S_OK;
14141
14142 return directControl->OnUSBControllerChange();
14143}
14144
14145/**
14146 * @note Locks this object for reading.
14147 */
14148HRESULT SessionMachine::i_onSharedFolderChange()
14149{
14150 LogFlowThisFunc(("\n"));
14151
14152 AutoCaller autoCaller(this);
14153 AssertComRCReturnRC(autoCaller.rc());
14154
14155 ComPtr<IInternalSessionControl> directControl;
14156 {
14157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14158 if (mData->mSession.mLockType == LockType_VM)
14159 directControl = mData->mSession.mDirectControl;
14160 }
14161
14162 /* ignore notifications sent after #OnSessionEnd() is called */
14163 if (!directControl)
14164 return S_OK;
14165
14166 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14167}
14168
14169/**
14170 * @note Locks this object for reading.
14171 */
14172HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14173{
14174 LogFlowThisFunc(("\n"));
14175
14176 AutoCaller autoCaller(this);
14177 AssertComRCReturnRC(autoCaller.rc());
14178
14179 ComPtr<IInternalSessionControl> directControl;
14180 {
14181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14182 if (mData->mSession.mLockType == LockType_VM)
14183 directControl = mData->mSession.mDirectControl;
14184 }
14185
14186 /* ignore notifications sent after #OnSessionEnd() is called */
14187 if (!directControl)
14188 return S_OK;
14189
14190 return directControl->OnClipboardModeChange(aClipboardMode);
14191}
14192
14193/**
14194 * @note Locks this object for reading.
14195 */
14196HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14197{
14198 LogFlowThisFunc(("\n"));
14199
14200 AutoCaller autoCaller(this);
14201 AssertComRCReturnRC(autoCaller.rc());
14202
14203 ComPtr<IInternalSessionControl> directControl;
14204 {
14205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14206 if (mData->mSession.mLockType == LockType_VM)
14207 directControl = mData->mSession.mDirectControl;
14208 }
14209
14210 /* ignore notifications sent after #OnSessionEnd() is called */
14211 if (!directControl)
14212 return S_OK;
14213
14214 return directControl->OnDnDModeChange(aDnDMode);
14215}
14216
14217/**
14218 * @note Locks this object for reading.
14219 */
14220HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14221{
14222 LogFlowThisFunc(("\n"));
14223
14224 AutoCaller autoCaller(this);
14225 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14226
14227 ComPtr<IInternalSessionControl> directControl;
14228 {
14229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14230 if (mData->mSession.mLockType == LockType_VM)
14231 directControl = mData->mSession.mDirectControl;
14232 }
14233
14234 /* ignore notifications sent after #OnSessionEnd() is called */
14235 if (!directControl)
14236 return S_OK;
14237
14238 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14239}
14240
14241/**
14242 * @note Locks this object for reading.
14243 */
14244HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14245{
14246 LogFlowThisFunc(("\n"));
14247
14248 AutoCaller autoCaller(this);
14249 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14250
14251 ComPtr<IInternalSessionControl> directControl;
14252 {
14253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14254 if (mData->mSession.mLockType == LockType_VM)
14255 directControl = mData->mSession.mDirectControl;
14256 }
14257
14258 /* ignore notifications sent after #OnSessionEnd() is called */
14259 if (!directControl)
14260 return S_OK;
14261
14262 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14263}
14264
14265/**
14266 * Returns @c true if this machine's USB controller reports it has a matching
14267 * filter for the given USB device and @c false otherwise.
14268 *
14269 * @note locks this object for reading.
14270 */
14271bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14272{
14273 AutoCaller autoCaller(this);
14274 /* silently return if not ready -- this method may be called after the
14275 * direct machine session has been called */
14276 if (!autoCaller.isOk())
14277 return false;
14278
14279#ifdef VBOX_WITH_USB
14280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14281
14282 switch (mData->mMachineState)
14283 {
14284 case MachineState_Starting:
14285 case MachineState_Restoring:
14286 case MachineState_TeleportingIn:
14287 case MachineState_Paused:
14288 case MachineState_Running:
14289 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14290 * elsewhere... */
14291 alock.release();
14292 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14293 default: break;
14294 }
14295#else
14296 NOREF(aDevice);
14297 NOREF(aMaskedIfs);
14298#endif
14299 return false;
14300}
14301
14302/**
14303 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14304 */
14305HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14306 IVirtualBoxErrorInfo *aError,
14307 ULONG aMaskedIfs,
14308 const com::Utf8Str &aCaptureFilename)
14309{
14310 LogFlowThisFunc(("\n"));
14311
14312 AutoCaller autoCaller(this);
14313
14314 /* This notification may happen after the machine object has been
14315 * uninitialized (the session was closed), so don't assert. */
14316 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14317
14318 ComPtr<IInternalSessionControl> directControl;
14319 {
14320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14321 if (mData->mSession.mLockType == LockType_VM)
14322 directControl = mData->mSession.mDirectControl;
14323 }
14324
14325 /* fail on notifications sent after #OnSessionEnd() is called, it is
14326 * expected by the caller */
14327 if (!directControl)
14328 return E_FAIL;
14329
14330 /* No locks should be held at this point. */
14331 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14332 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14333
14334 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14335}
14336
14337/**
14338 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14339 */
14340HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14341 IVirtualBoxErrorInfo *aError)
14342{
14343 LogFlowThisFunc(("\n"));
14344
14345 AutoCaller autoCaller(this);
14346
14347 /* This notification may happen after the machine object has been
14348 * uninitialized (the session was closed), so don't assert. */
14349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14350
14351 ComPtr<IInternalSessionControl> directControl;
14352 {
14353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14354 if (mData->mSession.mLockType == LockType_VM)
14355 directControl = mData->mSession.mDirectControl;
14356 }
14357
14358 /* fail on notifications sent after #OnSessionEnd() is called, it is
14359 * expected by the caller */
14360 if (!directControl)
14361 return E_FAIL;
14362
14363 /* No locks should be held at this point. */
14364 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14365 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14366
14367 return directControl->OnUSBDeviceDetach(aId, aError);
14368}
14369
14370// protected methods
14371/////////////////////////////////////////////////////////////////////////////
14372
14373/**
14374 * Deletes the given file if it is no longer in use by either the current machine state
14375 * (if the machine is "saved") or any of the machine's snapshots.
14376 *
14377 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14378 * but is different for each SnapshotMachine. When calling this, the order of calling this
14379 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14380 * is therefore critical. I know, it's all rather messy.
14381 *
14382 * @param strStateFile
14383 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14384 * the test for whether the saved state file is in use.
14385 */
14386void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14387 Snapshot *pSnapshotToIgnore)
14388{
14389 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14390 if ( (strStateFile.isNotEmpty())
14391 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14392 )
14393 // ... and it must also not be shared with other snapshots
14394 if ( !mData->mFirstSnapshot
14395 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14396 // this checks the SnapshotMachine's state file paths
14397 )
14398 RTFileDelete(strStateFile.c_str());
14399}
14400
14401/**
14402 * Locks the attached media.
14403 *
14404 * All attached hard disks are locked for writing and DVD/floppy are locked for
14405 * reading. Parents of attached hard disks (if any) are locked for reading.
14406 *
14407 * This method also performs accessibility check of all media it locks: if some
14408 * media is inaccessible, the method will return a failure and a bunch of
14409 * extended error info objects per each inaccessible medium.
14410 *
14411 * Note that this method is atomic: if it returns a success, all media are
14412 * locked as described above; on failure no media is locked at all (all
14413 * succeeded individual locks will be undone).
14414 *
14415 * The caller is responsible for doing the necessary state sanity checks.
14416 *
14417 * The locks made by this method must be undone by calling #unlockMedia() when
14418 * no more needed.
14419 */
14420HRESULT SessionMachine::i_lockMedia()
14421{
14422 AutoCaller autoCaller(this);
14423 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14424
14425 AutoMultiWriteLock2 alock(this->lockHandle(),
14426 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14427
14428 /* bail out if trying to lock things with already set up locking */
14429 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14430
14431 MultiResult mrc(S_OK);
14432
14433 /* Collect locking information for all medium objects attached to the VM. */
14434 for (MediumAttachmentList::const_iterator
14435 it = mMediumAttachments->begin();
14436 it != mMediumAttachments->end();
14437 ++it)
14438 {
14439 MediumAttachment *pAtt = *it;
14440 DeviceType_T devType = pAtt->i_getType();
14441 Medium *pMedium = pAtt->i_getMedium();
14442
14443 MediumLockList *pMediumLockList(new MediumLockList());
14444 // There can be attachments without a medium (floppy/dvd), and thus
14445 // it's impossible to create a medium lock list. It still makes sense
14446 // to have the empty medium lock list in the map in case a medium is
14447 // attached later.
14448 if (pMedium != NULL)
14449 {
14450 MediumType_T mediumType = pMedium->i_getType();
14451 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14452 || mediumType == MediumType_Shareable;
14453 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14454
14455 alock.release();
14456 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14457 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14458 false /* fMediumLockWriteAll */,
14459 NULL,
14460 *pMediumLockList);
14461 alock.acquire();
14462 if (FAILED(mrc))
14463 {
14464 delete pMediumLockList;
14465 mData->mSession.mLockedMedia.Clear();
14466 break;
14467 }
14468 }
14469
14470 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14471 if (FAILED(rc))
14472 {
14473 mData->mSession.mLockedMedia.Clear();
14474 mrc = setError(rc,
14475 tr("Collecting locking information for all attached media failed"));
14476 break;
14477 }
14478 }
14479
14480 if (SUCCEEDED(mrc))
14481 {
14482 /* Now lock all media. If this fails, nothing is locked. */
14483 alock.release();
14484 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14485 alock.acquire();
14486 if (FAILED(rc))
14487 {
14488 mrc = setError(rc,
14489 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14490 }
14491 }
14492
14493 return mrc;
14494}
14495
14496/**
14497 * Undoes the locks made by by #lockMedia().
14498 */
14499HRESULT SessionMachine::i_unlockMedia()
14500{
14501 AutoCaller autoCaller(this);
14502 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14503
14504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14505
14506 /* we may be holding important error info on the current thread;
14507 * preserve it */
14508 ErrorInfoKeeper eik;
14509
14510 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14511 AssertComRC(rc);
14512 return rc;
14513}
14514
14515/**
14516 * Helper to change the machine state (reimplementation).
14517 *
14518 * @note Locks this object for writing.
14519 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14520 * it can cause crashes in random places due to unexpectedly committing
14521 * the current settings. The caller is responsible for that. The call
14522 * to saveStateSettings is fine, because this method does not commit.
14523 */
14524HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14525{
14526 LogFlowThisFuncEnter();
14527 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14528
14529 AutoCaller autoCaller(this);
14530 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14531
14532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14533
14534 MachineState_T oldMachineState = mData->mMachineState;
14535
14536 AssertMsgReturn(oldMachineState != aMachineState,
14537 ("oldMachineState=%s, aMachineState=%s\n",
14538 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14539 E_FAIL);
14540
14541 HRESULT rc = S_OK;
14542
14543 int stsFlags = 0;
14544 bool deleteSavedState = false;
14545
14546 /* detect some state transitions */
14547
14548 if ( ( oldMachineState == MachineState_Saved
14549 && aMachineState == MachineState_Restoring)
14550 || ( ( oldMachineState == MachineState_PoweredOff
14551 || oldMachineState == MachineState_Teleported
14552 || oldMachineState == MachineState_Aborted
14553 )
14554 && ( aMachineState == MachineState_TeleportingIn
14555 || aMachineState == MachineState_Starting
14556 )
14557 )
14558 )
14559 {
14560 /* The EMT thread is about to start */
14561
14562 /* Nothing to do here for now... */
14563
14564 /// @todo NEWMEDIA don't let mDVDDrive and other children
14565 /// change anything when in the Starting/Restoring state
14566 }
14567 else if ( ( oldMachineState == MachineState_Running
14568 || oldMachineState == MachineState_Paused
14569 || oldMachineState == MachineState_Teleporting
14570 || oldMachineState == MachineState_OnlineSnapshotting
14571 || oldMachineState == MachineState_LiveSnapshotting
14572 || oldMachineState == MachineState_Stuck
14573 || oldMachineState == MachineState_Starting
14574 || oldMachineState == MachineState_Stopping
14575 || oldMachineState == MachineState_Saving
14576 || oldMachineState == MachineState_Restoring
14577 || oldMachineState == MachineState_TeleportingPausedVM
14578 || oldMachineState == MachineState_TeleportingIn
14579 )
14580 && ( aMachineState == MachineState_PoweredOff
14581 || aMachineState == MachineState_Saved
14582 || aMachineState == MachineState_Teleported
14583 || aMachineState == MachineState_Aborted
14584 )
14585 )
14586 {
14587 /* The EMT thread has just stopped, unlock attached media. Note that as
14588 * opposed to locking that is done from Console, we do unlocking here
14589 * because the VM process may have aborted before having a chance to
14590 * properly unlock all media it locked. */
14591
14592 unlockMedia();
14593 }
14594
14595 if (oldMachineState == MachineState_Restoring)
14596 {
14597 if (aMachineState != MachineState_Saved)
14598 {
14599 /*
14600 * delete the saved state file once the machine has finished
14601 * restoring from it (note that Console sets the state from
14602 * Restoring to Saved if the VM couldn't restore successfully,
14603 * to give the user an ability to fix an error and retry --
14604 * we keep the saved state file in this case)
14605 */
14606 deleteSavedState = true;
14607 }
14608 }
14609 else if ( oldMachineState == MachineState_Saved
14610 && ( aMachineState == MachineState_PoweredOff
14611 || aMachineState == MachineState_Aborted
14612 || aMachineState == MachineState_Teleported
14613 )
14614 )
14615 {
14616 /*
14617 * delete the saved state after SessionMachine::ForgetSavedState() is called
14618 * or if the VM process (owning a direct VM session) crashed while the
14619 * VM was Saved
14620 */
14621
14622 /// @todo (dmik)
14623 // Not sure that deleting the saved state file just because of the
14624 // client death before it attempted to restore the VM is a good
14625 // thing. But when it crashes we need to go to the Aborted state
14626 // which cannot have the saved state file associated... The only
14627 // way to fix this is to make the Aborted condition not a VM state
14628 // but a bool flag: i.e., when a crash occurs, set it to true and
14629 // change the state to PoweredOff or Saved depending on the
14630 // saved state presence.
14631
14632 deleteSavedState = true;
14633 mData->mCurrentStateModified = TRUE;
14634 stsFlags |= SaveSTS_CurStateModified;
14635 }
14636
14637 if ( aMachineState == MachineState_Starting
14638 || aMachineState == MachineState_Restoring
14639 || aMachineState == MachineState_TeleportingIn
14640 )
14641 {
14642 /* set the current state modified flag to indicate that the current
14643 * state is no more identical to the state in the
14644 * current snapshot */
14645 if (!mData->mCurrentSnapshot.isNull())
14646 {
14647 mData->mCurrentStateModified = TRUE;
14648 stsFlags |= SaveSTS_CurStateModified;
14649 }
14650 }
14651
14652 if (deleteSavedState)
14653 {
14654 if (mRemoveSavedState)
14655 {
14656 Assert(!mSSData->strStateFilePath.isEmpty());
14657
14658 // it is safe to delete the saved state file if ...
14659 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14660 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14661 // ... none of the snapshots share the saved state file
14662 )
14663 RTFileDelete(mSSData->strStateFilePath.c_str());
14664 }
14665
14666 mSSData->strStateFilePath.setNull();
14667 stsFlags |= SaveSTS_StateFilePath;
14668 }
14669
14670 /* redirect to the underlying peer machine */
14671 mPeer->i_setMachineState(aMachineState);
14672
14673 if ( oldMachineState != MachineState_RestoringSnapshot
14674 && ( aMachineState == MachineState_PoweredOff
14675 || aMachineState == MachineState_Teleported
14676 || aMachineState == MachineState_Aborted
14677 || aMachineState == MachineState_Saved))
14678 {
14679 /* the machine has stopped execution
14680 * (or the saved state file was adopted) */
14681 stsFlags |= SaveSTS_StateTimeStamp;
14682 }
14683
14684 if ( ( oldMachineState == MachineState_PoweredOff
14685 || oldMachineState == MachineState_Aborted
14686 || oldMachineState == MachineState_Teleported
14687 )
14688 && aMachineState == MachineState_Saved)
14689 {
14690 /* the saved state file was adopted */
14691 Assert(!mSSData->strStateFilePath.isEmpty());
14692 stsFlags |= SaveSTS_StateFilePath;
14693 }
14694
14695#ifdef VBOX_WITH_GUEST_PROPS
14696 if ( aMachineState == MachineState_PoweredOff
14697 || aMachineState == MachineState_Aborted
14698 || aMachineState == MachineState_Teleported)
14699 {
14700 /* Make sure any transient guest properties get removed from the
14701 * property store on shutdown. */
14702 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14703
14704 /* remove it from the settings representation */
14705 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14706 for (settings::GuestPropertiesList::iterator
14707 it = llGuestProperties.begin();
14708 it != llGuestProperties.end();
14709 /*nothing*/)
14710 {
14711 const settings::GuestProperty &prop = *it;
14712 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14713 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14714 {
14715 it = llGuestProperties.erase(it);
14716 fNeedsSaving = true;
14717 }
14718 else
14719 {
14720 ++it;
14721 }
14722 }
14723
14724 /* Additionally remove it from the HWData representation. Required to
14725 * keep everything in sync, as this is what the API keeps using. */
14726 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14727 for (HWData::GuestPropertyMap::iterator
14728 it = llHWGuestProperties.begin();
14729 it != llHWGuestProperties.end();
14730 /*nothing*/)
14731 {
14732 uint32_t fFlags = it->second.mFlags;
14733 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14734 {
14735 /* iterator where we need to continue after the erase call
14736 * (C++03 is a fact still, and it doesn't return the iterator
14737 * which would allow continuing) */
14738 HWData::GuestPropertyMap::iterator it2 = it;
14739 ++it2;
14740 llHWGuestProperties.erase(it);
14741 it = it2;
14742 fNeedsSaving = true;
14743 }
14744 else
14745 {
14746 ++it;
14747 }
14748 }
14749
14750 if (fNeedsSaving)
14751 {
14752 mData->mCurrentStateModified = TRUE;
14753 stsFlags |= SaveSTS_CurStateModified;
14754 }
14755 }
14756#endif /* VBOX_WITH_GUEST_PROPS */
14757
14758 rc = i_saveStateSettings(stsFlags);
14759
14760 if ( ( oldMachineState != MachineState_PoweredOff
14761 && oldMachineState != MachineState_Aborted
14762 && oldMachineState != MachineState_Teleported
14763 )
14764 && ( aMachineState == MachineState_PoweredOff
14765 || aMachineState == MachineState_Aborted
14766 || aMachineState == MachineState_Teleported
14767 )
14768 )
14769 {
14770 /* we've been shut down for any reason */
14771 /* no special action so far */
14772 }
14773
14774 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14775 LogFlowThisFuncLeave();
14776 return rc;
14777}
14778
14779/**
14780 * Sends the current machine state value to the VM process.
14781 *
14782 * @note Locks this object for reading, then calls a client process.
14783 */
14784HRESULT SessionMachine::i_updateMachineStateOnClient()
14785{
14786 AutoCaller autoCaller(this);
14787 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14788
14789 ComPtr<IInternalSessionControl> directControl;
14790 {
14791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14792 AssertReturn(!!mData, E_FAIL);
14793 if (mData->mSession.mLockType == LockType_VM)
14794 directControl = mData->mSession.mDirectControl;
14795
14796 /* directControl may be already set to NULL here in #OnSessionEnd()
14797 * called too early by the direct session process while there is still
14798 * some operation (like deleting the snapshot) in progress. The client
14799 * process in this case is waiting inside Session::close() for the
14800 * "end session" process object to complete, while #uninit() called by
14801 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14802 * operation to complete. For now, we accept this inconsistent behavior
14803 * and simply do nothing here. */
14804
14805 if (mData->mSession.mState == SessionState_Unlocking)
14806 return S_OK;
14807 }
14808
14809 /* ignore notifications sent after #OnSessionEnd() is called */
14810 if (!directControl)
14811 return S_OK;
14812
14813 return directControl->UpdateMachineState(mData->mMachineState);
14814}
14815
14816
14817/*static*/
14818HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14819{
14820 va_list args;
14821 va_start(args, pcszMsg);
14822 HRESULT rc = setErrorInternal(aResultCode,
14823 getStaticClassIID(),
14824 getStaticComponentName(),
14825 Utf8Str(pcszMsg, args),
14826 false /* aWarning */,
14827 true /* aLogIt */);
14828 va_end(args);
14829 return rc;
14830}
14831
14832
14833HRESULT Machine::updateState(MachineState_T aState)
14834{
14835 NOREF(aState);
14836 ReturnComNotImplemented();
14837}
14838
14839HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14840{
14841 NOREF(aProgress);
14842 ReturnComNotImplemented();
14843}
14844
14845HRESULT Machine::endPowerUp(LONG aResult)
14846{
14847 NOREF(aResult);
14848 ReturnComNotImplemented();
14849}
14850
14851HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14852{
14853 NOREF(aProgress);
14854 ReturnComNotImplemented();
14855}
14856
14857HRESULT Machine::endPoweringDown(LONG aResult,
14858 const com::Utf8Str &aErrMsg)
14859{
14860 NOREF(aResult);
14861 NOREF(aErrMsg);
14862 ReturnComNotImplemented();
14863}
14864
14865HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14866 BOOL *aMatched,
14867 ULONG *aMaskedInterfaces)
14868{
14869 NOREF(aDevice);
14870 NOREF(aMatched);
14871 NOREF(aMaskedInterfaces);
14872 ReturnComNotImplemented();
14873
14874}
14875
14876HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14877{
14878 NOREF(aId); NOREF(aCaptureFilename);
14879 ReturnComNotImplemented();
14880}
14881
14882HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14883 BOOL aDone)
14884{
14885 NOREF(aId);
14886 NOREF(aDone);
14887 ReturnComNotImplemented();
14888}
14889
14890HRESULT Machine::autoCaptureUSBDevices()
14891{
14892 ReturnComNotImplemented();
14893}
14894
14895HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14896{
14897 NOREF(aDone);
14898 ReturnComNotImplemented();
14899}
14900
14901HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14902 ComPtr<IProgress> &aProgress)
14903{
14904 NOREF(aSession);
14905 NOREF(aProgress);
14906 ReturnComNotImplemented();
14907}
14908
14909HRESULT Machine::finishOnlineMergeMedium()
14910{
14911 ReturnComNotImplemented();
14912}
14913
14914HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14915 std::vector<com::Utf8Str> &aValues,
14916 std::vector<LONG64> &aTimestamps,
14917 std::vector<com::Utf8Str> &aFlags)
14918{
14919 NOREF(aNames);
14920 NOREF(aValues);
14921 NOREF(aTimestamps);
14922 NOREF(aFlags);
14923 ReturnComNotImplemented();
14924}
14925
14926HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14927 const com::Utf8Str &aValue,
14928 LONG64 aTimestamp,
14929 const com::Utf8Str &aFlags)
14930{
14931 NOREF(aName);
14932 NOREF(aValue);
14933 NOREF(aTimestamp);
14934 NOREF(aFlags);
14935 ReturnComNotImplemented();
14936}
14937
14938HRESULT Machine::lockMedia()
14939{
14940 ReturnComNotImplemented();
14941}
14942
14943HRESULT Machine::unlockMedia()
14944{
14945 ReturnComNotImplemented();
14946}
14947
14948HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14949 ComPtr<IMediumAttachment> &aNewAttachment)
14950{
14951 NOREF(aAttachment);
14952 NOREF(aNewAttachment);
14953 ReturnComNotImplemented();
14954}
14955
14956HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14957 ULONG aCpuUser,
14958 ULONG aCpuKernel,
14959 ULONG aCpuIdle,
14960 ULONG aMemTotal,
14961 ULONG aMemFree,
14962 ULONG aMemBalloon,
14963 ULONG aMemShared,
14964 ULONG aMemCache,
14965 ULONG aPagedTotal,
14966 ULONG aMemAllocTotal,
14967 ULONG aMemFreeTotal,
14968 ULONG aMemBalloonTotal,
14969 ULONG aMemSharedTotal,
14970 ULONG aVmNetRx,
14971 ULONG aVmNetTx)
14972{
14973 NOREF(aValidStats);
14974 NOREF(aCpuUser);
14975 NOREF(aCpuKernel);
14976 NOREF(aCpuIdle);
14977 NOREF(aMemTotal);
14978 NOREF(aMemFree);
14979 NOREF(aMemBalloon);
14980 NOREF(aMemShared);
14981 NOREF(aMemCache);
14982 NOREF(aPagedTotal);
14983 NOREF(aMemAllocTotal);
14984 NOREF(aMemFreeTotal);
14985 NOREF(aMemBalloonTotal);
14986 NOREF(aMemSharedTotal);
14987 NOREF(aVmNetRx);
14988 NOREF(aVmNetTx);
14989 ReturnComNotImplemented();
14990}
14991
14992HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14993 com::Utf8Str &aResult)
14994{
14995 NOREF(aAuthParams);
14996 NOREF(aResult);
14997 ReturnComNotImplemented();
14998}
14999
15000HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15001{
15002 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15003
15004 AutoCaller autoCaller(this);
15005 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15006
15007 HRESULT rc = S_OK;
15008
15009 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15010 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15011 rc = getUSBDeviceFilters(usbDeviceFilters);
15012 if (FAILED(rc)) return rc;
15013
15014 NOREF(aFlags);
15015 com::Utf8Str osTypeId;
15016 ComObjPtr<GuestOSType> osType = NULL;
15017
15018 /* Get the guest os type as a string from the VB. */
15019 rc = getOSTypeId(osTypeId);
15020 if (FAILED(rc)) return rc;
15021
15022 /* Get the os type obj that coresponds, can be used to get
15023 * the defaults for this guest OS. */
15024 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15025 if (FAILED(rc)) return rc;
15026
15027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15028
15029 /* Let the OS type select 64-bit ness. */
15030 mHWData->mLongMode = osType->i_is64Bit()
15031 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15032
15033 /* Apply network adapters defaults */
15034 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15035 mNetworkAdapters[slot]->i_applyDefaults(osType);
15036
15037 /* Apply serial port defaults */
15038 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15039 mSerialPorts[slot]->i_applyDefaults(osType);
15040
15041 /* Apply parallel port defaults - not OS dependent*/
15042 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15043 mParallelPorts[slot]->i_applyDefaults();
15044
15045
15046 /* Let the OS type enable the X2APIC */
15047 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15048
15049 /* This one covers IOAPICEnabled. */
15050 mBIOSSettings->i_applyDefaults(osType);
15051
15052 /* Initialize default capture settings. */
15053 mCaptureSettings->i_applyDefaults();
15054
15055 /* Initialize default BIOS settings here */
15056 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15057 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15058
15059 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15060 if (FAILED(rc)) return rc;
15061
15062 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15063 if (FAILED(rc)) return rc;
15064
15065 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15066 if (FAILED(rc)) return rc;
15067
15068 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15069 if (FAILED(rc)) return rc;
15070
15071 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15072 if (FAILED(rc)) return rc;
15073
15074 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15075 if (FAILED(rc)) return rc;
15076
15077 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15078 if (FAILED(rc)) return rc;
15079
15080 BOOL mRTCUseUTC;
15081 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15082 if (FAILED(rc)) return rc;
15083
15084 setRTCUseUTC(mRTCUseUTC);
15085 if (FAILED(rc)) return rc;
15086
15087 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15088 if (FAILED(rc)) return rc;
15089
15090 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15091 if (FAILED(rc)) return rc;
15092
15093 /* Audio stuff. */
15094 AudioCodecType_T audioCodec;
15095 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15096 if (FAILED(rc)) return rc;
15097
15098 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15099 if (FAILED(rc)) return rc;
15100
15101 AudioControllerType_T audioController;
15102 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15103 if (FAILED(rc)) return rc;
15104
15105 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15106 if (FAILED(rc)) return rc;
15107
15108 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15109 if (FAILED(rc)) return rc;
15110
15111 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15112 if (FAILED(rc)) return rc;
15113
15114 /* Storage Controllers */
15115 StorageControllerType_T hdStorageControllerType;
15116 StorageBus_T hdStorageBusType;
15117 StorageControllerType_T dvdStorageControllerType;
15118 StorageBus_T dvdStorageBusType;
15119 BOOL recommendedFloppy;
15120 ComPtr<IStorageController> floppyController;
15121 ComPtr<IStorageController> hdController;
15122 ComPtr<IStorageController> dvdController;
15123 Utf8Str strFloppyName, strDVDName, strHDName;
15124
15125 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15126 strFloppyName = Bstr("Floppy 1").raw();
15127 strDVDName = Bstr("DVD 1").raw();
15128 strHDName = Bstr("HDD 1").raw();
15129
15130 /* Floppy recommended? add one. */
15131 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15132 if (FAILED(rc)) return rc;
15133 if (recommendedFloppy)
15134 {
15135 rc = addStorageController(strFloppyName,
15136 StorageBus_Floppy,
15137 floppyController);
15138 if (FAILED(rc)) return rc;
15139 }
15140
15141 /* Setup one DVD storage controller. */
15142 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15143 if (FAILED(rc)) return rc;
15144
15145 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15146 if (FAILED(rc)) return rc;
15147
15148 rc = addStorageController(strDVDName,
15149 dvdStorageBusType,
15150 dvdController);
15151 if (FAILED(rc)) return rc;
15152
15153 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15154 if (FAILED(rc)) return rc;
15155
15156 /* Setup one HDD storage controller. */
15157 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15158 if (FAILED(rc)) return rc;
15159
15160 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15161 if (FAILED(rc)) return rc;
15162
15163 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15164 {
15165 rc = addStorageController(strHDName,
15166 hdStorageBusType,
15167 hdController);
15168 if (FAILED(rc)) return rc;
15169
15170 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15171 if (FAILED(rc)) return rc;
15172 }
15173 else
15174 {
15175 /* The HD controller is the same as DVD: */
15176 hdController = dvdController;
15177 strHDName = Bstr("DVD 1").raw();
15178 }
15179
15180 /* Limit the AHCI port count if it's used because windows has trouble with
15181 * too many ports and other guest (OS X in particular) may take extra long
15182 * boot: */
15183
15184 // pParent = static_cast<Medium*>(aP)
15185 IStorageController *temp = hdController;
15186 ComObjPtr<StorageController> storageController;
15187 storageController = static_cast<StorageController *>(temp);
15188
15189 // tempHDController = aHDController;
15190 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15191 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15192 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15193 storageController->COMSETTER(PortCount)(1);
15194
15195 /* USB stuff */
15196
15197 bool ohciEnabled = false;
15198
15199 ComPtr<IUSBController> usbController;
15200 BOOL recommendedUSB3;
15201 BOOL recommendedUSB;
15202 BOOL usbProxyAvailable;
15203
15204 getUSBProxyAvailable(&usbProxyAvailable);
15205 if (FAILED(rc)) return rc;
15206
15207 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15208 if (FAILED(rc)) return rc;
15209 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15210 if (FAILED(rc)) return rc;
15211
15212 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15213 {
15214#ifdef VBOX_WITH_EXTPACK
15215 /* USB 3.0 is only available if the proper ExtPack is installed. */
15216 ExtPackManager *aManager = mParent->i_getExtPackManager();
15217 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15218 {
15219 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15220 if (FAILED(rc)) return rc;
15221
15222 /* xHci includes OHCI */
15223 ohciEnabled = true;
15224 }
15225#endif
15226 }
15227 if ( !ohciEnabled
15228 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15229 {
15230 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15231 if (FAILED(rc)) return rc;
15232 ohciEnabled = true;
15233
15234#ifdef VBOX_WITH_EXTPACK
15235 /* USB 2.0 is only available if the proper ExtPack is installed.
15236 * Note. Configuring EHCI here and providing messages about
15237 * the missing extpack isn't exactly clean, but it is a
15238 * necessary evil to patch over legacy compatability issues
15239 * introduced by the new distribution model. */
15240 ExtPackManager *manager = mParent->i_getExtPackManager();
15241 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15242 {
15243 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15244 if (FAILED(rc)) return rc;
15245 }
15246#endif
15247 }
15248
15249 /* Set recommended human interface device types: */
15250 BOOL recommendedUSBHID;
15251 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15252 if (FAILED(rc)) return rc;
15253
15254 if (recommendedUSBHID)
15255 {
15256 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15257 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15258 if (!ohciEnabled && !usbDeviceFilters.isNull())
15259 {
15260 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15261 if (FAILED(rc)) return rc;
15262 }
15263 }
15264
15265 BOOL recommendedUSBTablet;
15266 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15267 if (FAILED(rc)) return rc;
15268
15269 if (recommendedUSBTablet)
15270 {
15271 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15272 if (!ohciEnabled && !usbDeviceFilters.isNull())
15273 {
15274 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15275 if (FAILED(rc)) return rc;
15276 }
15277 }
15278 return S_OK;
15279}
15280
15281/* This isn't handled entirely by the wrapper generator yet. */
15282#ifdef VBOX_WITH_XPCOM
15283NS_DECL_CLASSINFO(SessionMachine)
15284NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15285
15286NS_DECL_CLASSINFO(SnapshotMachine)
15287NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15288#endif
Note: See TracBrowser for help on using the repository browser.

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