VirtualBox

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

Last change on this file since 76549 was 76394, checked in by vboxsync, 6 years ago

DisplayImpl.h,HGCM.h: Avoid including VBox/VMMDev.h and instead do it the handful of source files where it's actually needed. bugref:9344

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 530.2 KB
Line 
1/* $Id: MachineImpl.cpp 76394 2018-12-23 02:14:05Z 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/VMMDev.h>
80#include <VBox/vmm/ssm.h>
81
82#ifdef VBOX_WITH_GUEST_PROPS
83# include <VBox/HostServices/GuestPropertySvc.h>
84# include <VBox/com/array.h>
85#endif
86
87#include "VBox/com/MultiResult.h"
88
89#include <algorithm>
90
91#ifdef VBOX_WITH_DTRACE_R3_MAIN
92# include "dtrace/VBoxAPI.h"
93#endif
94
95#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
96# define HOSTSUFF_EXE ".exe"
97#else /* !RT_OS_WINDOWS */
98# define HOSTSUFF_EXE ""
99#endif /* !RT_OS_WINDOWS */
100
101// defines / prototypes
102/////////////////////////////////////////////////////////////////////////////
103
104/////////////////////////////////////////////////////////////////////////////
105// Machine::Data structure
106/////////////////////////////////////////////////////////////////////////////
107
108Machine::Data::Data()
109{
110 mRegistered = FALSE;
111 pMachineConfigFile = NULL;
112 /* Contains hints on what has changed when the user is using the VM (config
113 * changes, running the VM, ...). This is used to decide if a config needs
114 * to be written to disk. */
115 flModifications = 0;
116 /* VM modification usually also trigger setting the current state to
117 * "Modified". Although this is not always the case. An e.g. is the VM
118 * initialization phase or when snapshot related data is changed. The
119 * actually behavior is controlled by the following flag. */
120 m_fAllowStateModification = false;
121 mAccessible = FALSE;
122 /* mUuid is initialized in Machine::init() */
123
124 mMachineState = MachineState_PoweredOff;
125 RTTimeNow(&mLastStateChange);
126
127 mMachineStateDeps = 0;
128 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
129 mMachineStateChangePending = 0;
130
131 mCurrentStateModified = TRUE;
132 mGuestPropertiesModified = FALSE;
133
134 mSession.mPID = NIL_RTPROCESS;
135 mSession.mLockType = LockType_Null;
136 mSession.mState = SessionState_Unlocked;
137}
138
139Machine::Data::~Data()
140{
141 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
142 {
143 RTSemEventMultiDestroy(mMachineStateDepsSem);
144 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
145 }
146 if (pMachineConfigFile)
147 {
148 delete pMachineConfigFile;
149 pMachineConfigFile = NULL;
150 }
151}
152
153/////////////////////////////////////////////////////////////////////////////
154// Machine::HWData structure
155/////////////////////////////////////////////////////////////////////////////
156
157Machine::HWData::HWData()
158{
159 /* default values for a newly created machine */
160 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
161 mMemorySize = 128;
162 mCPUCount = 1;
163 mCPUHotPlugEnabled = false;
164 mMemoryBalloonSize = 0;
165 mPageFusionEnabled = false;
166 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
167 mVRAMSize = 8;
168 mAccelerate3DEnabled = false;
169 mAccelerate2DVideoEnabled = false;
170 mMonitorCount = 1;
171 mHWVirtExEnabled = true;
172 mHWVirtExNestedPagingEnabled = true;
173#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
174 mHWVirtExLargePagesEnabled = true;
175#else
176 /* Not supported on 32 bits hosts. */
177 mHWVirtExLargePagesEnabled = false;
178#endif
179 mHWVirtExVPIDEnabled = true;
180 mHWVirtExUXEnabled = true;
181 mHWVirtExForceEnabled = false;
182 mHWVirtExUseNativeApi = false;
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
189 mTripleFaultReset = false;
190 mAPIC = true;
191 mX2APIC = false;
192 mIBPBOnVMExit = false;
193 mIBPBOnVMEntry = false;
194 mSpecCtrl = false;
195 mSpecCtrlByHost = false;
196 mNestedHWVirt = false;
197 mHPETEnabled = false;
198 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
199 mCpuIdPortabilityLevel = 0;
200 mCpuProfile = "host";
201
202 /* default boot order: floppy - DVD - HDD */
203 mBootOrder[0] = DeviceType_Floppy;
204 mBootOrder[1] = DeviceType_DVD;
205 mBootOrder[2] = DeviceType_HardDisk;
206 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
207 mBootOrder[i] = DeviceType_Null;
208
209 mClipboardMode = ClipboardMode_Disabled;
210 mDnDMode = DnDMode_Disabled;
211
212 mFirmwareType = FirmwareType_BIOS;
213 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
214 mPointingHIDType = PointingHIDType_PS2Mouse;
215 mChipsetType = ChipsetType_PIIX3;
216 mParavirtProvider = ParavirtProvider_Default;
217 mEmulatedUSBCardReaderEnabled = FALSE;
218
219 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
220 mCPUAttached[i] = false;
221
222 mIOCacheEnabled = true;
223 mIOCacheSize = 5; /* 5MB */
224}
225
226Machine::HWData::~HWData()
227{
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// Machine class
232/////////////////////////////////////////////////////////////////////////////
233
234// constructor / destructor
235/////////////////////////////////////////////////////////////////////////////
236
237Machine::Machine() :
238#ifdef VBOX_WITH_RESOURCE_USAGE_API
239 mCollectorGuest(NULL),
240#endif
241 mPeer(NULL),
242 mParent(NULL),
243 mSerialPorts(),
244 mParallelPorts(),
245 uRegistryNeedsSaving(0)
246{}
247
248Machine::~Machine()
249{}
250
251HRESULT Machine::FinalConstruct()
252{
253 LogFlowThisFunc(("\n"));
254 return BaseFinalConstruct();
255}
256
257void Machine::FinalRelease()
258{
259 LogFlowThisFunc(("\n"));
260 uninit();
261 BaseFinalRelease();
262}
263
264/**
265 * Initializes a new machine instance; this init() variant creates a new, empty machine.
266 * This gets called from VirtualBox::CreateMachine().
267 *
268 * @param aParent Associated parent object
269 * @param strConfigFile Local file system path to the VM settings file (can
270 * be relative to the VirtualBox config directory).
271 * @param strName name for the machine
272 * @param llGroups list of groups for the machine
273 * @param strOsType OS Type string (stored as is if aOsType is NULL).
274 * @param aOsType OS Type of this machine or NULL.
275 * @param aId UUID for the new machine.
276 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
277 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
278 * scheme (includes the UUID).
279 *
280 * @return Success indicator. if not S_OK, the machine object is invalid
281 */
282HRESULT Machine::init(VirtualBox *aParent,
283 const Utf8Str &strConfigFile,
284 const Utf8Str &strName,
285 const StringsList &llGroups,
286 const Utf8Str &strOsType,
287 GuestOSType *aOsType,
288 const Guid &aId,
289 bool fForceOverwrite,
290 bool fDirectoryIncludesUUID)
291{
292 LogFlowThisFuncEnter();
293 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
294
295 /* Enclose the state transition NotReady->InInit->Ready */
296 AutoInitSpan autoInitSpan(this);
297 AssertReturn(autoInitSpan.isOk(), E_FAIL);
298
299 HRESULT rc = initImpl(aParent, strConfigFile);
300 if (FAILED(rc)) return rc;
301
302 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
303 if (FAILED(rc)) return rc;
304
305 if (SUCCEEDED(rc))
306 {
307 // create an empty machine config
308 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
309
310 rc = initDataAndChildObjects();
311 }
312
313 if (SUCCEEDED(rc))
314 {
315 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
316 mData->mAccessible = TRUE;
317
318 unconst(mData->mUuid) = aId;
319
320 mUserData->s.strName = strName;
321
322 if (llGroups.size())
323 mUserData->s.llGroups = llGroups;
324
325 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
326 // the "name sync" flag determines whether the machine directory gets renamed along
327 // with the machine file; say so if the settings file name is the same as the
328 // settings file parent directory (machine directory)
329 mUserData->s.fNameSync = i_isInOwnDir();
330
331 // initialize the default snapshots folder
332 rc = COMSETTER(SnapshotFolder)(NULL);
333 AssertComRC(rc);
334
335 if (aOsType)
336 {
337 /* Store OS type */
338 mUserData->s.strOsType = aOsType->i_id();
339
340 /* Let the OS type select 64-bit ness. */
341 mHWData->mLongMode = aOsType->i_is64Bit()
342 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
343
344 /* Let the OS type enable the X2APIC */
345 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
346 }
347 else if (!strOsType.isEmpty())
348 {
349 /* Store OS type */
350 mUserData->s.strOsType = strOsType;
351
352 /* No guest OS type object. Pick some plausible defaults which the
353 * host can handle. There's no way to know or validate anything. */
354 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
355 mHWData->mX2APIC = false;
356 }
357
358 /* Apply BIOS defaults. */
359 mBIOSSettings->i_applyDefaults(aOsType);
360
361 /* Apply record defaults. */
362 mRecordingSettings->i_applyDefaults();
363
364 /* Apply network adapters defaults */
365 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
366 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
367
368 /* Apply serial port defaults */
369 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
370 mSerialPorts[slot]->i_applyDefaults(aOsType);
371
372 /* Apply parallel port defaults */
373 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
374 mParallelPorts[slot]->i_applyDefaults();
375
376 /* At this point the changing of the current state modification
377 * flag is allowed. */
378 i_allowStateModification();
379
380 /* commit all changes made during the initialization */
381 i_commit();
382 }
383
384 /* Confirm a successful initialization when it's the case */
385 if (SUCCEEDED(rc))
386 {
387 if (mData->mAccessible)
388 autoInitSpan.setSucceeded();
389 else
390 autoInitSpan.setLimited();
391 }
392
393 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
394 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
395 mData->mRegistered,
396 mData->mAccessible,
397 rc));
398
399 LogFlowThisFuncLeave();
400
401 return rc;
402}
403
404/**
405 * Initializes a new instance with data from machine XML (formerly Init_Registered).
406 * Gets called in two modes:
407 *
408 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
409 * UUID is specified and we mark the machine as "registered";
410 *
411 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
412 * and the machine remains unregistered until RegisterMachine() is called.
413 *
414 * @param aParent Associated parent object
415 * @param strConfigFile Local file system path to the VM settings file (can
416 * be relative to the VirtualBox config directory).
417 * @param aId UUID of the machine or NULL (see above).
418 *
419 * @return Success indicator. if not S_OK, the machine object is invalid
420 */
421HRESULT Machine::initFromSettings(VirtualBox *aParent,
422 const Utf8Str &strConfigFile,
423 const Guid *aId)
424{
425 LogFlowThisFuncEnter();
426 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
427
428 /* Enclose the state transition NotReady->InInit->Ready */
429 AutoInitSpan autoInitSpan(this);
430 AssertReturn(autoInitSpan.isOk(), E_FAIL);
431
432 HRESULT rc = initImpl(aParent, strConfigFile);
433 if (FAILED(rc)) return rc;
434
435 if (aId)
436 {
437 // loading a registered VM:
438 unconst(mData->mUuid) = *aId;
439 mData->mRegistered = TRUE;
440 // now load the settings from XML:
441 rc = i_registeredInit();
442 // this calls initDataAndChildObjects() and loadSettings()
443 }
444 else
445 {
446 // opening an unregistered VM (VirtualBox::OpenMachine()):
447 rc = initDataAndChildObjects();
448
449 if (SUCCEEDED(rc))
450 {
451 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
452 mData->mAccessible = TRUE;
453
454 try
455 {
456 // load and parse machine XML; this will throw on XML or logic errors
457 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
458
459 // reject VM UUID duplicates, they can happen if someone
460 // tries to register an already known VM config again
461 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
462 true /* fPermitInaccessible */,
463 false /* aDoSetError */,
464 NULL) != VBOX_E_OBJECT_NOT_FOUND)
465 {
466 throw setError(E_FAIL,
467 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
468 mData->m_strConfigFile.c_str());
469 }
470
471 // use UUID from machine config
472 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
473
474 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
475 NULL /* puuidRegistry */);
476 if (FAILED(rc)) throw rc;
477
478 /* At this point the changing of the current state modification
479 * flag is allowed. */
480 i_allowStateModification();
481
482 i_commit();
483 }
484 catch (HRESULT err)
485 {
486 /* we assume that error info is set by the thrower */
487 rc = err;
488 }
489 catch (...)
490 {
491 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
492 }
493 }
494 }
495
496 /* Confirm a successful initialization when it's the case */
497 if (SUCCEEDED(rc))
498 {
499 if (mData->mAccessible)
500 autoInitSpan.setSucceeded();
501 else
502 {
503 autoInitSpan.setLimited();
504
505 // uninit media from this machine's media registry, or else
506 // reloading the settings will fail
507 mParent->i_unregisterMachineMedia(i_getId());
508 }
509 }
510
511 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
512 "rc=%08X\n",
513 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
514 mData->mRegistered, mData->mAccessible, rc));
515
516 LogFlowThisFuncLeave();
517
518 return rc;
519}
520
521/**
522 * Initializes a new instance from a machine config that is already in memory
523 * (import OVF case). Since we are importing, the UUID in the machine
524 * config is ignored and we always generate a fresh one.
525 *
526 * @param aParent Associated parent object.
527 * @param strName Name for the new machine; this overrides what is specified in config.
528 * @param strSettingsFilename File name of .vbox file.
529 * @param config Machine configuration loaded and parsed from XML.
530 *
531 * @return Success indicator. if not S_OK, the machine object is invalid
532 */
533HRESULT Machine::init(VirtualBox *aParent,
534 const Utf8Str &strName,
535 const Utf8Str &strSettingsFilename,
536 const settings::MachineConfigFile &config)
537{
538 LogFlowThisFuncEnter();
539
540 /* Enclose the state transition NotReady->InInit->Ready */
541 AutoInitSpan autoInitSpan(this);
542 AssertReturn(autoInitSpan.isOk(), E_FAIL);
543
544 HRESULT rc = initImpl(aParent, strSettingsFilename);
545 if (FAILED(rc)) return rc;
546
547 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
548 if (FAILED(rc)) return rc;
549
550 rc = initDataAndChildObjects();
551
552 if (SUCCEEDED(rc))
553 {
554 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
555 mData->mAccessible = TRUE;
556
557 // create empty machine config for instance data
558 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
559
560 // generate fresh UUID, ignore machine config
561 unconst(mData->mUuid).create();
562
563 rc = i_loadMachineDataFromSettings(config,
564 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
565
566 // override VM name as well, it may be different
567 mUserData->s.strName = strName;
568
569 if (SUCCEEDED(rc))
570 {
571 /* At this point the changing of the current state modification
572 * flag is allowed. */
573 i_allowStateModification();
574
575 /* commit all changes made during the initialization */
576 i_commit();
577 }
578 }
579
580 /* Confirm a successful initialization when it's the case */
581 if (SUCCEEDED(rc))
582 {
583 if (mData->mAccessible)
584 autoInitSpan.setSucceeded();
585 else
586 {
587 /* Ignore all errors from unregistering, they would destroy
588- * the more interesting error information we already have,
589- * pinpointing the issue with the VM config. */
590 ErrorInfoKeeper eik;
591
592 autoInitSpan.setLimited();
593
594 // uninit media from this machine's media registry, or else
595 // reloading the settings will fail
596 mParent->i_unregisterMachineMedia(i_getId());
597 }
598 }
599
600 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
601 "rc=%08X\n",
602 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
603 mData->mRegistered, mData->mAccessible, rc));
604
605 LogFlowThisFuncLeave();
606
607 return rc;
608}
609
610/**
611 * Shared code between the various init() implementations.
612 * @param aParent The VirtualBox object.
613 * @param strConfigFile Settings file.
614 * @return
615 */
616HRESULT Machine::initImpl(VirtualBox *aParent,
617 const Utf8Str &strConfigFile)
618{
619 LogFlowThisFuncEnter();
620
621 AssertReturn(aParent, E_INVALIDARG);
622 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
623
624 HRESULT rc = S_OK;
625
626 /* share the parent weakly */
627 unconst(mParent) = aParent;
628
629 /* allocate the essential machine data structure (the rest will be
630 * allocated later by initDataAndChildObjects() */
631 mData.allocate();
632
633 /* memorize the config file name (as provided) */
634 mData->m_strConfigFile = strConfigFile;
635
636 /* get the full file name */
637 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
638 if (RT_FAILURE(vrc1))
639 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
640 tr("Invalid machine settings file name '%s' (%Rrc)"),
641 strConfigFile.c_str(),
642 vrc1);
643
644 LogFlowThisFuncLeave();
645
646 return rc;
647}
648
649/**
650 * Tries to create a machine settings file in the path stored in the machine
651 * instance data. Used when a new machine is created to fail gracefully if
652 * the settings file could not be written (e.g. because machine dir is read-only).
653 * @return
654 */
655HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
656{
657 HRESULT rc = S_OK;
658
659 // when we create a new machine, we must be able to create the settings file
660 RTFILE f = NIL_RTFILE;
661 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
662 if ( RT_SUCCESS(vrc)
663 || vrc == VERR_SHARING_VIOLATION
664 )
665 {
666 if (RT_SUCCESS(vrc))
667 RTFileClose(f);
668 if (!fForceOverwrite)
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Machine settings file '%s' already exists"),
671 mData->m_strConfigFileFull.c_str());
672 else
673 {
674 /* try to delete the config file, as otherwise the creation
675 * of a new settings file will fail. */
676 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
677 if (RT_FAILURE(vrc2))
678 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
679 tr("Could not delete the existing settings file '%s' (%Rrc)"),
680 mData->m_strConfigFileFull.c_str(), vrc2);
681 }
682 }
683 else if ( vrc != VERR_FILE_NOT_FOUND
684 && vrc != VERR_PATH_NOT_FOUND
685 )
686 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
687 tr("Invalid machine settings file name '%s' (%Rrc)"),
688 mData->m_strConfigFileFull.c_str(),
689 vrc);
690 return rc;
691}
692
693/**
694 * Initializes the registered machine by loading the settings file.
695 * This method is separated from #init() in order to make it possible to
696 * retry the operation after VirtualBox startup instead of refusing to
697 * startup the whole VirtualBox server in case if the settings file of some
698 * registered VM is invalid or inaccessible.
699 *
700 * @note Must be always called from this object's write lock
701 * (unless called from #init() that doesn't need any locking).
702 * @note Locks the mUSBController method for writing.
703 * @note Subclasses must not call this method.
704 */
705HRESULT Machine::i_registeredInit()
706{
707 AssertReturn(!i_isSessionMachine(), E_FAIL);
708 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
709 AssertReturn(mData->mUuid.isValid(), E_FAIL);
710 AssertReturn(!mData->mAccessible, E_FAIL);
711
712 HRESULT rc = initDataAndChildObjects();
713
714 if (SUCCEEDED(rc))
715 {
716 /* Temporarily reset the registered flag in order to let setters
717 * potentially called from loadSettings() succeed (isMutable() used in
718 * all setters will return FALSE for a Machine instance if mRegistered
719 * is TRUE). */
720 mData->mRegistered = FALSE;
721
722 try
723 {
724 // load and parse machine XML; this will throw on XML or logic errors
725 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
726
727 if (mData->mUuid != mData->pMachineConfigFile->uuid)
728 throw setError(E_FAIL,
729 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
730 mData->pMachineConfigFile->uuid.raw(),
731 mData->m_strConfigFileFull.c_str(),
732 mData->mUuid.toString().c_str(),
733 mParent->i_settingsFilePath().c_str());
734
735 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
736 NULL /* const Guid *puuidRegistry */);
737 if (FAILED(rc)) throw rc;
738 }
739 catch (HRESULT err)
740 {
741 /* we assume that error info is set by the thrower */
742 rc = err;
743 }
744 catch (...)
745 {
746 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
747 }
748
749 /* Restore the registered flag (even on failure) */
750 mData->mRegistered = TRUE;
751 }
752
753 if (SUCCEEDED(rc))
754 {
755 /* Set mAccessible to TRUE only if we successfully locked and loaded
756 * the settings file */
757 mData->mAccessible = TRUE;
758
759 /* commit all changes made during loading the settings file */
760 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
761 /// @todo r=klaus for some reason the settings loading logic backs up
762 // the settings, and therefore a commit is needed. Should probably be changed.
763 }
764 else
765 {
766 /* If the machine is registered, then, instead of returning a
767 * failure, we mark it as inaccessible and set the result to
768 * success to give it a try later */
769
770 /* fetch the current error info */
771 mData->mAccessError = com::ErrorInfo();
772 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
773
774 /* rollback all changes */
775 i_rollback(false /* aNotify */);
776
777 // uninit media from this machine's media registry, or else
778 // reloading the settings will fail
779 mParent->i_unregisterMachineMedia(i_getId());
780
781 /* uninitialize the common part to make sure all data is reset to
782 * default (null) values */
783 uninitDataAndChildObjects();
784
785 rc = S_OK;
786 }
787
788 return rc;
789}
790
791/**
792 * Uninitializes the instance.
793 * Called either from FinalRelease() or by the parent when it gets destroyed.
794 *
795 * @note The caller of this method must make sure that this object
796 * a) doesn't have active callers on the current thread and b) is not locked
797 * by the current thread; otherwise uninit() will hang either a) due to
798 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
799 * a dead-lock caused by this thread waiting for all callers on the other
800 * threads are done but preventing them from doing so by holding a lock.
801 */
802void Machine::uninit()
803{
804 LogFlowThisFuncEnter();
805
806 Assert(!isWriteLockOnCurrentThread());
807
808 Assert(!uRegistryNeedsSaving);
809 if (uRegistryNeedsSaving)
810 {
811 AutoCaller autoCaller(this);
812 if (SUCCEEDED(autoCaller.rc()))
813 {
814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
815 i_saveSettings(NULL, Machine::SaveS_Force);
816 }
817 }
818
819 /* Enclose the state transition Ready->InUninit->NotReady */
820 AutoUninitSpan autoUninitSpan(this);
821 if (autoUninitSpan.uninitDone())
822 return;
823
824 Assert(!i_isSnapshotMachine());
825 Assert(!i_isSessionMachine());
826 Assert(!!mData);
827
828 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
829 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
830
831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
832
833 if (!mData->mSession.mMachine.isNull())
834 {
835 /* Theoretically, this can only happen if the VirtualBox server has been
836 * terminated while there were clients running that owned open direct
837 * sessions. Since in this case we are definitely called by
838 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
839 * won't happen on the client watcher thread (because it has a
840 * VirtualBox caller for the duration of the
841 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
842 * cannot happen until the VirtualBox caller is released). This is
843 * important, because SessionMachine::uninit() cannot correctly operate
844 * after we return from this method (it expects the Machine instance is
845 * still valid). We'll call it ourselves below.
846 */
847 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
848 (SessionMachine*)mData->mSession.mMachine));
849
850 if (Global::IsOnlineOrTransient(mData->mMachineState))
851 {
852 Log1WarningThisFunc(("Setting state to Aborted!\n"));
853 /* set machine state using SessionMachine reimplementation */
854 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
855 }
856
857 /*
858 * Uninitialize SessionMachine using public uninit() to indicate
859 * an unexpected uninitialization.
860 */
861 mData->mSession.mMachine->uninit();
862 /* SessionMachine::uninit() must set mSession.mMachine to null */
863 Assert(mData->mSession.mMachine.isNull());
864 }
865
866 // uninit media from this machine's media registry, if they're still there
867 Guid uuidMachine(i_getId());
868
869 /* the lock is no more necessary (SessionMachine is uninitialized) */
870 alock.release();
871
872 /* XXX This will fail with
873 * "cannot be closed because it is still attached to 1 virtual machines"
874 * because at this point we did not call uninitDataAndChildObjects() yet
875 * and therefore also removeBackReference() for all these mediums was not called! */
876
877 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
878 mParent->i_unregisterMachineMedia(uuidMachine);
879
880 // has machine been modified?
881 if (mData->flModifications)
882 {
883 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
884 i_rollback(false /* aNotify */);
885 }
886
887 if (mData->mAccessible)
888 uninitDataAndChildObjects();
889
890 /* free the essential data structure last */
891 mData.free();
892
893 LogFlowThisFuncLeave();
894}
895
896// Wrapped IMachine properties
897/////////////////////////////////////////////////////////////////////////////
898HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
899{
900 /* mParent is constant during life time, no need to lock */
901 ComObjPtr<VirtualBox> pVirtualBox(mParent);
902 aParent = pVirtualBox;
903
904 return S_OK;
905}
906
907
908HRESULT Machine::getAccessible(BOOL *aAccessible)
909{
910 /* In some cases (medium registry related), it is necessary to be able to
911 * go through the list of all machines. Happens when an inaccessible VM
912 * has a sensible medium registry. */
913 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
915
916 HRESULT rc = S_OK;
917
918 if (!mData->mAccessible)
919 {
920 /* try to initialize the VM once more if not accessible */
921
922 AutoReinitSpan autoReinitSpan(this);
923 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
924
925#ifdef DEBUG
926 LogFlowThisFunc(("Dumping media backreferences\n"));
927 mParent->i_dumpAllBackRefs();
928#endif
929
930 if (mData->pMachineConfigFile)
931 {
932 // reset the XML file to force loadSettings() (called from i_registeredInit())
933 // to parse it again; the file might have changed
934 delete mData->pMachineConfigFile;
935 mData->pMachineConfigFile = NULL;
936 }
937
938 rc = i_registeredInit();
939
940 if (SUCCEEDED(rc) && mData->mAccessible)
941 {
942 autoReinitSpan.setSucceeded();
943
944 /* make sure interesting parties will notice the accessibility
945 * state change */
946 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
947 mParent->i_onMachineDataChange(mData->mUuid);
948 }
949 }
950
951 if (SUCCEEDED(rc))
952 *aAccessible = mData->mAccessible;
953
954 LogFlowThisFuncLeave();
955
956 return rc;
957}
958
959HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
960{
961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
962
963 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
964 {
965 /* return shortly */
966 aAccessError = NULL;
967 return S_OK;
968 }
969
970 HRESULT rc = S_OK;
971
972 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
973 rc = errorInfo.createObject();
974 if (SUCCEEDED(rc))
975 {
976 errorInfo->init(mData->mAccessError.getResultCode(),
977 mData->mAccessError.getInterfaceID().ref(),
978 Utf8Str(mData->mAccessError.getComponent()).c_str(),
979 Utf8Str(mData->mAccessError.getText()));
980 aAccessError = errorInfo;
981 }
982
983 return rc;
984}
985
986HRESULT Machine::getName(com::Utf8Str &aName)
987{
988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
989
990 aName = mUserData->s.strName;
991
992 return S_OK;
993}
994
995HRESULT Machine::setName(const com::Utf8Str &aName)
996{
997 // prohibit setting a UUID only as the machine name, or else it can
998 // never be found by findMachine()
999 Guid test(aName);
1000
1001 if (test.isValid())
1002 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1003
1004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1005
1006 HRESULT rc = i_checkStateDependency(MutableStateDep);
1007 if (FAILED(rc)) return rc;
1008
1009 i_setModified(IsModified_MachineData);
1010 mUserData.backup();
1011 mUserData->s.strName = aName;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1017{
1018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 aDescription = mUserData->s.strDescription;
1021
1022 return S_OK;
1023}
1024
1025HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1026{
1027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1028
1029 // this can be done in principle in any state as it doesn't affect the VM
1030 // significantly, but play safe by not messing around while complex
1031 // activities are going on
1032 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1033 if (FAILED(rc)) return rc;
1034
1035 i_setModified(IsModified_MachineData);
1036 mUserData.backup();
1037 mUserData->s.strDescription = aDescription;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getId(com::Guid &aId)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045
1046 aId = mData->mUuid;
1047
1048 return S_OK;
1049}
1050
1051HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1052{
1053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1054 aGroups.resize(mUserData->s.llGroups.size());
1055 size_t i = 0;
1056 for (StringsList::const_iterator
1057 it = mUserData->s.llGroups.begin();
1058 it != mUserData->s.llGroups.end();
1059 ++it, ++i)
1060 aGroups[i] = (*it);
1061
1062 return S_OK;
1063}
1064
1065HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1066{
1067 StringsList llGroups;
1068 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1069 if (FAILED(rc))
1070 return rc;
1071
1072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1073
1074 rc = i_checkStateDependency(MutableOrSavedStateDep);
1075 if (FAILED(rc)) return rc;
1076
1077 i_setModified(IsModified_MachineData);
1078 mUserData.backup();
1079 mUserData->s.llGroups = llGroups;
1080
1081 return S_OK;
1082}
1083
1084HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1085{
1086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1087
1088 aOSTypeId = mUserData->s.strOsType;
1089
1090 return S_OK;
1091}
1092
1093HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1094{
1095 /* look up the object by Id to check it is valid */
1096 ComObjPtr<GuestOSType> pGuestOSType;
1097 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1098
1099 /* when setting, always use the "etalon" value for consistency -- lookup
1100 * by ID is case-insensitive and the input value may have different case */
1101 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1102
1103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1104
1105 HRESULT rc = i_checkStateDependency(MutableStateDep);
1106 if (FAILED(rc)) return rc;
1107
1108 i_setModified(IsModified_MachineData);
1109 mUserData.backup();
1110 mUserData->s.strOsType = osTypeId;
1111
1112 return S_OK;
1113}
1114
1115HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1116{
1117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 *aFirmwareType = mHWData->mFirmwareType;
1120
1121 return S_OK;
1122}
1123
1124HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1125{
1126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 HRESULT rc = i_checkStateDependency(MutableStateDep);
1129 if (FAILED(rc)) return rc;
1130
1131 i_setModified(IsModified_MachineData);
1132 mHWData.backup();
1133 mHWData->mFirmwareType = aFirmwareType;
1134
1135 return S_OK;
1136}
1137
1138HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1139{
1140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1141
1142 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1143
1144 return S_OK;
1145}
1146
1147HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1148{
1149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1150
1151 HRESULT rc = i_checkStateDependency(MutableStateDep);
1152 if (FAILED(rc)) return rc;
1153
1154 i_setModified(IsModified_MachineData);
1155 mHWData.backup();
1156 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1162{
1163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 *aPointingHIDType = mHWData->mPointingHIDType;
1166
1167 return S_OK;
1168}
1169
1170HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1171{
1172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1173
1174 HRESULT rc = i_checkStateDependency(MutableStateDep);
1175 if (FAILED(rc)) return rc;
1176
1177 i_setModified(IsModified_MachineData);
1178 mHWData.backup();
1179 mHWData->mPointingHIDType = aPointingHIDType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1185{
1186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 *aChipsetType = mHWData->mChipsetType;
1189
1190 return S_OK;
1191}
1192
1193HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1194{
1195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1196
1197 HRESULT rc = i_checkStateDependency(MutableStateDep);
1198 if (FAILED(rc)) return rc;
1199
1200 if (aChipsetType != mHWData->mChipsetType)
1201 {
1202 i_setModified(IsModified_MachineData);
1203 mHWData.backup();
1204 mHWData->mChipsetType = aChipsetType;
1205
1206 // Resize network adapter array, to be finalized on commit/rollback.
1207 // We must not throw away entries yet, otherwise settings are lost
1208 // without a way to roll back.
1209 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1210 size_t oldCount = mNetworkAdapters.size();
1211 if (newCount > oldCount)
1212 {
1213 mNetworkAdapters.resize(newCount);
1214 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1215 {
1216 unconst(mNetworkAdapters[slot]).createObject();
1217 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1218 }
1219 }
1220 }
1221
1222 return S_OK;
1223}
1224
1225HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1226{
1227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1228
1229 aParavirtDebug = mHWData->mParavirtDebug;
1230 return S_OK;
1231}
1232
1233HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1234{
1235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1236
1237 HRESULT rc = i_checkStateDependency(MutableStateDep);
1238 if (FAILED(rc)) return rc;
1239
1240 /** @todo Parse/validate options? */
1241 if (aParavirtDebug != mHWData->mParavirtDebug)
1242 {
1243 i_setModified(IsModified_MachineData);
1244 mHWData.backup();
1245 mHWData->mParavirtDebug = aParavirtDebug;
1246 }
1247
1248 return S_OK;
1249}
1250
1251HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1252{
1253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1254
1255 *aParavirtProvider = mHWData->mParavirtProvider;
1256
1257 return S_OK;
1258}
1259
1260HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1261{
1262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1263
1264 HRESULT rc = i_checkStateDependency(MutableStateDep);
1265 if (FAILED(rc)) return rc;
1266
1267 if (aParavirtProvider != mHWData->mParavirtProvider)
1268 {
1269 i_setModified(IsModified_MachineData);
1270 mHWData.backup();
1271 mHWData->mParavirtProvider = aParavirtProvider;
1272 }
1273
1274 return S_OK;
1275}
1276
1277HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1278{
1279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1280
1281 *aParavirtProvider = mHWData->mParavirtProvider;
1282 switch (mHWData->mParavirtProvider)
1283 {
1284 case ParavirtProvider_None:
1285 case ParavirtProvider_HyperV:
1286 case ParavirtProvider_KVM:
1287 case ParavirtProvider_Minimal:
1288 break;
1289
1290 /* Resolve dynamic provider types to the effective types. */
1291 default:
1292 {
1293 ComObjPtr<GuestOSType> pGuestOSType;
1294 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1295 pGuestOSType);
1296 if (FAILED(hrc2) || pGuestOSType.isNull())
1297 {
1298 *aParavirtProvider = ParavirtProvider_None;
1299 break;
1300 }
1301
1302 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1303 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1304
1305 switch (mHWData->mParavirtProvider)
1306 {
1307 case ParavirtProvider_Legacy:
1308 {
1309 if (fOsXGuest)
1310 *aParavirtProvider = ParavirtProvider_Minimal;
1311 else
1312 *aParavirtProvider = ParavirtProvider_None;
1313 break;
1314 }
1315
1316 case ParavirtProvider_Default:
1317 {
1318 if (fOsXGuest)
1319 *aParavirtProvider = ParavirtProvider_Minimal;
1320 else if ( mUserData->s.strOsType == "Windows10"
1321 || mUserData->s.strOsType == "Windows10_64"
1322 || mUserData->s.strOsType == "Windows81"
1323 || mUserData->s.strOsType == "Windows81_64"
1324 || mUserData->s.strOsType == "Windows8"
1325 || mUserData->s.strOsType == "Windows8_64"
1326 || mUserData->s.strOsType == "Windows7"
1327 || mUserData->s.strOsType == "Windows7_64"
1328 || mUserData->s.strOsType == "WindowsVista"
1329 || mUserData->s.strOsType == "WindowsVista_64"
1330 || mUserData->s.strOsType == "Windows2012"
1331 || mUserData->s.strOsType == "Windows2012_64"
1332 || mUserData->s.strOsType == "Windows2008"
1333 || mUserData->s.strOsType == "Windows2008_64")
1334 {
1335 *aParavirtProvider = ParavirtProvider_HyperV;
1336 }
1337 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1338 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1339 || mUserData->s.strOsType == "Linux"
1340 || mUserData->s.strOsType == "Linux_64"
1341 || mUserData->s.strOsType == "ArchLinux"
1342 || mUserData->s.strOsType == "ArchLinux_64"
1343 || mUserData->s.strOsType == "Debian"
1344 || mUserData->s.strOsType == "Debian_64"
1345 || mUserData->s.strOsType == "Fedora"
1346 || mUserData->s.strOsType == "Fedora_64"
1347 || mUserData->s.strOsType == "Gentoo"
1348 || mUserData->s.strOsType == "Gentoo_64"
1349 || mUserData->s.strOsType == "Mandriva"
1350 || mUserData->s.strOsType == "Mandriva_64"
1351 || mUserData->s.strOsType == "OpenSUSE"
1352 || mUserData->s.strOsType == "OpenSUSE_64"
1353 || mUserData->s.strOsType == "Oracle"
1354 || mUserData->s.strOsType == "Oracle_64"
1355 || mUserData->s.strOsType == "RedHat"
1356 || mUserData->s.strOsType == "RedHat_64"
1357 || mUserData->s.strOsType == "Turbolinux"
1358 || mUserData->s.strOsType == "Turbolinux_64"
1359 || mUserData->s.strOsType == "Ubuntu"
1360 || mUserData->s.strOsType == "Ubuntu_64"
1361 || mUserData->s.strOsType == "Xandros"
1362 || mUserData->s.strOsType == "Xandros_64")
1363 {
1364 *aParavirtProvider = ParavirtProvider_KVM;
1365 }
1366 else
1367 *aParavirtProvider = ParavirtProvider_None;
1368 break;
1369 }
1370
1371 default: AssertFailedBreak(); /* Shut up MSC. */
1372 }
1373 break;
1374 }
1375 }
1376
1377 Assert( *aParavirtProvider == ParavirtProvider_None
1378 || *aParavirtProvider == ParavirtProvider_Minimal
1379 || *aParavirtProvider == ParavirtProvider_HyperV
1380 || *aParavirtProvider == ParavirtProvider_KVM);
1381 return S_OK;
1382}
1383
1384HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1385{
1386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1387
1388 aHardwareVersion = mHWData->mHWVersion;
1389
1390 return S_OK;
1391}
1392
1393HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1394{
1395 /* check known version */
1396 Utf8Str hwVersion = aHardwareVersion;
1397 if ( hwVersion.compare("1") != 0
1398 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1399 return setError(E_INVALIDARG,
1400 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1401
1402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1403
1404 HRESULT rc = i_checkStateDependency(MutableStateDep);
1405 if (FAILED(rc)) return rc;
1406
1407 i_setModified(IsModified_MachineData);
1408 mHWData.backup();
1409 mHWData->mHWVersion = aHardwareVersion;
1410
1411 return S_OK;
1412}
1413
1414HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1415{
1416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1417
1418 if (!mHWData->mHardwareUUID.isZero())
1419 aHardwareUUID = mHWData->mHardwareUUID;
1420 else
1421 aHardwareUUID = mData->mUuid;
1422
1423 return S_OK;
1424}
1425
1426HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1427{
1428 if (!aHardwareUUID.isValid())
1429 return E_INVALIDARG;
1430
1431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1432
1433 HRESULT rc = i_checkStateDependency(MutableStateDep);
1434 if (FAILED(rc)) return rc;
1435
1436 i_setModified(IsModified_MachineData);
1437 mHWData.backup();
1438 if (aHardwareUUID == mData->mUuid)
1439 mHWData->mHardwareUUID.clear();
1440 else
1441 mHWData->mHardwareUUID = aHardwareUUID;
1442
1443 return S_OK;
1444}
1445
1446HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1447{
1448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1449
1450 *aMemorySize = mHWData->mMemorySize;
1451
1452 return S_OK;
1453}
1454
1455HRESULT Machine::setMemorySize(ULONG aMemorySize)
1456{
1457 /* check RAM limits */
1458 if ( aMemorySize < MM_RAM_MIN_IN_MB
1459 || aMemorySize > MM_RAM_MAX_IN_MB
1460 )
1461 return setError(E_INVALIDARG,
1462 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1463 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1464
1465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1466
1467 HRESULT rc = i_checkStateDependency(MutableStateDep);
1468 if (FAILED(rc)) return rc;
1469
1470 i_setModified(IsModified_MachineData);
1471 mHWData.backup();
1472 mHWData->mMemorySize = aMemorySize;
1473
1474 return S_OK;
1475}
1476
1477HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1478{
1479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 *aCPUCount = mHWData->mCPUCount;
1482
1483 return S_OK;
1484}
1485
1486HRESULT Machine::setCPUCount(ULONG aCPUCount)
1487{
1488 /* check CPU limits */
1489 if ( aCPUCount < SchemaDefs::MinCPUCount
1490 || aCPUCount > SchemaDefs::MaxCPUCount
1491 )
1492 return setError(E_INVALIDARG,
1493 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1494 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1495
1496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1497
1498 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1499 if (mHWData->mCPUHotPlugEnabled)
1500 {
1501 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1502 {
1503 if (mHWData->mCPUAttached[idx])
1504 return setError(E_INVALIDARG,
1505 tr("There is still a CPU attached to socket %lu."
1506 "Detach the CPU before removing the socket"),
1507 aCPUCount, idx+1);
1508 }
1509 }
1510
1511 HRESULT rc = i_checkStateDependency(MutableStateDep);
1512 if (FAILED(rc)) return rc;
1513
1514 i_setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCPUCount = aCPUCount;
1517
1518 return S_OK;
1519}
1520
1521HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1522{
1523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1524
1525 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1526
1527 return S_OK;
1528}
1529
1530HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1531{
1532 HRESULT rc = S_OK;
1533
1534 /* check throttle limits */
1535 if ( aCPUExecutionCap < 1
1536 || aCPUExecutionCap > 100
1537 )
1538 return setError(E_INVALIDARG,
1539 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1540 aCPUExecutionCap, 1, 100);
1541
1542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1543
1544 alock.release();
1545 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1546 alock.acquire();
1547 if (FAILED(rc)) return rc;
1548
1549 i_setModified(IsModified_MachineData);
1550 mHWData.backup();
1551 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1552
1553 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1554 if (Global::IsOnline(mData->mMachineState))
1555 i_saveSettings(NULL);
1556
1557 return S_OK;
1558}
1559
1560HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1561{
1562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1563
1564 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1565
1566 return S_OK;
1567}
1568
1569HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1570{
1571 HRESULT rc = S_OK;
1572
1573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1574
1575 rc = i_checkStateDependency(MutableStateDep);
1576 if (FAILED(rc)) return rc;
1577
1578 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1579 {
1580 if (aCPUHotPlugEnabled)
1581 {
1582 i_setModified(IsModified_MachineData);
1583 mHWData.backup();
1584
1585 /* Add the amount of CPUs currently attached */
1586 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1587 mHWData->mCPUAttached[i] = true;
1588 }
1589 else
1590 {
1591 /*
1592 * We can disable hotplug only if the amount of maximum CPUs is equal
1593 * to the amount of attached CPUs
1594 */
1595 unsigned cCpusAttached = 0;
1596 unsigned iHighestId = 0;
1597
1598 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1599 {
1600 if (mHWData->mCPUAttached[i])
1601 {
1602 cCpusAttached++;
1603 iHighestId = i;
1604 }
1605 }
1606
1607 if ( (cCpusAttached != mHWData->mCPUCount)
1608 || (iHighestId >= mHWData->mCPUCount))
1609 return setError(E_INVALIDARG,
1610 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1611
1612 i_setModified(IsModified_MachineData);
1613 mHWData.backup();
1614 }
1615 }
1616
1617 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1618
1619 return rc;
1620}
1621
1622HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1623{
1624 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1627
1628 return S_OK;
1629}
1630
1631HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1632{
1633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1634
1635 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1636 if (SUCCEEDED(hrc))
1637 {
1638 i_setModified(IsModified_MachineData);
1639 mHWData.backup();
1640 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1641 }
1642 return hrc;
1643}
1644
1645HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1646{
1647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1648 aCPUProfile = mHWData->mCpuProfile;
1649 return S_OK;
1650}
1651
1652HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1653{
1654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1655 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1656 if (SUCCEEDED(hrc))
1657 {
1658 i_setModified(IsModified_MachineData);
1659 mHWData.backup();
1660 /* Empty equals 'host'. */
1661 if (aCPUProfile.isNotEmpty())
1662 mHWData->mCpuProfile = aCPUProfile;
1663 else
1664 mHWData->mCpuProfile = "host";
1665 }
1666 return hrc;
1667}
1668
1669HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1670{
1671#ifdef VBOX_WITH_USB_CARDREADER
1672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1675
1676 return S_OK;
1677#else
1678 NOREF(aEmulatedUSBCardReaderEnabled);
1679 return E_NOTIMPL;
1680#endif
1681}
1682
1683HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1684{
1685#ifdef VBOX_WITH_USB_CARDREADER
1686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1687
1688 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1689 if (FAILED(rc)) return rc;
1690
1691 i_setModified(IsModified_MachineData);
1692 mHWData.backup();
1693 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1694
1695 return S_OK;
1696#else
1697 NOREF(aEmulatedUSBCardReaderEnabled);
1698 return E_NOTIMPL;
1699#endif
1700}
1701
1702HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1703{
1704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1705
1706 *aHPETEnabled = mHWData->mHPETEnabled;
1707
1708 return S_OK;
1709}
1710
1711HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1712{
1713 HRESULT rc = S_OK;
1714
1715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 rc = i_checkStateDependency(MutableStateDep);
1718 if (FAILED(rc)) return rc;
1719
1720 i_setModified(IsModified_MachineData);
1721 mHWData.backup();
1722
1723 mHWData->mHPETEnabled = aHPETEnabled;
1724
1725 return rc;
1726}
1727
1728HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1729{
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1733
1734 return S_OK;
1735}
1736
1737HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1738{
1739 switch (aGraphicsControllerType)
1740 {
1741 case GraphicsControllerType_Null:
1742 case GraphicsControllerType_VBoxVGA:
1743#ifdef VBOX_WITH_VMSVGA
1744 case GraphicsControllerType_VMSVGA:
1745 case GraphicsControllerType_VBoxSVGA:
1746#endif
1747 break;
1748 default:
1749 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1750 }
1751
1752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1753
1754 HRESULT rc = i_checkStateDependency(MutableStateDep);
1755 if (FAILED(rc)) return rc;
1756
1757 i_setModified(IsModified_MachineData);
1758 mHWData.backup();
1759 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1760
1761 return S_OK;
1762}
1763
1764HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1765{
1766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1767
1768 *aVRAMSize = mHWData->mVRAMSize;
1769
1770 return S_OK;
1771}
1772
1773HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1774{
1775 /* check VRAM limits */
1776 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1777 return setError(E_INVALIDARG,
1778 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1779 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1780
1781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1782
1783 HRESULT rc = i_checkStateDependency(MutableStateDep);
1784 if (FAILED(rc)) return rc;
1785
1786 i_setModified(IsModified_MachineData);
1787 mHWData.backup();
1788 mHWData->mVRAMSize = aVRAMSize;
1789
1790 return S_OK;
1791}
1792
1793/** @todo this method should not be public */
1794HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1795{
1796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1797
1798 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1799
1800 return S_OK;
1801}
1802
1803/**
1804 * Set the memory balloon size.
1805 *
1806 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1807 * we have to make sure that we never call IGuest from here.
1808 */
1809HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1810{
1811 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1812#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1813 /* check limits */
1814 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1815 return setError(E_INVALIDARG,
1816 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1817 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1818
1819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1820
1821 i_setModified(IsModified_MachineData);
1822 mHWData.backup();
1823 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1824
1825 return S_OK;
1826#else
1827 NOREF(aMemoryBalloonSize);
1828 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1829#endif
1830}
1831
1832HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1833{
1834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1835
1836 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1837 return S_OK;
1838}
1839
1840HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1841{
1842#ifdef VBOX_WITH_PAGE_SHARING
1843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1844
1845 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1846 i_setModified(IsModified_MachineData);
1847 mHWData.backup();
1848 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1849 return S_OK;
1850#else
1851 NOREF(aPageFusionEnabled);
1852 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1853#endif
1854}
1855
1856HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1857{
1858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1859
1860 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1861
1862 return S_OK;
1863}
1864
1865HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1866{
1867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1868
1869 HRESULT rc = i_checkStateDependency(MutableStateDep);
1870 if (FAILED(rc)) return rc;
1871
1872 /** @todo check validity! */
1873
1874 i_setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1877
1878 return S_OK;
1879}
1880
1881
1882HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1883{
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885
1886 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1887
1888 return S_OK;
1889}
1890
1891HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1892{
1893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1894
1895 HRESULT rc = i_checkStateDependency(MutableStateDep);
1896 if (FAILED(rc)) return rc;
1897
1898 /** @todo check validity! */
1899 i_setModified(IsModified_MachineData);
1900 mHWData.backup();
1901 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1902
1903 return S_OK;
1904}
1905
1906HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1907{
1908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1909
1910 *aMonitorCount = mHWData->mMonitorCount;
1911
1912 return S_OK;
1913}
1914
1915HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1916{
1917 /* make sure monitor count is a sensible number */
1918 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1919 return setError(E_INVALIDARG,
1920 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1921 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1922
1923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 HRESULT rc = i_checkStateDependency(MutableStateDep);
1926 if (FAILED(rc)) return rc;
1927
1928 i_setModified(IsModified_MachineData);
1929 mHWData.backup();
1930 mHWData->mMonitorCount = aMonitorCount;
1931
1932 return S_OK;
1933}
1934
1935HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1936{
1937 /* mBIOSSettings is constant during life time, no need to lock */
1938 aBIOSSettings = mBIOSSettings;
1939
1940 return S_OK;
1941}
1942
1943HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1944{
1945 /* mRecordingSettings is constant during life time, no need to lock */
1946 aRecordingSettings = mRecordingSettings;
1947
1948 return S_OK;
1949}
1950
1951HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1952{
1953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1954
1955 switch (aProperty)
1956 {
1957 case CPUPropertyType_PAE:
1958 *aValue = mHWData->mPAEEnabled;
1959 break;
1960
1961 case CPUPropertyType_LongMode:
1962 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1963 *aValue = TRUE;
1964 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1965 *aValue = FALSE;
1966#if HC_ARCH_BITS == 64
1967 else
1968 *aValue = TRUE;
1969#else
1970 else
1971 {
1972 *aValue = FALSE;
1973
1974 ComObjPtr<GuestOSType> pGuestOSType;
1975 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1976 pGuestOSType);
1977 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1978 {
1979 if (pGuestOSType->i_is64Bit())
1980 {
1981 ComObjPtr<Host> pHost = mParent->i_host();
1982 alock.release();
1983
1984 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1985 if (FAILED(hrc2))
1986 *aValue = FALSE;
1987 }
1988 }
1989 }
1990#endif
1991 break;
1992
1993 case CPUPropertyType_TripleFaultReset:
1994 *aValue = mHWData->mTripleFaultReset;
1995 break;
1996
1997 case CPUPropertyType_APIC:
1998 *aValue = mHWData->mAPIC;
1999 break;
2000
2001 case CPUPropertyType_X2APIC:
2002 *aValue = mHWData->mX2APIC;
2003 break;
2004
2005 case CPUPropertyType_IBPBOnVMExit:
2006 *aValue = mHWData->mIBPBOnVMExit;
2007 break;
2008
2009 case CPUPropertyType_IBPBOnVMEntry:
2010 *aValue = mHWData->mIBPBOnVMEntry;
2011 break;
2012
2013 case CPUPropertyType_SpecCtrl:
2014 *aValue = mHWData->mSpecCtrl;
2015 break;
2016
2017 case CPUPropertyType_SpecCtrlByHost:
2018 *aValue = mHWData->mSpecCtrlByHost;
2019 break;
2020
2021 case CPUPropertyType_HWVirt:
2022 *aValue = mHWData->mNestedHWVirt;
2023 break;
2024
2025 default:
2026 return E_INVALIDARG;
2027 }
2028 return S_OK;
2029}
2030
2031HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2032{
2033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 HRESULT rc = i_checkStateDependency(MutableStateDep);
2036 if (FAILED(rc)) return rc;
2037
2038 switch (aProperty)
2039 {
2040 case CPUPropertyType_PAE:
2041 i_setModified(IsModified_MachineData);
2042 mHWData.backup();
2043 mHWData->mPAEEnabled = !!aValue;
2044 break;
2045
2046 case CPUPropertyType_LongMode:
2047 i_setModified(IsModified_MachineData);
2048 mHWData.backup();
2049 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2050 break;
2051
2052 case CPUPropertyType_TripleFaultReset:
2053 i_setModified(IsModified_MachineData);
2054 mHWData.backup();
2055 mHWData->mTripleFaultReset = !!aValue;
2056 break;
2057
2058 case CPUPropertyType_APIC:
2059 if (mHWData->mX2APIC)
2060 aValue = TRUE;
2061 i_setModified(IsModified_MachineData);
2062 mHWData.backup();
2063 mHWData->mAPIC = !!aValue;
2064 break;
2065
2066 case CPUPropertyType_X2APIC:
2067 i_setModified(IsModified_MachineData);
2068 mHWData.backup();
2069 mHWData->mX2APIC = !!aValue;
2070 if (aValue)
2071 mHWData->mAPIC = !!aValue;
2072 break;
2073
2074 case CPUPropertyType_IBPBOnVMExit:
2075 i_setModified(IsModified_MachineData);
2076 mHWData.backup();
2077 mHWData->mIBPBOnVMExit = !!aValue;
2078 break;
2079
2080 case CPUPropertyType_IBPBOnVMEntry:
2081 i_setModified(IsModified_MachineData);
2082 mHWData.backup();
2083 mHWData->mIBPBOnVMEntry = !!aValue;
2084 break;
2085
2086 case CPUPropertyType_SpecCtrl:
2087 i_setModified(IsModified_MachineData);
2088 mHWData.backup();
2089 mHWData->mSpecCtrl = !!aValue;
2090 break;
2091
2092 case CPUPropertyType_SpecCtrlByHost:
2093 i_setModified(IsModified_MachineData);
2094 mHWData.backup();
2095 mHWData->mSpecCtrlByHost = !!aValue;
2096 break;
2097
2098 case CPUPropertyType_HWVirt:
2099 i_setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mNestedHWVirt = !!aValue;
2102 break;
2103
2104 default:
2105 return E_INVALIDARG;
2106 }
2107 return S_OK;
2108}
2109
2110HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2111 ULONG *aValEcx, ULONG *aValEdx)
2112{
2113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2114 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2115 {
2116 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2117 it != mHWData->mCpuIdLeafList.end();
2118 ++it)
2119 {
2120 if (aOrdinal == 0)
2121 {
2122 const settings::CpuIdLeaf &rLeaf= *it;
2123 *aIdx = rLeaf.idx;
2124 *aSubIdx = rLeaf.idxSub;
2125 *aValEax = rLeaf.uEax;
2126 *aValEbx = rLeaf.uEbx;
2127 *aValEcx = rLeaf.uEcx;
2128 *aValEdx = rLeaf.uEdx;
2129 return S_OK;
2130 }
2131 aOrdinal--;
2132 }
2133 }
2134 return E_INVALIDARG;
2135}
2136
2137HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2138{
2139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 /*
2142 * Search the list.
2143 */
2144 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2145 {
2146 const settings::CpuIdLeaf &rLeaf= *it;
2147 if ( rLeaf.idx == aIdx
2148 && ( aSubIdx == UINT32_MAX
2149 || rLeaf.idxSub == aSubIdx) )
2150 {
2151 *aValEax = rLeaf.uEax;
2152 *aValEbx = rLeaf.uEbx;
2153 *aValEcx = rLeaf.uEcx;
2154 *aValEdx = rLeaf.uEdx;
2155 return S_OK;
2156 }
2157 }
2158
2159 return E_INVALIDARG;
2160}
2161
2162
2163HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2164{
2165 /*
2166 * Validate input before taking locks and checking state.
2167 */
2168 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2169 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2170 if ( aIdx >= UINT32_C(0x20)
2171 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2172 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2173 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2174
2175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2176 HRESULT rc = i_checkStateDependency(MutableStateDep);
2177 if (FAILED(rc)) return rc;
2178
2179 /*
2180 * Impose a maximum number of leaves.
2181 */
2182 if (mHWData->mCpuIdLeafList.size() > 256)
2183 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2184
2185 /*
2186 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2187 */
2188 i_setModified(IsModified_MachineData);
2189 mHWData.backup();
2190
2191 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2192 {
2193 settings::CpuIdLeaf &rLeaf= *it;
2194 if ( rLeaf.idx == aIdx
2195 && ( aSubIdx == UINT32_MAX
2196 || rLeaf.idxSub == aSubIdx) )
2197 it = mHWData->mCpuIdLeafList.erase(it);
2198 else
2199 ++it;
2200 }
2201
2202 settings::CpuIdLeaf NewLeaf;
2203 NewLeaf.idx = aIdx;
2204 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2205 NewLeaf.uEax = aValEax;
2206 NewLeaf.uEbx = aValEbx;
2207 NewLeaf.uEcx = aValEcx;
2208 NewLeaf.uEdx = aValEdx;
2209 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2210 return S_OK;
2211}
2212
2213HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2214{
2215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2216
2217 HRESULT rc = i_checkStateDependency(MutableStateDep);
2218 if (FAILED(rc)) return rc;
2219
2220 /*
2221 * Do the removal.
2222 */
2223 bool fModified = false;
2224 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2225 {
2226 settings::CpuIdLeaf &rLeaf= *it;
2227 if ( rLeaf.idx == aIdx
2228 && ( aSubIdx == UINT32_MAX
2229 || rLeaf.idxSub == aSubIdx) )
2230 {
2231 if (!fModified)
2232 {
2233 fModified = true;
2234 i_setModified(IsModified_MachineData);
2235 mHWData.backup();
2236 }
2237 it = mHWData->mCpuIdLeafList.erase(it);
2238 }
2239 else
2240 ++it;
2241 }
2242
2243 return S_OK;
2244}
2245
2246HRESULT Machine::removeAllCPUIDLeaves()
2247{
2248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2249
2250 HRESULT rc = i_checkStateDependency(MutableStateDep);
2251 if (FAILED(rc)) return rc;
2252
2253 if (mHWData->mCpuIdLeafList.size() > 0)
2254 {
2255 i_setModified(IsModified_MachineData);
2256 mHWData.backup();
2257
2258 mHWData->mCpuIdLeafList.clear();
2259 }
2260
2261 return S_OK;
2262}
2263HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2264{
2265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2266
2267 switch(aProperty)
2268 {
2269 case HWVirtExPropertyType_Enabled:
2270 *aValue = mHWData->mHWVirtExEnabled;
2271 break;
2272
2273 case HWVirtExPropertyType_VPID:
2274 *aValue = mHWData->mHWVirtExVPIDEnabled;
2275 break;
2276
2277 case HWVirtExPropertyType_NestedPaging:
2278 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2279 break;
2280
2281 case HWVirtExPropertyType_UnrestrictedExecution:
2282 *aValue = mHWData->mHWVirtExUXEnabled;
2283 break;
2284
2285 case HWVirtExPropertyType_LargePages:
2286 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2287#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2288 *aValue = FALSE;
2289#endif
2290 break;
2291
2292 case HWVirtExPropertyType_Force:
2293 *aValue = mHWData->mHWVirtExForceEnabled;
2294 break;
2295
2296 case HWVirtExPropertyType_UseNativeApi:
2297 *aValue = mHWData->mHWVirtExUseNativeApi;
2298 break;
2299
2300 default:
2301 return E_INVALIDARG;
2302 }
2303 return S_OK;
2304}
2305
2306HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2307{
2308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2309
2310 HRESULT rc = i_checkStateDependency(MutableStateDep);
2311 if (FAILED(rc)) return rc;
2312
2313 switch (aProperty)
2314 {
2315 case HWVirtExPropertyType_Enabled:
2316 i_setModified(IsModified_MachineData);
2317 mHWData.backup();
2318 mHWData->mHWVirtExEnabled = !!aValue;
2319 break;
2320
2321 case HWVirtExPropertyType_VPID:
2322 i_setModified(IsModified_MachineData);
2323 mHWData.backup();
2324 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2325 break;
2326
2327 case HWVirtExPropertyType_NestedPaging:
2328 i_setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2331 break;
2332
2333 case HWVirtExPropertyType_UnrestrictedExecution:
2334 i_setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mHWVirtExUXEnabled = !!aValue;
2337 break;
2338
2339 case HWVirtExPropertyType_LargePages:
2340 i_setModified(IsModified_MachineData);
2341 mHWData.backup();
2342 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2343 break;
2344
2345 case HWVirtExPropertyType_Force:
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 mHWData->mHWVirtExForceEnabled = !!aValue;
2349 break;
2350
2351 case HWVirtExPropertyType_UseNativeApi:
2352 i_setModified(IsModified_MachineData);
2353 mHWData.backup();
2354 mHWData->mHWVirtExUseNativeApi = !!aValue;
2355 break;
2356
2357 default:
2358 return E_INVALIDARG;
2359 }
2360
2361 return S_OK;
2362}
2363
2364HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2365{
2366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2367
2368 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2369
2370 return S_OK;
2371}
2372
2373HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2374{
2375 /** @todo (r=dmik):
2376 * 1. Allow to change the name of the snapshot folder containing snapshots
2377 * 2. Rename the folder on disk instead of just changing the property
2378 * value (to be smart and not to leave garbage). Note that it cannot be
2379 * done here because the change may be rolled back. Thus, the right
2380 * place is #saveSettings().
2381 */
2382
2383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2384
2385 HRESULT rc = i_checkStateDependency(MutableStateDep);
2386 if (FAILED(rc)) return rc;
2387
2388 if (!mData->mCurrentSnapshot.isNull())
2389 return setError(E_FAIL,
2390 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2391
2392 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2393
2394 if (strSnapshotFolder.isEmpty())
2395 strSnapshotFolder = "Snapshots";
2396 int vrc = i_calculateFullPath(strSnapshotFolder,
2397 strSnapshotFolder);
2398 if (RT_FAILURE(vrc))
2399 return setErrorBoth(E_FAIL, vrc,
2400 tr("Invalid snapshot folder '%s' (%Rrc)"),
2401 strSnapshotFolder.c_str(), vrc);
2402
2403 i_setModified(IsModified_MachineData);
2404 mUserData.backup();
2405
2406 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2407
2408 return S_OK;
2409}
2410
2411HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2412{
2413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2414
2415 aMediumAttachments.resize(mMediumAttachments->size());
2416 size_t i = 0;
2417 for (MediumAttachmentList::const_iterator
2418 it = mMediumAttachments->begin();
2419 it != mMediumAttachments->end();
2420 ++it, ++i)
2421 aMediumAttachments[i] = *it;
2422
2423 return S_OK;
2424}
2425
2426HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2427{
2428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2429
2430 Assert(!!mVRDEServer);
2431
2432 aVRDEServer = mVRDEServer;
2433
2434 return S_OK;
2435}
2436
2437HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2438{
2439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2440
2441 aAudioAdapter = mAudioAdapter;
2442
2443 return S_OK;
2444}
2445
2446HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2447{
2448#ifdef VBOX_WITH_VUSB
2449 clearError();
2450 MultiResult rc(S_OK);
2451
2452# ifdef VBOX_WITH_USB
2453 rc = mParent->i_host()->i_checkUSBProxyService();
2454 if (FAILED(rc)) return rc;
2455# endif
2456
2457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 aUSBControllers.resize(mUSBControllers->size());
2460 size_t i = 0;
2461 for (USBControllerList::const_iterator
2462 it = mUSBControllers->begin();
2463 it != mUSBControllers->end();
2464 ++it, ++i)
2465 aUSBControllers[i] = *it;
2466
2467 return S_OK;
2468#else
2469 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2470 * extended error info to indicate that USB is simply not available
2471 * (w/o treating it as a failure), for example, as in OSE */
2472 NOREF(aUSBControllers);
2473 ReturnComNotImplemented();
2474#endif /* VBOX_WITH_VUSB */
2475}
2476
2477HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2478{
2479#ifdef VBOX_WITH_VUSB
2480 clearError();
2481 MultiResult rc(S_OK);
2482
2483# ifdef VBOX_WITH_USB
2484 rc = mParent->i_host()->i_checkUSBProxyService();
2485 if (FAILED(rc)) return rc;
2486# endif
2487
2488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2489
2490 aUSBDeviceFilters = mUSBDeviceFilters;
2491 return rc;
2492#else
2493 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2494 * extended error info to indicate that USB is simply not available
2495 * (w/o treating it as a failure), for example, as in OSE */
2496 NOREF(aUSBDeviceFilters);
2497 ReturnComNotImplemented();
2498#endif /* VBOX_WITH_VUSB */
2499}
2500
2501HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2502{
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 aSettingsFilePath = mData->m_strConfigFileFull;
2506
2507 return S_OK;
2508}
2509
2510HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2511{
2512 RT_NOREF(aSettingsFilePath);
2513 ReturnComNotImplemented();
2514}
2515
2516HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2517{
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2521 if (FAILED(rc)) return rc;
2522
2523 if (!mData->pMachineConfigFile->fileExists())
2524 // this is a new machine, and no config file exists yet:
2525 *aSettingsModified = TRUE;
2526 else
2527 *aSettingsModified = (mData->flModifications != 0);
2528
2529 return S_OK;
2530}
2531
2532HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2533{
2534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 *aSessionState = mData->mSession.mState;
2537
2538 return S_OK;
2539}
2540
2541HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2542{
2543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2544
2545 aSessionName = mData->mSession.mName;
2546
2547 return S_OK;
2548}
2549
2550HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2551{
2552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2553
2554 *aSessionPID = mData->mSession.mPID;
2555
2556 return S_OK;
2557}
2558
2559HRESULT Machine::getState(MachineState_T *aState)
2560{
2561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2562
2563 *aState = mData->mMachineState;
2564 Assert(mData->mMachineState != MachineState_Null);
2565
2566 return S_OK;
2567}
2568
2569HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2570{
2571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2572
2573 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2574
2575 return S_OK;
2576}
2577
2578HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2579{
2580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2581
2582 aStateFilePath = mSSData->strStateFilePath;
2583
2584 return S_OK;
2585}
2586
2587HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2588{
2589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2590
2591 i_getLogFolder(aLogFolder);
2592
2593 return S_OK;
2594}
2595
2596HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2597{
2598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2599
2600 aCurrentSnapshot = mData->mCurrentSnapshot;
2601
2602 return S_OK;
2603}
2604
2605HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2606{
2607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2608
2609 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2610 ? 0
2611 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2612
2613 return S_OK;
2614}
2615
2616HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2617{
2618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2619
2620 /* Note: for machines with no snapshots, we always return FALSE
2621 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2622 * reasons :) */
2623
2624 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2625 ? FALSE
2626 : mData->mCurrentStateModified;
2627
2628 return S_OK;
2629}
2630
2631HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2632{
2633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 aSharedFolders.resize(mHWData->mSharedFolders.size());
2636 size_t i = 0;
2637 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2638 it = mHWData->mSharedFolders.begin();
2639 it != mHWData->mSharedFolders.end();
2640 ++it, ++i)
2641 aSharedFolders[i] = *it;
2642
2643 return S_OK;
2644}
2645
2646HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2647{
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 *aClipboardMode = mHWData->mClipboardMode;
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2656{
2657 HRESULT rc = S_OK;
2658
2659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2660
2661 alock.release();
2662 rc = i_onClipboardModeChange(aClipboardMode);
2663 alock.acquire();
2664 if (FAILED(rc)) return rc;
2665
2666 i_setModified(IsModified_MachineData);
2667 mHWData.backup();
2668 mHWData->mClipboardMode = aClipboardMode;
2669
2670 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2671 if (Global::IsOnline(mData->mMachineState))
2672 i_saveSettings(NULL);
2673
2674 return S_OK;
2675}
2676
2677HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2678{
2679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2680
2681 *aDnDMode = mHWData->mDnDMode;
2682
2683 return S_OK;
2684}
2685
2686HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2687{
2688 HRESULT rc = S_OK;
2689
2690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 alock.release();
2693 rc = i_onDnDModeChange(aDnDMode);
2694
2695 alock.acquire();
2696 if (FAILED(rc)) return rc;
2697
2698 i_setModified(IsModified_MachineData);
2699 mHWData.backup();
2700 mHWData->mDnDMode = aDnDMode;
2701
2702 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2703 if (Global::IsOnline(mData->mMachineState))
2704 i_saveSettings(NULL);
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2710{
2711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2712
2713 aStorageControllers.resize(mStorageControllers->size());
2714 size_t i = 0;
2715 for (StorageControllerList::const_iterator
2716 it = mStorageControllers->begin();
2717 it != mStorageControllers->end();
2718 ++it, ++i)
2719 aStorageControllers[i] = *it;
2720
2721 return S_OK;
2722}
2723
2724HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2725{
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 *aEnabled = mUserData->s.fTeleporterEnabled;
2729
2730 return S_OK;
2731}
2732
2733HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2734{
2735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 /* Only allow it to be set to true when PoweredOff or Aborted.
2738 (Clearing it is always permitted.) */
2739 if ( aTeleporterEnabled
2740 && mData->mRegistered
2741 && ( !i_isSessionMachine()
2742 || ( mData->mMachineState != MachineState_PoweredOff
2743 && mData->mMachineState != MachineState_Teleported
2744 && mData->mMachineState != MachineState_Aborted
2745 )
2746 )
2747 )
2748 return setError(VBOX_E_INVALID_VM_STATE,
2749 tr("The machine is not powered off (state is %s)"),
2750 Global::stringifyMachineState(mData->mMachineState));
2751
2752 i_setModified(IsModified_MachineData);
2753 mUserData.backup();
2754 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2764
2765 return S_OK;
2766}
2767
2768HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2769{
2770 if (aTeleporterPort >= _64K)
2771 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2772
2773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2776 if (FAILED(rc)) return rc;
2777
2778 i_setModified(IsModified_MachineData);
2779 mUserData.backup();
2780 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2781
2782 return S_OK;
2783}
2784
2785HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2786{
2787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2788
2789 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2790
2791 return S_OK;
2792}
2793
2794HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2795{
2796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2799 if (FAILED(rc)) return rc;
2800
2801 i_setModified(IsModified_MachineData);
2802 mUserData.backup();
2803 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2812
2813 return S_OK;
2814}
2815
2816HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2817{
2818 /*
2819 * Hash the password first.
2820 */
2821 com::Utf8Str aT = aTeleporterPassword;
2822
2823 if (!aT.isEmpty())
2824 {
2825 if (VBoxIsPasswordHashed(&aT))
2826 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2827 VBoxHashPassword(&aT);
2828 }
2829
2830 /*
2831 * Do the update.
2832 */
2833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2834 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2835 if (SUCCEEDED(hrc))
2836 {
2837 i_setModified(IsModified_MachineData);
2838 mUserData.backup();
2839 mUserData->s.strTeleporterPassword = aT;
2840 }
2841
2842 return hrc;
2843}
2844
2845HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2846{
2847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2850 return S_OK;
2851}
2852
2853HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2854{
2855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 /** @todo deal with running state change. */
2858 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2859 if (FAILED(rc)) return rc;
2860
2861 i_setModified(IsModified_MachineData);
2862 mUserData.backup();
2863 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2864 return S_OK;
2865}
2866
2867HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870
2871 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2872 return S_OK;
2873}
2874
2875HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2876{
2877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 /** @todo deal with running state change. */
2880 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2881 if (FAILED(rc)) return rc;
2882
2883 i_setModified(IsModified_MachineData);
2884 mUserData.backup();
2885 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2886 return S_OK;
2887}
2888
2889HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2890{
2891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2892
2893 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2894 return S_OK;
2895}
2896
2897HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2898{
2899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2900
2901 /** @todo deal with running state change. */
2902 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2903 if (FAILED(rc)) return rc;
2904
2905 i_setModified(IsModified_MachineData);
2906 mUserData.backup();
2907 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2908 return S_OK;
2909}
2910
2911HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2912{
2913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2914
2915 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2916
2917 return S_OK;
2918}
2919
2920HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2921{
2922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2923
2924 /** @todo deal with running state change. */
2925 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2926 if (FAILED(rc)) return rc;
2927
2928 i_setModified(IsModified_MachineData);
2929 mUserData.backup();
2930 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2931
2932 return S_OK;
2933}
2934
2935HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2936{
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2940 return S_OK;
2941}
2942
2943HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2944{
2945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2946
2947 /** @todo deal with running state change. */
2948 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2949 if (FAILED(rc)) return rc;
2950
2951 i_setModified(IsModified_MachineData);
2952 mUserData.backup();
2953 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2954 return S_OK;
2955}
2956
2957HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2958{
2959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2962
2963 return S_OK;
2964}
2965
2966HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2967{
2968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2969
2970 /* Only allow it to be set to true when PoweredOff or Aborted.
2971 (Clearing it is always permitted.) */
2972 if ( aRTCUseUTC
2973 && mData->mRegistered
2974 && ( !i_isSessionMachine()
2975 || ( mData->mMachineState != MachineState_PoweredOff
2976 && mData->mMachineState != MachineState_Teleported
2977 && mData->mMachineState != MachineState_Aborted
2978 )
2979 )
2980 )
2981 return setError(VBOX_E_INVALID_VM_STATE,
2982 tr("The machine is not powered off (state is %s)"),
2983 Global::stringifyMachineState(mData->mMachineState));
2984
2985 i_setModified(IsModified_MachineData);
2986 mUserData.backup();
2987 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2988
2989 return S_OK;
2990}
2991
2992HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2993{
2994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2995
2996 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2997
2998 return S_OK;
2999}
3000
3001HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3002{
3003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3004
3005 HRESULT rc = i_checkStateDependency(MutableStateDep);
3006 if (FAILED(rc)) return rc;
3007
3008 i_setModified(IsModified_MachineData);
3009 mHWData.backup();
3010 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3011
3012 return S_OK;
3013}
3014
3015HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3016{
3017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3018
3019 *aIOCacheSize = mHWData->mIOCacheSize;
3020
3021 return S_OK;
3022}
3023
3024HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3025{
3026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3027
3028 HRESULT rc = i_checkStateDependency(MutableStateDep);
3029 if (FAILED(rc)) return rc;
3030
3031 i_setModified(IsModified_MachineData);
3032 mHWData.backup();
3033 mHWData->mIOCacheSize = aIOCacheSize;
3034
3035 return S_OK;
3036}
3037
3038
3039/**
3040 * @note Locks objects!
3041 */
3042HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3043 LockType_T aLockType)
3044{
3045 /* check the session state */
3046 SessionState_T state;
3047 HRESULT rc = aSession->COMGETTER(State)(&state);
3048 if (FAILED(rc)) return rc;
3049
3050 if (state != SessionState_Unlocked)
3051 return setError(VBOX_E_INVALID_OBJECT_STATE,
3052 tr("The given session is busy"));
3053
3054 // get the client's IInternalSessionControl interface
3055 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3056 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3057 E_INVALIDARG);
3058
3059 // session name (only used in some code paths)
3060 Utf8Str strSessionName;
3061
3062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3063
3064 if (!mData->mRegistered)
3065 return setError(E_UNEXPECTED,
3066 tr("The machine '%s' is not registered"),
3067 mUserData->s.strName.c_str());
3068
3069 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3070
3071 SessionState_T oldState = mData->mSession.mState;
3072 /* Hack: in case the session is closing and there is a progress object
3073 * which allows waiting for the session to be closed, take the opportunity
3074 * and do a limited wait (max. 1 second). This helps a lot when the system
3075 * is busy and thus session closing can take a little while. */
3076 if ( mData->mSession.mState == SessionState_Unlocking
3077 && mData->mSession.mProgress)
3078 {
3079 alock.release();
3080 mData->mSession.mProgress->WaitForCompletion(1000);
3081 alock.acquire();
3082 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3083 }
3084
3085 // try again now
3086 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3087 // (i.e. session machine exists)
3088 && (aLockType == LockType_Shared) // caller wants a shared link to the
3089 // existing session that holds the write lock:
3090 )
3091 {
3092 // OK, share the session... we are now dealing with three processes:
3093 // 1) VBoxSVC (where this code runs);
3094 // 2) process C: the caller's client process (who wants a shared session);
3095 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3096
3097 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3098 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3099 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3100 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3101 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3102
3103 /*
3104 * Release the lock before calling the client process. It's safe here
3105 * since the only thing to do after we get the lock again is to add
3106 * the remote control to the list (which doesn't directly influence
3107 * anything).
3108 */
3109 alock.release();
3110
3111 // get the console of the session holding the write lock (this is a remote call)
3112 ComPtr<IConsole> pConsoleW;
3113 if (mData->mSession.mLockType == LockType_VM)
3114 {
3115 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3116 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3117 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3118 if (FAILED(rc))
3119 // the failure may occur w/o any error info (from RPC), so provide one
3120 return setError(VBOX_E_VM_ERROR,
3121 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3122 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3123 }
3124
3125 // share the session machine and W's console with the caller's session
3126 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3127 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3128 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3129
3130 if (FAILED(rc))
3131 // the failure may occur w/o any error info (from RPC), so provide one
3132 return setError(VBOX_E_VM_ERROR,
3133 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3134 alock.acquire();
3135
3136 // need to revalidate the state after acquiring the lock again
3137 if (mData->mSession.mState != SessionState_Locked)
3138 {
3139 pSessionControl->Uninitialize();
3140 return setError(VBOX_E_INVALID_SESSION_STATE,
3141 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3142 mUserData->s.strName.c_str());
3143 }
3144
3145 // add the caller's session to the list
3146 mData->mSession.mRemoteControls.push_back(pSessionControl);
3147 }
3148 else if ( mData->mSession.mState == SessionState_Locked
3149 || mData->mSession.mState == SessionState_Unlocking
3150 )
3151 {
3152 // sharing not permitted, or machine still unlocking:
3153 return setError(VBOX_E_INVALID_OBJECT_STATE,
3154 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3155 mUserData->s.strName.c_str());
3156 }
3157 else
3158 {
3159 // machine is not locked: then write-lock the machine (create the session machine)
3160
3161 // must not be busy
3162 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3163
3164 // get the caller's session PID
3165 RTPROCESS pid = NIL_RTPROCESS;
3166 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3167 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3168 Assert(pid != NIL_RTPROCESS);
3169
3170 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3171
3172 if (fLaunchingVMProcess)
3173 {
3174 if (mData->mSession.mPID == NIL_RTPROCESS)
3175 {
3176 // two or more clients racing for a lock, the one which set the
3177 // session state to Spawning will win, the others will get an
3178 // error as we can't decide here if waiting a little would help
3179 // (only for shared locks this would avoid an error)
3180 return setError(VBOX_E_INVALID_OBJECT_STATE,
3181 tr("The machine '%s' already has a lock request pending"),
3182 mUserData->s.strName.c_str());
3183 }
3184
3185 // this machine is awaiting for a spawning session to be opened:
3186 // then the calling process must be the one that got started by
3187 // LaunchVMProcess()
3188
3189 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3190 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3191
3192#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3193 /* Hardened windows builds spawns three processes when a VM is
3194 launched, the 3rd one is the one that will end up here. */
3195 RTPROCESS ppid;
3196 int rc = RTProcQueryParent(pid, &ppid);
3197 if (RT_SUCCESS(rc))
3198 rc = RTProcQueryParent(ppid, &ppid);
3199 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3200 || rc == VERR_ACCESS_DENIED)
3201 {
3202 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3203 mData->mSession.mPID = pid;
3204 }
3205#endif
3206
3207 if (mData->mSession.mPID != pid)
3208 return setError(E_ACCESSDENIED,
3209 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3210 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3211 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3212 }
3213
3214 // create the mutable SessionMachine from the current machine
3215 ComObjPtr<SessionMachine> sessionMachine;
3216 sessionMachine.createObject();
3217 rc = sessionMachine->init(this);
3218 AssertComRC(rc);
3219
3220 /* NOTE: doing return from this function after this point but
3221 * before the end is forbidden since it may call SessionMachine::uninit()
3222 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3223 * lock while still holding the Machine lock in alock so that a deadlock
3224 * is possible due to the wrong lock order. */
3225
3226 if (SUCCEEDED(rc))
3227 {
3228 /*
3229 * Set the session state to Spawning to protect against subsequent
3230 * attempts to open a session and to unregister the machine after
3231 * we release the lock.
3232 */
3233 SessionState_T origState = mData->mSession.mState;
3234 mData->mSession.mState = SessionState_Spawning;
3235
3236#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3237 /* Get the client token ID to be passed to the client process */
3238 Utf8Str strTokenId;
3239 sessionMachine->i_getTokenId(strTokenId);
3240 Assert(!strTokenId.isEmpty());
3241#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3242 /* Get the client token to be passed to the client process */
3243 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3244 /* The token is now "owned" by pToken, fix refcount */
3245 if (!pToken.isNull())
3246 pToken->Release();
3247#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3248
3249 /*
3250 * Release the lock before calling the client process -- it will call
3251 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3252 * because the state is Spawning, so that LaunchVMProcess() and
3253 * LockMachine() calls will fail. This method, called before we
3254 * acquire the lock again, will fail because of the wrong PID.
3255 *
3256 * Note that mData->mSession.mRemoteControls accessed outside
3257 * the lock may not be modified when state is Spawning, so it's safe.
3258 */
3259 alock.release();
3260
3261 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3262#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3263 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3264#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3265 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3266 /* Now the token is owned by the client process. */
3267 pToken.setNull();
3268#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3269 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3270
3271 /* The failure may occur w/o any error info (from RPC), so provide one */
3272 if (FAILED(rc))
3273 setError(VBOX_E_VM_ERROR,
3274 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3275
3276 // get session name, either to remember or to compare against
3277 // the already known session name.
3278 {
3279 Bstr bstrSessionName;
3280 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3281 if (SUCCEEDED(rc2))
3282 strSessionName = bstrSessionName;
3283 }
3284
3285 if ( SUCCEEDED(rc)
3286 && fLaunchingVMProcess
3287 )
3288 {
3289 /* complete the remote session initialization */
3290
3291 /* get the console from the direct session */
3292 ComPtr<IConsole> console;
3293 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3294 ComAssertComRC(rc);
3295
3296 if (SUCCEEDED(rc) && !console)
3297 {
3298 ComAssert(!!console);
3299 rc = E_FAIL;
3300 }
3301
3302 /* assign machine & console to the remote session */
3303 if (SUCCEEDED(rc))
3304 {
3305 /*
3306 * after LaunchVMProcess(), the first and the only
3307 * entry in remoteControls is that remote session
3308 */
3309 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3310 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3311 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3312
3313 /* The failure may occur w/o any error info (from RPC), so provide one */
3314 if (FAILED(rc))
3315 setError(VBOX_E_VM_ERROR,
3316 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3317 }
3318
3319 if (FAILED(rc))
3320 pSessionControl->Uninitialize();
3321 }
3322
3323 /* acquire the lock again */
3324 alock.acquire();
3325
3326 /* Restore the session state */
3327 mData->mSession.mState = origState;
3328 }
3329
3330 // finalize spawning anyway (this is why we don't return on errors above)
3331 if (fLaunchingVMProcess)
3332 {
3333 Assert(mData->mSession.mName == strSessionName);
3334 /* Note that the progress object is finalized later */
3335 /** @todo Consider checking mData->mSession.mProgress for cancellation
3336 * around here. */
3337
3338 /* We don't reset mSession.mPID here because it is necessary for
3339 * SessionMachine::uninit() to reap the child process later. */
3340
3341 if (FAILED(rc))
3342 {
3343 /* Close the remote session, remove the remote control from the list
3344 * and reset session state to Closed (@note keep the code in sync
3345 * with the relevant part in checkForSpawnFailure()). */
3346
3347 Assert(mData->mSession.mRemoteControls.size() == 1);
3348 if (mData->mSession.mRemoteControls.size() == 1)
3349 {
3350 ErrorInfoKeeper eik;
3351 mData->mSession.mRemoteControls.front()->Uninitialize();
3352 }
3353
3354 mData->mSession.mRemoteControls.clear();
3355 mData->mSession.mState = SessionState_Unlocked;
3356 }
3357 }
3358 else
3359 {
3360 /* memorize PID of the directly opened session */
3361 if (SUCCEEDED(rc))
3362 mData->mSession.mPID = pid;
3363 }
3364
3365 if (SUCCEEDED(rc))
3366 {
3367 mData->mSession.mLockType = aLockType;
3368 /* memorize the direct session control and cache IUnknown for it */
3369 mData->mSession.mDirectControl = pSessionControl;
3370 mData->mSession.mState = SessionState_Locked;
3371 if (!fLaunchingVMProcess)
3372 mData->mSession.mName = strSessionName;
3373 /* associate the SessionMachine with this Machine */
3374 mData->mSession.mMachine = sessionMachine;
3375
3376 /* request an IUnknown pointer early from the remote party for later
3377 * identity checks (it will be internally cached within mDirectControl
3378 * at least on XPCOM) */
3379 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3380 NOREF(unk);
3381 }
3382
3383 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3384 * would break the lock order */
3385 alock.release();
3386
3387 /* uninitialize the created session machine on failure */
3388 if (FAILED(rc))
3389 sessionMachine->uninit();
3390 }
3391
3392 if (SUCCEEDED(rc))
3393 {
3394 /*
3395 * tell the client watcher thread to update the set of
3396 * machines that have open sessions
3397 */
3398 mParent->i_updateClientWatcher();
3399
3400 if (oldState != SessionState_Locked)
3401 /* fire an event */
3402 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3403 }
3404
3405 return rc;
3406}
3407
3408/**
3409 * @note Locks objects!
3410 */
3411HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3412 const com::Utf8Str &aName,
3413 const com::Utf8Str &aEnvironment,
3414 ComPtr<IProgress> &aProgress)
3415{
3416 Utf8Str strFrontend(aName);
3417 /* "emergencystop" doesn't need the session, so skip the checks/interface
3418 * retrieval. This code doesn't quite fit in here, but introducing a
3419 * special API method would be even more effort, and would require explicit
3420 * support by every API client. It's better to hide the feature a bit. */
3421 if (strFrontend != "emergencystop")
3422 CheckComArgNotNull(aSession);
3423
3424 HRESULT rc = S_OK;
3425 if (strFrontend.isEmpty())
3426 {
3427 Bstr bstrFrontend;
3428 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3429 if (FAILED(rc))
3430 return rc;
3431 strFrontend = bstrFrontend;
3432 if (strFrontend.isEmpty())
3433 {
3434 ComPtr<ISystemProperties> systemProperties;
3435 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3436 if (FAILED(rc))
3437 return rc;
3438 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3439 if (FAILED(rc))
3440 return rc;
3441 strFrontend = bstrFrontend;
3442 }
3443 /* paranoia - emergencystop is not a valid default */
3444 if (strFrontend == "emergencystop")
3445 strFrontend = Utf8Str::Empty;
3446 }
3447 /* default frontend: Qt GUI */
3448 if (strFrontend.isEmpty())
3449 strFrontend = "GUI/Qt";
3450
3451 if (strFrontend != "emergencystop")
3452 {
3453 /* check the session state */
3454 SessionState_T state;
3455 rc = aSession->COMGETTER(State)(&state);
3456 if (FAILED(rc))
3457 return rc;
3458
3459 if (state != SessionState_Unlocked)
3460 return setError(VBOX_E_INVALID_OBJECT_STATE,
3461 tr("The given session is busy"));
3462
3463 /* get the IInternalSessionControl interface */
3464 ComPtr<IInternalSessionControl> control(aSession);
3465 ComAssertMsgRet(!control.isNull(),
3466 ("No IInternalSessionControl interface"),
3467 E_INVALIDARG);
3468
3469 /* get the teleporter enable state for the progress object init. */
3470 BOOL fTeleporterEnabled;
3471 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3472 if (FAILED(rc))
3473 return rc;
3474
3475 /* create a progress object */
3476 ComObjPtr<ProgressProxy> progress;
3477 progress.createObject();
3478 rc = progress->init(mParent,
3479 static_cast<IMachine*>(this),
3480 Bstr(tr("Starting VM")).raw(),
3481 TRUE /* aCancelable */,
3482 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3483 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3484 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3485 2 /* uFirstOperationWeight */,
3486 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3487
3488 if (SUCCEEDED(rc))
3489 {
3490 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3491 if (SUCCEEDED(rc))
3492 {
3493 aProgress = progress;
3494
3495 /* signal the client watcher thread */
3496 mParent->i_updateClientWatcher();
3497
3498 /* fire an event */
3499 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3500 }
3501 }
3502 }
3503 else
3504 {
3505 /* no progress object - either instant success or failure */
3506 aProgress = NULL;
3507
3508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3509
3510 if (mData->mSession.mState != SessionState_Locked)
3511 return setError(VBOX_E_INVALID_OBJECT_STATE,
3512 tr("The machine '%s' is not locked by a session"),
3513 mUserData->s.strName.c_str());
3514
3515 /* must have a VM process associated - do not kill normal API clients
3516 * with an open session */
3517 if (!Global::IsOnline(mData->mMachineState))
3518 return setError(VBOX_E_INVALID_OBJECT_STATE,
3519 tr("The machine '%s' does not have a VM process"),
3520 mUserData->s.strName.c_str());
3521
3522 /* forcibly terminate the VM process */
3523 if (mData->mSession.mPID != NIL_RTPROCESS)
3524 RTProcTerminate(mData->mSession.mPID);
3525
3526 /* signal the client watcher thread, as most likely the client has
3527 * been terminated */
3528 mParent->i_updateClientWatcher();
3529 }
3530
3531 return rc;
3532}
3533
3534HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3535{
3536 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3537 return setError(E_INVALIDARG,
3538 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3539 aPosition, SchemaDefs::MaxBootPosition);
3540
3541 if (aDevice == DeviceType_USB)
3542 return setError(E_NOTIMPL,
3543 tr("Booting from USB device is currently not supported"));
3544
3545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3546
3547 HRESULT rc = i_checkStateDependency(MutableStateDep);
3548 if (FAILED(rc)) return rc;
3549
3550 i_setModified(IsModified_MachineData);
3551 mHWData.backup();
3552 mHWData->mBootOrder[aPosition - 1] = aDevice;
3553
3554 return S_OK;
3555}
3556
3557HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3558{
3559 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3560 return setError(E_INVALIDARG,
3561 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3562 aPosition, SchemaDefs::MaxBootPosition);
3563
3564 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3565
3566 *aDevice = mHWData->mBootOrder[aPosition - 1];
3567
3568 return S_OK;
3569}
3570
3571HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3572 LONG aControllerPort,
3573 LONG aDevice,
3574 DeviceType_T aType,
3575 const ComPtr<IMedium> &aMedium)
3576{
3577 IMedium *aM = aMedium;
3578 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3579 aName.c_str(), aControllerPort, aDevice, aType, aM));
3580
3581 // request the host lock first, since might be calling Host methods for getting host drives;
3582 // next, protect the media tree all the while we're in here, as well as our member variables
3583 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3584 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3585
3586 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3587 if (FAILED(rc)) return rc;
3588
3589 /// @todo NEWMEDIA implicit machine registration
3590 if (!mData->mRegistered)
3591 return setError(VBOX_E_INVALID_OBJECT_STATE,
3592 tr("Cannot attach storage devices to an unregistered machine"));
3593
3594 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3595
3596 /* Check for an existing controller. */
3597 ComObjPtr<StorageController> ctl;
3598 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3599 if (FAILED(rc)) return rc;
3600
3601 StorageControllerType_T ctrlType;
3602 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3603 if (FAILED(rc))
3604 return setError(E_FAIL,
3605 tr("Could not get type of controller '%s'"),
3606 aName.c_str());
3607
3608 bool fSilent = false;
3609 Utf8Str strReconfig;
3610
3611 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3612 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3613 if ( mData->mMachineState == MachineState_Paused
3614 && strReconfig == "1")
3615 fSilent = true;
3616
3617 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3618 bool fHotplug = false;
3619 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3620 fHotplug = true;
3621
3622 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3623 return setError(VBOX_E_INVALID_VM_STATE,
3624 tr("Controller '%s' does not support hotplugging"),
3625 aName.c_str());
3626
3627 // check that the port and device are not out of range
3628 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3629 if (FAILED(rc)) return rc;
3630
3631 /* check if the device slot is already busy */
3632 MediumAttachment *pAttachTemp;
3633 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3634 aName,
3635 aControllerPort,
3636 aDevice)))
3637 {
3638 Medium *pMedium = pAttachTemp->i_getMedium();
3639 if (pMedium)
3640 {
3641 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3642 return setError(VBOX_E_OBJECT_IN_USE,
3643 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3644 pMedium->i_getLocationFull().c_str(),
3645 aControllerPort,
3646 aDevice,
3647 aName.c_str());
3648 }
3649 else
3650 return setError(VBOX_E_OBJECT_IN_USE,
3651 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3652 aControllerPort, aDevice, aName.c_str());
3653 }
3654
3655 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3656 if (aMedium && medium.isNull())
3657 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3658
3659 AutoCaller mediumCaller(medium);
3660 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3661
3662 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3663
3664 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3665 && !medium.isNull()
3666 )
3667 return setError(VBOX_E_OBJECT_IN_USE,
3668 tr("Medium '%s' is already attached to this virtual machine"),
3669 medium->i_getLocationFull().c_str());
3670
3671 if (!medium.isNull())
3672 {
3673 MediumType_T mtype = medium->i_getType();
3674 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3675 // For DVDs it's not written to the config file, so needs no global config
3676 // version bump. For floppies it's a new attribute "type", which is ignored
3677 // by older VirtualBox version, so needs no global config version bump either.
3678 // For hard disks this type is not accepted.
3679 if (mtype == MediumType_MultiAttach)
3680 {
3681 // This type is new with VirtualBox 4.0 and therefore requires settings
3682 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3683 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3684 // two reasons: The medium type is a property of the media registry tree, which
3685 // can reside in the global config file (for pre-4.0 media); we would therefore
3686 // possibly need to bump the global config version. We don't want to do that though
3687 // because that might make downgrading to pre-4.0 impossible.
3688 // As a result, we can only use these two new types if the medium is NOT in the
3689 // global registry:
3690 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3691 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3692 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3693 )
3694 return setError(VBOX_E_INVALID_OBJECT_STATE,
3695 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3696 "to machines that were created with VirtualBox 4.0 or later"),
3697 medium->i_getLocationFull().c_str());
3698 }
3699 }
3700
3701 bool fIndirect = false;
3702 if (!medium.isNull())
3703 fIndirect = medium->i_isReadOnly();
3704 bool associate = true;
3705
3706 do
3707 {
3708 if ( aType == DeviceType_HardDisk
3709 && mMediumAttachments.isBackedUp())
3710 {
3711 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3712
3713 /* check if the medium was attached to the VM before we started
3714 * changing attachments in which case the attachment just needs to
3715 * be restored */
3716 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3717 {
3718 AssertReturn(!fIndirect, E_FAIL);
3719
3720 /* see if it's the same bus/channel/device */
3721 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3722 {
3723 /* the simplest case: restore the whole attachment
3724 * and return, nothing else to do */
3725 mMediumAttachments->push_back(pAttachTemp);
3726
3727 /* Reattach the medium to the VM. */
3728 if (fHotplug || fSilent)
3729 {
3730 mediumLock.release();
3731 treeLock.release();
3732 alock.release();
3733
3734 MediumLockList *pMediumLockList(new MediumLockList());
3735
3736 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3737 medium /* pToLockWrite */,
3738 false /* fMediumLockWriteAll */,
3739 NULL,
3740 *pMediumLockList);
3741 alock.acquire();
3742 if (FAILED(rc))
3743 delete pMediumLockList;
3744 else
3745 {
3746 mData->mSession.mLockedMedia.Unlock();
3747 alock.release();
3748 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3749 mData->mSession.mLockedMedia.Lock();
3750 alock.acquire();
3751 }
3752 alock.release();
3753
3754 if (SUCCEEDED(rc))
3755 {
3756 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3757 /* Remove lock list in case of error. */
3758 if (FAILED(rc))
3759 {
3760 mData->mSession.mLockedMedia.Unlock();
3761 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3762 mData->mSession.mLockedMedia.Lock();
3763 }
3764 }
3765 }
3766
3767 return S_OK;
3768 }
3769
3770 /* bus/channel/device differ; we need a new attachment object,
3771 * but don't try to associate it again */
3772 associate = false;
3773 break;
3774 }
3775 }
3776
3777 /* go further only if the attachment is to be indirect */
3778 if (!fIndirect)
3779 break;
3780
3781 /* perform the so called smart attachment logic for indirect
3782 * attachments. Note that smart attachment is only applicable to base
3783 * hard disks. */
3784
3785 if (medium->i_getParent().isNull())
3786 {
3787 /* first, investigate the backup copy of the current hard disk
3788 * attachments to make it possible to re-attach existing diffs to
3789 * another device slot w/o losing their contents */
3790 if (mMediumAttachments.isBackedUp())
3791 {
3792 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3793
3794 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3795 uint32_t foundLevel = 0;
3796
3797 for (MediumAttachmentList::const_iterator
3798 it = oldAtts.begin();
3799 it != oldAtts.end();
3800 ++it)
3801 {
3802 uint32_t level = 0;
3803 MediumAttachment *pAttach = *it;
3804 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3805 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3806 if (pMedium.isNull())
3807 continue;
3808
3809 if (pMedium->i_getBase(&level) == medium)
3810 {
3811 /* skip the hard disk if its currently attached (we
3812 * cannot attach the same hard disk twice) */
3813 if (i_findAttachment(*mMediumAttachments.data(),
3814 pMedium))
3815 continue;
3816
3817 /* matched device, channel and bus (i.e. attached to the
3818 * same place) will win and immediately stop the search;
3819 * otherwise the attachment that has the youngest
3820 * descendant of medium will be used
3821 */
3822 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3823 {
3824 /* the simplest case: restore the whole attachment
3825 * and return, nothing else to do */
3826 mMediumAttachments->push_back(*it);
3827
3828 /* Reattach the medium to the VM. */
3829 if (fHotplug || fSilent)
3830 {
3831 mediumLock.release();
3832 treeLock.release();
3833 alock.release();
3834
3835 MediumLockList *pMediumLockList(new MediumLockList());
3836
3837 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3838 medium /* pToLockWrite */,
3839 false /* fMediumLockWriteAll */,
3840 NULL,
3841 *pMediumLockList);
3842 alock.acquire();
3843 if (FAILED(rc))
3844 delete pMediumLockList;
3845 else
3846 {
3847 mData->mSession.mLockedMedia.Unlock();
3848 alock.release();
3849 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3850 mData->mSession.mLockedMedia.Lock();
3851 alock.acquire();
3852 }
3853 alock.release();
3854
3855 if (SUCCEEDED(rc))
3856 {
3857 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3858 /* Remove lock list in case of error. */
3859 if (FAILED(rc))
3860 {
3861 mData->mSession.mLockedMedia.Unlock();
3862 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3863 mData->mSession.mLockedMedia.Lock();
3864 }
3865 }
3866 }
3867
3868 return S_OK;
3869 }
3870 else if ( foundIt == oldAtts.end()
3871 || level > foundLevel /* prefer younger */
3872 )
3873 {
3874 foundIt = it;
3875 foundLevel = level;
3876 }
3877 }
3878 }
3879
3880 if (foundIt != oldAtts.end())
3881 {
3882 /* use the previously attached hard disk */
3883 medium = (*foundIt)->i_getMedium();
3884 mediumCaller.attach(medium);
3885 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3886 mediumLock.attach(medium);
3887 /* not implicit, doesn't require association with this VM */
3888 fIndirect = false;
3889 associate = false;
3890 /* go right to the MediumAttachment creation */
3891 break;
3892 }
3893 }
3894
3895 /* must give up the medium lock and medium tree lock as below we
3896 * go over snapshots, which needs a lock with higher lock order. */
3897 mediumLock.release();
3898 treeLock.release();
3899
3900 /* then, search through snapshots for the best diff in the given
3901 * hard disk's chain to base the new diff on */
3902
3903 ComObjPtr<Medium> base;
3904 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3905 while (snap)
3906 {
3907 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3908
3909 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3910
3911 MediumAttachment *pAttachFound = NULL;
3912 uint32_t foundLevel = 0;
3913
3914 for (MediumAttachmentList::const_iterator
3915 it = snapAtts.begin();
3916 it != snapAtts.end();
3917 ++it)
3918 {
3919 MediumAttachment *pAttach = *it;
3920 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3921 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3922 if (pMedium.isNull())
3923 continue;
3924
3925 uint32_t level = 0;
3926 if (pMedium->i_getBase(&level) == medium)
3927 {
3928 /* matched device, channel and bus (i.e. attached to the
3929 * same place) will win and immediately stop the search;
3930 * otherwise the attachment that has the youngest
3931 * descendant of medium will be used
3932 */
3933 if ( pAttach->i_getDevice() == aDevice
3934 && pAttach->i_getPort() == aControllerPort
3935 && pAttach->i_getControllerName() == aName
3936 )
3937 {
3938 pAttachFound = pAttach;
3939 break;
3940 }
3941 else if ( !pAttachFound
3942 || level > foundLevel /* prefer younger */
3943 )
3944 {
3945 pAttachFound = pAttach;
3946 foundLevel = level;
3947 }
3948 }
3949 }
3950
3951 if (pAttachFound)
3952 {
3953 base = pAttachFound->i_getMedium();
3954 break;
3955 }
3956
3957 snap = snap->i_getParent();
3958 }
3959
3960 /* re-lock medium tree and the medium, as we need it below */
3961 treeLock.acquire();
3962 mediumLock.acquire();
3963
3964 /* found a suitable diff, use it as a base */
3965 if (!base.isNull())
3966 {
3967 medium = base;
3968 mediumCaller.attach(medium);
3969 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3970 mediumLock.attach(medium);
3971 }
3972 }
3973
3974 Utf8Str strFullSnapshotFolder;
3975 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3976
3977 ComObjPtr<Medium> diff;
3978 diff.createObject();
3979 // store this diff in the same registry as the parent
3980 Guid uuidRegistryParent;
3981 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3982 {
3983 // parent image has no registry: this can happen if we're attaching a new immutable
3984 // image that has not yet been attached (medium then points to the base and we're
3985 // creating the diff image for the immutable, and the parent is not yet registered);
3986 // put the parent in the machine registry then
3987 mediumLock.release();
3988 treeLock.release();
3989 alock.release();
3990 i_addMediumToRegistry(medium);
3991 alock.acquire();
3992 treeLock.acquire();
3993 mediumLock.acquire();
3994 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3995 }
3996 rc = diff->init(mParent,
3997 medium->i_getPreferredDiffFormat(),
3998 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3999 uuidRegistryParent,
4000 DeviceType_HardDisk);
4001 if (FAILED(rc)) return rc;
4002
4003 /* Apply the normal locking logic to the entire chain. */
4004 MediumLockList *pMediumLockList(new MediumLockList());
4005 mediumLock.release();
4006 treeLock.release();
4007 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4008 diff /* pToLockWrite */,
4009 false /* fMediumLockWriteAll */,
4010 medium,
4011 *pMediumLockList);
4012 treeLock.acquire();
4013 mediumLock.acquire();
4014 if (SUCCEEDED(rc))
4015 {
4016 mediumLock.release();
4017 treeLock.release();
4018 rc = pMediumLockList->Lock();
4019 treeLock.acquire();
4020 mediumLock.acquire();
4021 if (FAILED(rc))
4022 setError(rc,
4023 tr("Could not lock medium when creating diff '%s'"),
4024 diff->i_getLocationFull().c_str());
4025 else
4026 {
4027 /* will release the lock before the potentially lengthy
4028 * operation, so protect with the special state */
4029 MachineState_T oldState = mData->mMachineState;
4030 i_setMachineState(MachineState_SettingUp);
4031
4032 mediumLock.release();
4033 treeLock.release();
4034 alock.release();
4035
4036 rc = medium->i_createDiffStorage(diff,
4037 medium->i_getPreferredDiffVariant(),
4038 pMediumLockList,
4039 NULL /* aProgress */,
4040 true /* aWait */,
4041 false /* aNotify */);
4042
4043 alock.acquire();
4044 treeLock.acquire();
4045 mediumLock.acquire();
4046
4047 i_setMachineState(oldState);
4048 }
4049 }
4050
4051 /* Unlock the media and free the associated memory. */
4052 delete pMediumLockList;
4053
4054 if (FAILED(rc)) return rc;
4055
4056 /* use the created diff for the actual attachment */
4057 medium = diff;
4058 mediumCaller.attach(medium);
4059 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4060 mediumLock.attach(medium);
4061 }
4062 while (0);
4063
4064 ComObjPtr<MediumAttachment> attachment;
4065 attachment.createObject();
4066 rc = attachment->init(this,
4067 medium,
4068 aName,
4069 aControllerPort,
4070 aDevice,
4071 aType,
4072 fIndirect,
4073 false /* fPassthrough */,
4074 false /* fTempEject */,
4075 false /* fNonRotational */,
4076 false /* fDiscard */,
4077 fHotplug /* fHotPluggable */,
4078 Utf8Str::Empty);
4079 if (FAILED(rc)) return rc;
4080
4081 if (associate && !medium.isNull())
4082 {
4083 // as the last step, associate the medium to the VM
4084 rc = medium->i_addBackReference(mData->mUuid);
4085 // here we can fail because of Deleting, or being in process of creating a Diff
4086 if (FAILED(rc)) return rc;
4087
4088 mediumLock.release();
4089 treeLock.release();
4090 alock.release();
4091 i_addMediumToRegistry(medium);
4092 alock.acquire();
4093 treeLock.acquire();
4094 mediumLock.acquire();
4095 }
4096
4097 /* success: finally remember the attachment */
4098 i_setModified(IsModified_Storage);
4099 mMediumAttachments.backup();
4100 mMediumAttachments->push_back(attachment);
4101
4102 mediumLock.release();
4103 treeLock.release();
4104 alock.release();
4105
4106 if (fHotplug || fSilent)
4107 {
4108 if (!medium.isNull())
4109 {
4110 MediumLockList *pMediumLockList(new MediumLockList());
4111
4112 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4113 medium /* pToLockWrite */,
4114 false /* fMediumLockWriteAll */,
4115 NULL,
4116 *pMediumLockList);
4117 alock.acquire();
4118 if (FAILED(rc))
4119 delete pMediumLockList;
4120 else
4121 {
4122 mData->mSession.mLockedMedia.Unlock();
4123 alock.release();
4124 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4125 mData->mSession.mLockedMedia.Lock();
4126 alock.acquire();
4127 }
4128 alock.release();
4129 }
4130
4131 if (SUCCEEDED(rc))
4132 {
4133 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4134 /* Remove lock list in case of error. */
4135 if (FAILED(rc))
4136 {
4137 mData->mSession.mLockedMedia.Unlock();
4138 mData->mSession.mLockedMedia.Remove(attachment);
4139 mData->mSession.mLockedMedia.Lock();
4140 }
4141 }
4142 }
4143
4144 /* Save modified registries, but skip this machine as it's the caller's
4145 * job to save its settings like all other settings changes. */
4146 mParent->i_unmarkRegistryModified(i_getId());
4147 mParent->i_saveModifiedRegistries();
4148
4149 if (aM)
4150 mParent->i_onMediumConfigChanged(aM);
4151 return rc;
4152}
4153
4154HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4155 LONG aDevice)
4156{
4157 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4158 aName.c_str(), aControllerPort, aDevice));
4159
4160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4161
4162 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4163 if (FAILED(rc)) return rc;
4164
4165 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4166
4167 /* Check for an existing controller. */
4168 ComObjPtr<StorageController> ctl;
4169 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4170 if (FAILED(rc)) return rc;
4171
4172 StorageControllerType_T ctrlType;
4173 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4174 if (FAILED(rc))
4175 return setError(E_FAIL,
4176 tr("Could not get type of controller '%s'"),
4177 aName.c_str());
4178
4179 bool fSilent = false;
4180 Utf8Str strReconfig;
4181
4182 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4183 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4184 if ( mData->mMachineState == MachineState_Paused
4185 && strReconfig == "1")
4186 fSilent = true;
4187
4188 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4189 bool fHotplug = false;
4190 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4191 fHotplug = true;
4192
4193 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4194 return setError(VBOX_E_INVALID_VM_STATE,
4195 tr("Controller '%s' does not support hotplugging"),
4196 aName.c_str());
4197
4198 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4199 aName,
4200 aControllerPort,
4201 aDevice);
4202 if (!pAttach)
4203 return setError(VBOX_E_OBJECT_NOT_FOUND,
4204 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4205 aDevice, aControllerPort, aName.c_str());
4206
4207 if (fHotplug && !pAttach->i_getHotPluggable())
4208 return setError(VBOX_E_NOT_SUPPORTED,
4209 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4210 aDevice, aControllerPort, aName.c_str());
4211
4212 /*
4213 * The VM has to detach the device before we delete any implicit diffs.
4214 * If this fails we can roll back without loosing data.
4215 */
4216 if (fHotplug || fSilent)
4217 {
4218 alock.release();
4219 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4220 alock.acquire();
4221 }
4222 if (FAILED(rc)) return rc;
4223
4224 /* If we are here everything went well and we can delete the implicit now. */
4225 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4226
4227 alock.release();
4228
4229 /* Save modified registries, but skip this machine as it's the caller's
4230 * job to save its settings like all other settings changes. */
4231 mParent->i_unmarkRegistryModified(i_getId());
4232 mParent->i_saveModifiedRegistries();
4233
4234 return rc;
4235}
4236
4237HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4238 LONG aDevice, BOOL aPassthrough)
4239{
4240 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4241 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4242
4243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4244
4245 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4246 if (FAILED(rc)) return rc;
4247
4248 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4249
4250 /* Check for an existing controller. */
4251 ComObjPtr<StorageController> ctl;
4252 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4253 if (FAILED(rc)) return rc;
4254
4255 StorageControllerType_T ctrlType;
4256 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4257 if (FAILED(rc))
4258 return setError(E_FAIL,
4259 tr("Could not get type of controller '%s'"),
4260 aName.c_str());
4261
4262 bool fSilent = false;
4263 Utf8Str strReconfig;
4264
4265 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4266 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4267 if ( mData->mMachineState == MachineState_Paused
4268 && strReconfig == "1")
4269 fSilent = true;
4270
4271 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4272 bool fHotplug = false;
4273 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4274 fHotplug = true;
4275
4276 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4277 return setError(VBOX_E_INVALID_VM_STATE,
4278 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4279 aName.c_str());
4280
4281 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4282 aName,
4283 aControllerPort,
4284 aDevice);
4285 if (!pAttach)
4286 return setError(VBOX_E_OBJECT_NOT_FOUND,
4287 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4288 aDevice, aControllerPort, aName.c_str());
4289
4290
4291 i_setModified(IsModified_Storage);
4292 mMediumAttachments.backup();
4293
4294 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4295
4296 if (pAttach->i_getType() != DeviceType_DVD)
4297 return setError(E_INVALIDARG,
4298 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4299 aDevice, aControllerPort, aName.c_str());
4300 pAttach->i_updatePassthrough(!!aPassthrough);
4301
4302 attLock.release();
4303 alock.release();
4304 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4305
4306 return rc;
4307}
4308
4309HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4310 LONG aDevice, BOOL aTemporaryEject)
4311{
4312
4313 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4314 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4315
4316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4317
4318 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4319 if (FAILED(rc)) return rc;
4320
4321 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4322 aName,
4323 aControllerPort,
4324 aDevice);
4325 if (!pAttach)
4326 return setError(VBOX_E_OBJECT_NOT_FOUND,
4327 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4328 aDevice, aControllerPort, aName.c_str());
4329
4330
4331 i_setModified(IsModified_Storage);
4332 mMediumAttachments.backup();
4333
4334 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4335
4336 if (pAttach->i_getType() != DeviceType_DVD)
4337 return setError(E_INVALIDARG,
4338 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4339 aDevice, aControllerPort, aName.c_str());
4340 pAttach->i_updateTempEject(!!aTemporaryEject);
4341
4342 return S_OK;
4343}
4344
4345HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4346 LONG aDevice, BOOL aNonRotational)
4347{
4348
4349 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4350 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4351
4352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4353
4354 HRESULT rc = i_checkStateDependency(MutableStateDep);
4355 if (FAILED(rc)) return rc;
4356
4357 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4358
4359 if (Global::IsOnlineOrTransient(mData->mMachineState))
4360 return setError(VBOX_E_INVALID_VM_STATE,
4361 tr("Invalid machine state: %s"),
4362 Global::stringifyMachineState(mData->mMachineState));
4363
4364 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4365 aName,
4366 aControllerPort,
4367 aDevice);
4368 if (!pAttach)
4369 return setError(VBOX_E_OBJECT_NOT_FOUND,
4370 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4371 aDevice, aControllerPort, aName.c_str());
4372
4373
4374 i_setModified(IsModified_Storage);
4375 mMediumAttachments.backup();
4376
4377 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4378
4379 if (pAttach->i_getType() != DeviceType_HardDisk)
4380 return setError(E_INVALIDARG,
4381 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"),
4382 aDevice, aControllerPort, aName.c_str());
4383 pAttach->i_updateNonRotational(!!aNonRotational);
4384
4385 return S_OK;
4386}
4387
4388HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4389 LONG aDevice, BOOL aDiscard)
4390{
4391
4392 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4393 aName.c_str(), aControllerPort, aDevice, aDiscard));
4394
4395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4396
4397 HRESULT rc = i_checkStateDependency(MutableStateDep);
4398 if (FAILED(rc)) return rc;
4399
4400 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4401
4402 if (Global::IsOnlineOrTransient(mData->mMachineState))
4403 return setError(VBOX_E_INVALID_VM_STATE,
4404 tr("Invalid machine state: %s"),
4405 Global::stringifyMachineState(mData->mMachineState));
4406
4407 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4408 aName,
4409 aControllerPort,
4410 aDevice);
4411 if (!pAttach)
4412 return setError(VBOX_E_OBJECT_NOT_FOUND,
4413 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4414 aDevice, aControllerPort, aName.c_str());
4415
4416
4417 i_setModified(IsModified_Storage);
4418 mMediumAttachments.backup();
4419
4420 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4421
4422 if (pAttach->i_getType() != DeviceType_HardDisk)
4423 return setError(E_INVALIDARG,
4424 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"),
4425 aDevice, aControllerPort, aName.c_str());
4426 pAttach->i_updateDiscard(!!aDiscard);
4427
4428 return S_OK;
4429}
4430
4431HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4432 LONG aDevice, BOOL aHotPluggable)
4433{
4434 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4435 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4436
4437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4438
4439 HRESULT rc = i_checkStateDependency(MutableStateDep);
4440 if (FAILED(rc)) return rc;
4441
4442 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4443
4444 if (Global::IsOnlineOrTransient(mData->mMachineState))
4445 return setError(VBOX_E_INVALID_VM_STATE,
4446 tr("Invalid machine state: %s"),
4447 Global::stringifyMachineState(mData->mMachineState));
4448
4449 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4450 aName,
4451 aControllerPort,
4452 aDevice);
4453 if (!pAttach)
4454 return setError(VBOX_E_OBJECT_NOT_FOUND,
4455 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4456 aDevice, aControllerPort, aName.c_str());
4457
4458 /* Check for an existing controller. */
4459 ComObjPtr<StorageController> ctl;
4460 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4461 if (FAILED(rc)) return rc;
4462
4463 StorageControllerType_T ctrlType;
4464 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4465 if (FAILED(rc))
4466 return setError(E_FAIL,
4467 tr("Could not get type of controller '%s'"),
4468 aName.c_str());
4469
4470 if (!i_isControllerHotplugCapable(ctrlType))
4471 return setError(VBOX_E_NOT_SUPPORTED,
4472 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4473 aName.c_str());
4474
4475 i_setModified(IsModified_Storage);
4476 mMediumAttachments.backup();
4477
4478 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4479
4480 if (pAttach->i_getType() == DeviceType_Floppy)
4481 return setError(E_INVALIDARG,
4482 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"),
4483 aDevice, aControllerPort, aName.c_str());
4484 pAttach->i_updateHotPluggable(!!aHotPluggable);
4485
4486 return S_OK;
4487}
4488
4489HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4490 LONG aDevice)
4491{
4492 int rc = S_OK;
4493 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4494 aName.c_str(), aControllerPort, aDevice));
4495
4496 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4497
4498 return rc;
4499}
4500
4501HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4502 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4503{
4504 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4505 aName.c_str(), aControllerPort, aDevice));
4506
4507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4508
4509 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4510 if (FAILED(rc)) return rc;
4511
4512 if (Global::IsOnlineOrTransient(mData->mMachineState))
4513 return setError(VBOX_E_INVALID_VM_STATE,
4514 tr("Invalid machine state: %s"),
4515 Global::stringifyMachineState(mData->mMachineState));
4516
4517 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4518 aName,
4519 aControllerPort,
4520 aDevice);
4521 if (!pAttach)
4522 return setError(VBOX_E_OBJECT_NOT_FOUND,
4523 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4524 aDevice, aControllerPort, aName.c_str());
4525
4526
4527 i_setModified(IsModified_Storage);
4528 mMediumAttachments.backup();
4529
4530 IBandwidthGroup *iB = aBandwidthGroup;
4531 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4532 if (aBandwidthGroup && group.isNull())
4533 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4534
4535 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4536
4537 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4538 if (strBandwidthGroupOld.isNotEmpty())
4539 {
4540 /* Get the bandwidth group object and release it - this must not fail. */
4541 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4542 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4543 Assert(SUCCEEDED(rc));
4544
4545 pBandwidthGroupOld->i_release();
4546 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4547 }
4548
4549 if (!group.isNull())
4550 {
4551 group->i_reference();
4552 pAttach->i_updateBandwidthGroup(group->i_getName());
4553 }
4554
4555 return S_OK;
4556}
4557
4558HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4559 LONG aControllerPort,
4560 LONG aDevice,
4561 DeviceType_T aType)
4562{
4563 HRESULT rc = S_OK;
4564
4565 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4566 aName.c_str(), aControllerPort, aDevice, aType));
4567
4568 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4569
4570 return rc;
4571}
4572
4573
4574HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4575 LONG aControllerPort,
4576 LONG aDevice,
4577 BOOL aForce)
4578{
4579 int rc = S_OK;
4580 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4581 aName.c_str(), aControllerPort, aForce));
4582
4583 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4584
4585 return rc;
4586}
4587
4588HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4589 LONG aControllerPort,
4590 LONG aDevice,
4591 const ComPtr<IMedium> &aMedium,
4592 BOOL aForce)
4593{
4594 int rc = S_OK;
4595 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4596 aName.c_str(), aControllerPort, aDevice, aForce));
4597
4598 // request the host lock first, since might be calling Host methods for getting host drives;
4599 // next, protect the media tree all the while we're in here, as well as our member variables
4600 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4601 this->lockHandle(),
4602 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4603
4604 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4605 aName,
4606 aControllerPort,
4607 aDevice);
4608 if (pAttach.isNull())
4609 return setError(VBOX_E_OBJECT_NOT_FOUND,
4610 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4611 aDevice, aControllerPort, aName.c_str());
4612
4613 /* Remember previously mounted medium. The medium before taking the
4614 * backup is not necessarily the same thing. */
4615 ComObjPtr<Medium> oldmedium;
4616 oldmedium = pAttach->i_getMedium();
4617
4618 IMedium *iM = aMedium;
4619 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4620 if (aMedium && pMedium.isNull())
4621 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4622
4623 AutoCaller mediumCaller(pMedium);
4624 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4625
4626 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4627 if (pMedium)
4628 {
4629 DeviceType_T mediumType = pAttach->i_getType();
4630 switch (mediumType)
4631 {
4632 case DeviceType_DVD:
4633 case DeviceType_Floppy:
4634 break;
4635
4636 default:
4637 return setError(VBOX_E_INVALID_OBJECT_STATE,
4638 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4639 aControllerPort,
4640 aDevice,
4641 aName.c_str());
4642 }
4643 }
4644
4645 i_setModified(IsModified_Storage);
4646 mMediumAttachments.backup();
4647
4648 {
4649 // The backup operation makes the pAttach reference point to the
4650 // old settings. Re-get the correct reference.
4651 pAttach = i_findAttachment(*mMediumAttachments.data(),
4652 aName,
4653 aControllerPort,
4654 aDevice);
4655 if (!oldmedium.isNull())
4656 oldmedium->i_removeBackReference(mData->mUuid);
4657 if (!pMedium.isNull())
4658 {
4659 pMedium->i_addBackReference(mData->mUuid);
4660
4661 mediumLock.release();
4662 multiLock.release();
4663 i_addMediumToRegistry(pMedium);
4664 multiLock.acquire();
4665 mediumLock.acquire();
4666 }
4667
4668 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4669 pAttach->i_updateMedium(pMedium);
4670 }
4671
4672 i_setModified(IsModified_Storage);
4673
4674 mediumLock.release();
4675 multiLock.release();
4676 rc = i_onMediumChange(pAttach, aForce);
4677 multiLock.acquire();
4678 mediumLock.acquire();
4679
4680 /* On error roll back this change only. */
4681 if (FAILED(rc))
4682 {
4683 if (!pMedium.isNull())
4684 pMedium->i_removeBackReference(mData->mUuid);
4685 pAttach = i_findAttachment(*mMediumAttachments.data(),
4686 aName,
4687 aControllerPort,
4688 aDevice);
4689 /* If the attachment is gone in the meantime, bail out. */
4690 if (pAttach.isNull())
4691 return rc;
4692 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4693 if (!oldmedium.isNull())
4694 oldmedium->i_addBackReference(mData->mUuid);
4695 pAttach->i_updateMedium(oldmedium);
4696 }
4697
4698 mediumLock.release();
4699 multiLock.release();
4700
4701 /* Save modified registries, but skip this machine as it's the caller's
4702 * job to save its settings like all other settings changes. */
4703 mParent->i_unmarkRegistryModified(i_getId());
4704 mParent->i_saveModifiedRegistries();
4705
4706 return rc;
4707}
4708HRESULT Machine::getMedium(const com::Utf8Str &aName,
4709 LONG aControllerPort,
4710 LONG aDevice,
4711 ComPtr<IMedium> &aMedium)
4712{
4713 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4714 aName.c_str(), aControllerPort, aDevice));
4715
4716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4717
4718 aMedium = NULL;
4719
4720 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4721 aName,
4722 aControllerPort,
4723 aDevice);
4724 if (pAttach.isNull())
4725 return setError(VBOX_E_OBJECT_NOT_FOUND,
4726 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4727 aDevice, aControllerPort, aName.c_str());
4728
4729 aMedium = pAttach->i_getMedium();
4730
4731 return S_OK;
4732}
4733
4734HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4735{
4736
4737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4738
4739 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4740
4741 return S_OK;
4742}
4743
4744HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4745{
4746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4747
4748 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4749
4750 return S_OK;
4751}
4752
4753HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4754{
4755 /* Do not assert if slot is out of range, just return the advertised
4756 status. testdriver/vbox.py triggers this in logVmInfo. */
4757 if (aSlot >= mNetworkAdapters.size())
4758 return setError(E_INVALIDARG,
4759 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4760 aSlot, mNetworkAdapters.size());
4761
4762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4763
4764 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4765
4766 return S_OK;
4767}
4768
4769HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4770{
4771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4772
4773 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4774 size_t i = 0;
4775 for (settings::StringsMap::const_iterator
4776 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4777 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4778 ++it, ++i)
4779 aKeys[i] = it->first;
4780
4781 return S_OK;
4782}
4783
4784 /**
4785 * @note Locks this object for reading.
4786 */
4787HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4788 com::Utf8Str &aValue)
4789{
4790 /* start with nothing found */
4791 aValue = "";
4792
4793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4794
4795 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4796 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4797 // found:
4798 aValue = it->second; // source is a Utf8Str
4799
4800 /* return the result to caller (may be empty) */
4801 return S_OK;
4802}
4803
4804 /**
4805 * @note Locks mParent for writing + this object for writing.
4806 */
4807HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4808{
4809 /* Because non-ASCII characters in aKey have caused problems in the settings
4810 * they are rejected unless the key should be deleted. */
4811 if (!aValue.isEmpty())
4812 {
4813 for (size_t i = 0; i < aKey.length(); ++i)
4814 {
4815 char ch = aKey[i];
4816 if (!RTLocCIsPrint(ch))
4817 return E_INVALIDARG;
4818 }
4819 }
4820
4821 Utf8Str strOldValue; // empty
4822
4823 // locking note: we only hold the read lock briefly to look up the old value,
4824 // then release it and call the onExtraCanChange callbacks. There is a small
4825 // chance of a race insofar as the callback might be called twice if two callers
4826 // change the same key at the same time, but that's a much better solution
4827 // than the deadlock we had here before. The actual changing of the extradata
4828 // is then performed under the write lock and race-free.
4829
4830 // look up the old value first; if nothing has changed then we need not do anything
4831 {
4832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4833
4834 // For snapshots don't even think about allowing changes, extradata
4835 // is global for a machine, so there is nothing snapshot specific.
4836 if (i_isSnapshotMachine())
4837 return setError(VBOX_E_INVALID_VM_STATE,
4838 tr("Cannot set extradata for a snapshot"));
4839
4840 // check if the right IMachine instance is used
4841 if (mData->mRegistered && !i_isSessionMachine())
4842 return setError(VBOX_E_INVALID_VM_STATE,
4843 tr("Cannot set extradata for an immutable machine"));
4844
4845 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4846 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4847 strOldValue = it->second;
4848 }
4849
4850 bool fChanged;
4851 if ((fChanged = (strOldValue != aValue)))
4852 {
4853 // ask for permission from all listeners outside the locks;
4854 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4855 // lock to copy the list of callbacks to invoke
4856 Bstr error;
4857 Bstr bstrValue(aValue);
4858
4859 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4860 {
4861 const char *sep = error.isEmpty() ? "" : ": ";
4862 CBSTR err = error.raw();
4863 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4864 return setError(E_ACCESSDENIED,
4865 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4866 aKey.c_str(),
4867 aValue.c_str(),
4868 sep,
4869 err);
4870 }
4871
4872 // data is changing and change not vetoed: then write it out under the lock
4873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4874
4875 if (aValue.isEmpty())
4876 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4877 else
4878 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4879 // creates a new key if needed
4880
4881 bool fNeedsGlobalSaveSettings = false;
4882 // This saving of settings is tricky: there is no "old state" for the
4883 // extradata items at all (unlike all other settings), so the old/new
4884 // settings comparison would give a wrong result!
4885 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4886
4887 if (fNeedsGlobalSaveSettings)
4888 {
4889 // save the global settings; for that we should hold only the VirtualBox lock
4890 alock.release();
4891 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4892 mParent->i_saveSettings();
4893 }
4894 }
4895
4896 // fire notification outside the lock
4897 if (fChanged)
4898 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4899
4900 return S_OK;
4901}
4902
4903HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4904{
4905 aProgress = NULL;
4906 NOREF(aSettingsFilePath);
4907 ReturnComNotImplemented();
4908}
4909
4910HRESULT Machine::saveSettings()
4911{
4912 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4913
4914 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4915 if (FAILED(rc)) return rc;
4916
4917 /* the settings file path may never be null */
4918 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4919
4920 /* save all VM data excluding snapshots */
4921 bool fNeedsGlobalSaveSettings = false;
4922 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4923 mlock.release();
4924
4925 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4926 {
4927 // save the global settings; for that we should hold only the VirtualBox lock
4928 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4929 rc = mParent->i_saveSettings();
4930 }
4931
4932 return rc;
4933}
4934
4935
4936HRESULT Machine::discardSettings()
4937{
4938 /*
4939 * We need to take the machine list lock here as well as the machine one
4940 * or we'll get into trouble should any media stuff require rolling back.
4941 *
4942 * Details:
4943 *
4944 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4945 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4946 * 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]
4947 * 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
4948 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4949 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4950 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4951 * 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
4952 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4953 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4954 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4955 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4956 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4957 * 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]
4958 * 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] (*)
4959 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4960 * 0:005> k
4961 * # Child-SP RetAddr Call Site
4962 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4963 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4964 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4965 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4966 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4967 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4968 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4969 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4970 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4971 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4972 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4973 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4974 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4975 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4976 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4977 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4978 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4979 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4980 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4981 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4982 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4983 *
4984 */
4985 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4987
4988 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4989 if (FAILED(rc)) return rc;
4990
4991 /*
4992 * during this rollback, the session will be notified if data has
4993 * been actually changed
4994 */
4995 i_rollback(true /* aNotify */);
4996
4997 return S_OK;
4998}
4999
5000/** @note Locks objects! */
5001HRESULT Machine::unregister(AutoCaller &autoCaller,
5002 CleanupMode_T aCleanupMode,
5003 std::vector<ComPtr<IMedium> > &aMedia)
5004{
5005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5006
5007 Guid id(i_getId());
5008
5009 if (mData->mSession.mState != SessionState_Unlocked)
5010 return setError(VBOX_E_INVALID_OBJECT_STATE,
5011 tr("Cannot unregister the machine '%s' while it is locked"),
5012 mUserData->s.strName.c_str());
5013
5014 // wait for state dependents to drop to zero
5015 i_ensureNoStateDependencies();
5016
5017 if (!mData->mAccessible)
5018 {
5019 // inaccessible maschines can only be unregistered; uninitialize ourselves
5020 // here because currently there may be no unregistered that are inaccessible
5021 // (this state combination is not supported). Note releasing the caller and
5022 // leaving the lock before calling uninit()
5023 alock.release();
5024 autoCaller.release();
5025
5026 uninit();
5027
5028 mParent->i_unregisterMachine(this, id);
5029 // calls VirtualBox::i_saveSettings()
5030
5031 return S_OK;
5032 }
5033
5034 HRESULT rc = S_OK;
5035
5036 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5037 // discard saved state
5038 if (mData->mMachineState == MachineState_Saved)
5039 {
5040 // add the saved state file to the list of files the caller should delete
5041 Assert(!mSSData->strStateFilePath.isEmpty());
5042 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5043
5044 mSSData->strStateFilePath.setNull();
5045
5046 // unconditionally set the machine state to powered off, we now
5047 // know no session has locked the machine
5048 mData->mMachineState = MachineState_PoweredOff;
5049 }
5050
5051 size_t cSnapshots = 0;
5052 if (mData->mFirstSnapshot)
5053 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5054 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5055 // fail now before we start detaching media
5056 return setError(VBOX_E_INVALID_OBJECT_STATE,
5057 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5058 mUserData->s.strName.c_str(), cSnapshots);
5059
5060 // This list collects the medium objects from all medium attachments
5061 // which we will detach from the machine and its snapshots, in a specific
5062 // order which allows for closing all media without getting "media in use"
5063 // errors, simply by going through the list from the front to the back:
5064 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5065 // and must be closed before the parent media from the snapshots, or closing the parents
5066 // will fail because they still have children);
5067 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5068 // the root ("first") snapshot of the machine.
5069 MediaList llMedia;
5070
5071 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5072 && mMediumAttachments->size()
5073 )
5074 {
5075 // we have media attachments: detach them all and add the Medium objects to our list
5076 if (aCleanupMode != CleanupMode_UnregisterOnly)
5077 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5078 else
5079 return setError(VBOX_E_INVALID_OBJECT_STATE,
5080 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5081 mUserData->s.strName.c_str(), mMediumAttachments->size());
5082 }
5083
5084 if (cSnapshots)
5085 {
5086 // add the media from the medium attachments of the snapshots to llMedia
5087 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5088 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5089 // into the children first
5090
5091 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5092 MachineState_T oldState = mData->mMachineState;
5093 mData->mMachineState = MachineState_DeletingSnapshot;
5094
5095 // make a copy of the first snapshot so the refcount does not drop to 0
5096 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5097 // because of the AutoCaller voodoo)
5098 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5099
5100 // GO!
5101 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5102
5103 mData->mMachineState = oldState;
5104 }
5105
5106 if (FAILED(rc))
5107 {
5108 i_rollbackMedia();
5109 return rc;
5110 }
5111
5112 // commit all the media changes made above
5113 i_commitMedia();
5114
5115 mData->mRegistered = false;
5116
5117 // machine lock no longer needed
5118 alock.release();
5119
5120 // return media to caller
5121 aMedia.resize(llMedia.size());
5122 size_t i = 0;
5123 for (MediaList::const_iterator
5124 it = llMedia.begin();
5125 it != llMedia.end();
5126 ++it, ++i)
5127 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5128
5129 mParent->i_unregisterMachine(this, id);
5130 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5131
5132 return S_OK;
5133}
5134
5135/**
5136 * Task record for deleting a machine config.
5137 */
5138class Machine::DeleteConfigTask
5139 : public Machine::Task
5140{
5141public:
5142 DeleteConfigTask(Machine *m,
5143 Progress *p,
5144 const Utf8Str &t,
5145 const RTCList<ComPtr<IMedium> > &llMediums,
5146 const StringsList &llFilesToDelete)
5147 : Task(m, p, t),
5148 m_llMediums(llMediums),
5149 m_llFilesToDelete(llFilesToDelete)
5150 {}
5151
5152private:
5153 void handler()
5154 {
5155 try
5156 {
5157 m_pMachine->i_deleteConfigHandler(*this);
5158 }
5159 catch (...)
5160 {
5161 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5162 }
5163 }
5164
5165 RTCList<ComPtr<IMedium> > m_llMediums;
5166 StringsList m_llFilesToDelete;
5167
5168 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5169};
5170
5171/**
5172 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5173 * SessionMachine::taskHandler().
5174 *
5175 * @note Locks this object for writing.
5176 *
5177 * @param task
5178 * @return
5179 */
5180void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5181{
5182 LogFlowThisFuncEnter();
5183
5184 AutoCaller autoCaller(this);
5185 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5186 if (FAILED(autoCaller.rc()))
5187 {
5188 /* we might have been uninitialized because the session was accidentally
5189 * closed by the client, so don't assert */
5190 HRESULT rc = setError(E_FAIL,
5191 tr("The session has been accidentally closed"));
5192 task.m_pProgress->i_notifyComplete(rc);
5193 LogFlowThisFuncLeave();
5194 return;
5195 }
5196
5197 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5198
5199 HRESULT rc = S_OK;
5200
5201 try
5202 {
5203 ULONG uLogHistoryCount = 3;
5204 ComPtr<ISystemProperties> systemProperties;
5205 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5206 if (FAILED(rc)) throw rc;
5207
5208 if (!systemProperties.isNull())
5209 {
5210 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5211 if (FAILED(rc)) throw rc;
5212 }
5213
5214 MachineState_T oldState = mData->mMachineState;
5215 i_setMachineState(MachineState_SettingUp);
5216 alock.release();
5217 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5218 {
5219 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5220 {
5221 AutoCaller mac(pMedium);
5222 if (FAILED(mac.rc())) throw mac.rc();
5223 Utf8Str strLocation = pMedium->i_getLocationFull();
5224 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5225 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5226 if (FAILED(rc)) throw rc;
5227 }
5228 if (pMedium->i_isMediumFormatFile())
5229 {
5230 ComPtr<IProgress> pProgress2;
5231 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5232 if (FAILED(rc)) throw rc;
5233 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5234 if (FAILED(rc)) throw rc;
5235 }
5236
5237 /* Close the medium, deliberately without checking the return
5238 * code, and without leaving any trace in the error info, as
5239 * a failure here is a very minor issue, which shouldn't happen
5240 * as above we even managed to delete the medium. */
5241 {
5242 ErrorInfoKeeper eik;
5243 pMedium->Close();
5244 }
5245 }
5246 i_setMachineState(oldState);
5247 alock.acquire();
5248
5249 // delete the files pushed on the task list by Machine::Delete()
5250 // (this includes saved states of the machine and snapshots and
5251 // medium storage files from the IMedium list passed in, and the
5252 // machine XML file)
5253 for (StringsList::const_iterator
5254 it = task.m_llFilesToDelete.begin();
5255 it != task.m_llFilesToDelete.end();
5256 ++it)
5257 {
5258 const Utf8Str &strFile = *it;
5259 LogFunc(("Deleting file %s\n", strFile.c_str()));
5260 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5261 if (FAILED(rc)) throw rc;
5262
5263 int vrc = RTFileDelete(strFile.c_str());
5264 if (RT_FAILURE(vrc))
5265 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5266 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5267 }
5268
5269 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5270 if (FAILED(rc)) throw rc;
5271
5272 /* delete the settings only when the file actually exists */
5273 if (mData->pMachineConfigFile->fileExists())
5274 {
5275 /* Delete any backup or uncommitted XML files. Ignore failures.
5276 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5277 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5278 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5279 RTFileDelete(otherXml.c_str());
5280 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5281 RTFileDelete(otherXml.c_str());
5282
5283 /* delete the Logs folder, nothing important should be left
5284 * there (we don't check for errors because the user might have
5285 * some private files there that we don't want to delete) */
5286 Utf8Str logFolder;
5287 getLogFolder(logFolder);
5288 Assert(logFolder.length());
5289 if (RTDirExists(logFolder.c_str()))
5290 {
5291 /* Delete all VBox.log[.N] files from the Logs folder
5292 * (this must be in sync with the rotation logic in
5293 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5294 * files that may have been created by the GUI. */
5295 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5296 logFolder.c_str(), RTPATH_DELIMITER);
5297 RTFileDelete(log.c_str());
5298 log = Utf8StrFmt("%s%cVBox.png",
5299 logFolder.c_str(), RTPATH_DELIMITER);
5300 RTFileDelete(log.c_str());
5301 for (int i = uLogHistoryCount; i > 0; i--)
5302 {
5303 log = Utf8StrFmt("%s%cVBox.log.%d",
5304 logFolder.c_str(), RTPATH_DELIMITER, i);
5305 RTFileDelete(log.c_str());
5306 log = Utf8StrFmt("%s%cVBox.png.%d",
5307 logFolder.c_str(), RTPATH_DELIMITER, i);
5308 RTFileDelete(log.c_str());
5309 }
5310#if defined(RT_OS_WINDOWS)
5311 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5312 RTFileDelete(log.c_str());
5313 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5314 RTFileDelete(log.c_str());
5315#endif
5316
5317 RTDirRemove(logFolder.c_str());
5318 }
5319
5320 /* delete the Snapshots folder, nothing important should be left
5321 * there (we don't check for errors because the user might have
5322 * some private files there that we don't want to delete) */
5323 Utf8Str strFullSnapshotFolder;
5324 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5325 Assert(!strFullSnapshotFolder.isEmpty());
5326 if (RTDirExists(strFullSnapshotFolder.c_str()))
5327 RTDirRemove(strFullSnapshotFolder.c_str());
5328
5329 // delete the directory that contains the settings file, but only
5330 // if it matches the VM name
5331 Utf8Str settingsDir;
5332 if (i_isInOwnDir(&settingsDir))
5333 RTDirRemove(settingsDir.c_str());
5334 }
5335
5336 alock.release();
5337
5338 mParent->i_saveModifiedRegistries();
5339 }
5340 catch (HRESULT aRC) { rc = aRC; }
5341
5342 task.m_pProgress->i_notifyComplete(rc);
5343
5344 LogFlowThisFuncLeave();
5345}
5346
5347HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5348{
5349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5350
5351 HRESULT rc = i_checkStateDependency(MutableStateDep);
5352 if (FAILED(rc)) return rc;
5353
5354 if (mData->mRegistered)
5355 return setError(VBOX_E_INVALID_VM_STATE,
5356 tr("Cannot delete settings of a registered machine"));
5357
5358 // collect files to delete
5359 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5360 if (mData->pMachineConfigFile->fileExists())
5361 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5362
5363 RTCList<ComPtr<IMedium> > llMediums;
5364 for (size_t i = 0; i < aMedia.size(); ++i)
5365 {
5366 IMedium *pIMedium(aMedia[i]);
5367 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5368 if (pMedium.isNull())
5369 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5370 SafeArray<BSTR> ids;
5371 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5372 if (FAILED(rc)) return rc;
5373 /* At this point the medium should not have any back references
5374 * anymore. If it has it is attached to another VM and *must* not
5375 * deleted. */
5376 if (ids.size() < 1)
5377 llMediums.append(pMedium);
5378 }
5379
5380 ComObjPtr<Progress> pProgress;
5381 pProgress.createObject();
5382 rc = pProgress->init(i_getVirtualBox(),
5383 static_cast<IMachine*>(this) /* aInitiator */,
5384 tr("Deleting files"),
5385 true /* fCancellable */,
5386 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5387 tr("Collecting file inventory"));
5388 if (FAILED(rc))
5389 return rc;
5390
5391 /* create and start the task on a separate thread (note that it will not
5392 * start working until we release alock) */
5393 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5394 rc = pTask->createThread();
5395 if (FAILED(rc))
5396 return rc;
5397
5398 pProgress.queryInterfaceTo(aProgress.asOutParam());
5399
5400 LogFlowFuncLeave();
5401
5402 return S_OK;
5403}
5404
5405HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5406{
5407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5408
5409 ComObjPtr<Snapshot> pSnapshot;
5410 HRESULT rc;
5411
5412 if (aNameOrId.isEmpty())
5413 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5414 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5415 else
5416 {
5417 Guid uuid(aNameOrId);
5418 if (uuid.isValid())
5419 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5420 else
5421 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5422 }
5423 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5424
5425 return rc;
5426}
5427
5428HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5429 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5430{
5431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5432
5433 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5434 if (FAILED(rc)) return rc;
5435
5436 ComObjPtr<SharedFolder> sharedFolder;
5437 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5438 if (SUCCEEDED(rc))
5439 return setError(VBOX_E_OBJECT_IN_USE,
5440 tr("Shared folder named '%s' already exists"),
5441 aName.c_str());
5442
5443 sharedFolder.createObject();
5444 rc = sharedFolder->init(i_getMachine(),
5445 aName,
5446 aHostPath,
5447 !!aWritable,
5448 !!aAutomount,
5449 aAutoMountPoint,
5450 true /* fFailOnError */);
5451 if (FAILED(rc)) return rc;
5452
5453 i_setModified(IsModified_SharedFolders);
5454 mHWData.backup();
5455 mHWData->mSharedFolders.push_back(sharedFolder);
5456
5457 /* inform the direct session if any */
5458 alock.release();
5459 i_onSharedFolderChange();
5460
5461 return S_OK;
5462}
5463
5464HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5465{
5466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5467
5468 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5469 if (FAILED(rc)) return rc;
5470
5471 ComObjPtr<SharedFolder> sharedFolder;
5472 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5473 if (FAILED(rc)) return rc;
5474
5475 i_setModified(IsModified_SharedFolders);
5476 mHWData.backup();
5477 mHWData->mSharedFolders.remove(sharedFolder);
5478
5479 /* inform the direct session if any */
5480 alock.release();
5481 i_onSharedFolderChange();
5482
5483 return S_OK;
5484}
5485
5486HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5487{
5488 /* start with No */
5489 *aCanShow = FALSE;
5490
5491 ComPtr<IInternalSessionControl> directControl;
5492 {
5493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5494
5495 if (mData->mSession.mState != SessionState_Locked)
5496 return setError(VBOX_E_INVALID_VM_STATE,
5497 tr("Machine is not locked for session (session state: %s)"),
5498 Global::stringifySessionState(mData->mSession.mState));
5499
5500 if (mData->mSession.mLockType == LockType_VM)
5501 directControl = mData->mSession.mDirectControl;
5502 }
5503
5504 /* ignore calls made after #OnSessionEnd() is called */
5505 if (!directControl)
5506 return S_OK;
5507
5508 LONG64 dummy;
5509 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5510}
5511
5512HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5513{
5514 ComPtr<IInternalSessionControl> directControl;
5515 {
5516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5517
5518 if (mData->mSession.mState != SessionState_Locked)
5519 return setError(E_FAIL,
5520 tr("Machine is not locked for session (session state: %s)"),
5521 Global::stringifySessionState(mData->mSession.mState));
5522
5523 if (mData->mSession.mLockType == LockType_VM)
5524 directControl = mData->mSession.mDirectControl;
5525 }
5526
5527 /* ignore calls made after #OnSessionEnd() is called */
5528 if (!directControl)
5529 return S_OK;
5530
5531 BOOL dummy;
5532 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5533}
5534
5535#ifdef VBOX_WITH_GUEST_PROPS
5536/**
5537 * Look up a guest property in VBoxSVC's internal structures.
5538 */
5539HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5540 com::Utf8Str &aValue,
5541 LONG64 *aTimestamp,
5542 com::Utf8Str &aFlags) const
5543{
5544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5545
5546 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5547 if (it != mHWData->mGuestProperties.end())
5548 {
5549 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5550 aValue = it->second.strValue;
5551 *aTimestamp = it->second.mTimestamp;
5552 GuestPropWriteFlags(it->second.mFlags, szFlags);
5553 aFlags = Utf8Str(szFlags);
5554 }
5555
5556 return S_OK;
5557}
5558
5559/**
5560 * Query the VM that a guest property belongs to for the property.
5561 * @returns E_ACCESSDENIED if the VM process is not available or not
5562 * currently handling queries and the lookup should then be done in
5563 * VBoxSVC.
5564 */
5565HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5566 com::Utf8Str &aValue,
5567 LONG64 *aTimestamp,
5568 com::Utf8Str &aFlags) const
5569{
5570 HRESULT rc = S_OK;
5571 BSTR bValue = NULL;
5572 BSTR bFlags = NULL;
5573
5574 ComPtr<IInternalSessionControl> directControl;
5575 {
5576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5577 if (mData->mSession.mLockType == LockType_VM)
5578 directControl = mData->mSession.mDirectControl;
5579 }
5580
5581 /* ignore calls made after #OnSessionEnd() is called */
5582 if (!directControl)
5583 rc = E_ACCESSDENIED;
5584 else
5585 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5586 0 /* accessMode */,
5587 &bValue, aTimestamp, &bFlags);
5588
5589 aValue = bValue;
5590 aFlags = bFlags;
5591
5592 return rc;
5593}
5594#endif // VBOX_WITH_GUEST_PROPS
5595
5596HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5597 com::Utf8Str &aValue,
5598 LONG64 *aTimestamp,
5599 com::Utf8Str &aFlags)
5600{
5601#ifndef VBOX_WITH_GUEST_PROPS
5602 ReturnComNotImplemented();
5603#else // VBOX_WITH_GUEST_PROPS
5604
5605 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5606
5607 if (rc == E_ACCESSDENIED)
5608 /* The VM is not running or the service is not (yet) accessible */
5609 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5610 return rc;
5611#endif // VBOX_WITH_GUEST_PROPS
5612}
5613
5614HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5615{
5616 LONG64 dummyTimestamp;
5617 com::Utf8Str dummyFlags;
5618 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5619 return rc;
5620
5621}
5622HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5623{
5624 com::Utf8Str dummyFlags;
5625 com::Utf8Str dummyValue;
5626 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5627 return rc;
5628}
5629
5630#ifdef VBOX_WITH_GUEST_PROPS
5631/**
5632 * Set a guest property in VBoxSVC's internal structures.
5633 */
5634HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5635 const com::Utf8Str &aFlags, bool fDelete)
5636{
5637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5638 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5639 if (FAILED(rc)) return rc;
5640
5641 try
5642 {
5643 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5644 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5645 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5646
5647 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5648 if (it == mHWData->mGuestProperties.end())
5649 {
5650 if (!fDelete)
5651 {
5652 i_setModified(IsModified_MachineData);
5653 mHWData.backupEx();
5654
5655 RTTIMESPEC time;
5656 HWData::GuestProperty prop;
5657 prop.strValue = Bstr(aValue).raw();
5658 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5659 prop.mFlags = fFlags;
5660 mHWData->mGuestProperties[aName] = prop;
5661 }
5662 }
5663 else
5664 {
5665 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5666 {
5667 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5668 }
5669 else
5670 {
5671 i_setModified(IsModified_MachineData);
5672 mHWData.backupEx();
5673
5674 /* The backupEx() operation invalidates our iterator,
5675 * so get a new one. */
5676 it = mHWData->mGuestProperties.find(aName);
5677 Assert(it != mHWData->mGuestProperties.end());
5678
5679 if (!fDelete)
5680 {
5681 RTTIMESPEC time;
5682 it->second.strValue = aValue;
5683 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5684 it->second.mFlags = fFlags;
5685 }
5686 else
5687 mHWData->mGuestProperties.erase(it);
5688 }
5689 }
5690
5691 if (SUCCEEDED(rc))
5692 {
5693 alock.release();
5694
5695 mParent->i_onGuestPropertyChange(mData->mUuid,
5696 Bstr(aName).raw(),
5697 Bstr(aValue).raw(),
5698 Bstr(aFlags).raw());
5699 }
5700 }
5701 catch (std::bad_alloc &)
5702 {
5703 rc = E_OUTOFMEMORY;
5704 }
5705
5706 return rc;
5707}
5708
5709/**
5710 * Set a property on the VM that that property belongs to.
5711 * @returns E_ACCESSDENIED if the VM process is not available or not
5712 * currently handling queries and the setting should then be done in
5713 * VBoxSVC.
5714 */
5715HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5716 const com::Utf8Str &aFlags, bool fDelete)
5717{
5718 HRESULT rc;
5719
5720 try
5721 {
5722 ComPtr<IInternalSessionControl> directControl;
5723 {
5724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5725 if (mData->mSession.mLockType == LockType_VM)
5726 directControl = mData->mSession.mDirectControl;
5727 }
5728
5729 BSTR dummy = NULL; /* will not be changed (setter) */
5730 LONG64 dummy64;
5731 if (!directControl)
5732 rc = E_ACCESSDENIED;
5733 else
5734 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5735 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5736 fDelete? 2: 1 /* accessMode */,
5737 &dummy, &dummy64, &dummy);
5738 }
5739 catch (std::bad_alloc &)
5740 {
5741 rc = E_OUTOFMEMORY;
5742 }
5743
5744 return rc;
5745}
5746#endif // VBOX_WITH_GUEST_PROPS
5747
5748HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5749 const com::Utf8Str &aFlags)
5750{
5751#ifndef VBOX_WITH_GUEST_PROPS
5752 ReturnComNotImplemented();
5753#else // VBOX_WITH_GUEST_PROPS
5754 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5755 if (rc == E_ACCESSDENIED)
5756 /* The VM is not running or the service is not (yet) accessible */
5757 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5758 return rc;
5759#endif // VBOX_WITH_GUEST_PROPS
5760}
5761
5762HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5763{
5764 return setGuestProperty(aProperty, aValue, "");
5765}
5766
5767HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5768{
5769#ifndef VBOX_WITH_GUEST_PROPS
5770 ReturnComNotImplemented();
5771#else // VBOX_WITH_GUEST_PROPS
5772 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5773 if (rc == E_ACCESSDENIED)
5774 /* The VM is not running or the service is not (yet) accessible */
5775 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5776 return rc;
5777#endif // VBOX_WITH_GUEST_PROPS
5778}
5779
5780#ifdef VBOX_WITH_GUEST_PROPS
5781/**
5782 * Enumerate the guest properties in VBoxSVC's internal structures.
5783 */
5784HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5785 std::vector<com::Utf8Str> &aNames,
5786 std::vector<com::Utf8Str> &aValues,
5787 std::vector<LONG64> &aTimestamps,
5788 std::vector<com::Utf8Str> &aFlags)
5789{
5790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5791 Utf8Str strPatterns(aPatterns);
5792
5793 /*
5794 * Look for matching patterns and build up a list.
5795 */
5796 HWData::GuestPropertyMap propMap;
5797 for (HWData::GuestPropertyMap::const_iterator
5798 it = mHWData->mGuestProperties.begin();
5799 it != mHWData->mGuestProperties.end();
5800 ++it)
5801 {
5802 if ( strPatterns.isEmpty()
5803 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5804 RTSTR_MAX,
5805 it->first.c_str(),
5806 RTSTR_MAX,
5807 NULL)
5808 )
5809 propMap.insert(*it);
5810 }
5811
5812 alock.release();
5813
5814 /*
5815 * And build up the arrays for returning the property information.
5816 */
5817 size_t cEntries = propMap.size();
5818
5819 aNames.resize(cEntries);
5820 aValues.resize(cEntries);
5821 aTimestamps.resize(cEntries);
5822 aFlags.resize(cEntries);
5823
5824 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5825 size_t i = 0;
5826 for (HWData::GuestPropertyMap::const_iterator
5827 it = propMap.begin();
5828 it != propMap.end();
5829 ++it, ++i)
5830 {
5831 aNames[i] = it->first;
5832 aValues[i] = it->second.strValue;
5833 aTimestamps[i] = it->second.mTimestamp;
5834 GuestPropWriteFlags(it->second.mFlags, szFlags);
5835 aFlags[i] = Utf8Str(szFlags);
5836 }
5837
5838 return S_OK;
5839}
5840
5841/**
5842 * Enumerate the properties managed by a VM.
5843 * @returns E_ACCESSDENIED if the VM process is not available or not
5844 * currently handling queries and the setting should then be done in
5845 * VBoxSVC.
5846 */
5847HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5848 std::vector<com::Utf8Str> &aNames,
5849 std::vector<com::Utf8Str> &aValues,
5850 std::vector<LONG64> &aTimestamps,
5851 std::vector<com::Utf8Str> &aFlags)
5852{
5853 HRESULT rc;
5854 ComPtr<IInternalSessionControl> directControl;
5855 {
5856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5857 if (mData->mSession.mLockType == LockType_VM)
5858 directControl = mData->mSession.mDirectControl;
5859 }
5860
5861 com::SafeArray<BSTR> bNames;
5862 com::SafeArray<BSTR> bValues;
5863 com::SafeArray<LONG64> bTimestamps;
5864 com::SafeArray<BSTR> bFlags;
5865
5866 if (!directControl)
5867 rc = E_ACCESSDENIED;
5868 else
5869 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5870 ComSafeArrayAsOutParam(bNames),
5871 ComSafeArrayAsOutParam(bValues),
5872 ComSafeArrayAsOutParam(bTimestamps),
5873 ComSafeArrayAsOutParam(bFlags));
5874 size_t i;
5875 aNames.resize(bNames.size());
5876 for (i = 0; i < bNames.size(); ++i)
5877 aNames[i] = Utf8Str(bNames[i]);
5878 aValues.resize(bValues.size());
5879 for (i = 0; i < bValues.size(); ++i)
5880 aValues[i] = Utf8Str(bValues[i]);
5881 aTimestamps.resize(bTimestamps.size());
5882 for (i = 0; i < bTimestamps.size(); ++i)
5883 aTimestamps[i] = bTimestamps[i];
5884 aFlags.resize(bFlags.size());
5885 for (i = 0; i < bFlags.size(); ++i)
5886 aFlags[i] = Utf8Str(bFlags[i]);
5887
5888 return rc;
5889}
5890#endif // VBOX_WITH_GUEST_PROPS
5891HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5892 std::vector<com::Utf8Str> &aNames,
5893 std::vector<com::Utf8Str> &aValues,
5894 std::vector<LONG64> &aTimestamps,
5895 std::vector<com::Utf8Str> &aFlags)
5896{
5897#ifndef VBOX_WITH_GUEST_PROPS
5898 ReturnComNotImplemented();
5899#else // VBOX_WITH_GUEST_PROPS
5900
5901 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5902
5903 if (rc == E_ACCESSDENIED)
5904 /* The VM is not running or the service is not (yet) accessible */
5905 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5906 return rc;
5907#endif // VBOX_WITH_GUEST_PROPS
5908}
5909
5910HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5911 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5912{
5913 MediumAttachmentList atts;
5914
5915 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5916 if (FAILED(rc)) return rc;
5917
5918 aMediumAttachments.resize(atts.size());
5919 size_t i = 0;
5920 for (MediumAttachmentList::const_iterator
5921 it = atts.begin();
5922 it != atts.end();
5923 ++it, ++i)
5924 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5925
5926 return S_OK;
5927}
5928
5929HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5930 LONG aControllerPort,
5931 LONG aDevice,
5932 ComPtr<IMediumAttachment> &aAttachment)
5933{
5934 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5935 aName.c_str(), aControllerPort, aDevice));
5936
5937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5938
5939 aAttachment = NULL;
5940
5941 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5942 aName,
5943 aControllerPort,
5944 aDevice);
5945 if (pAttach.isNull())
5946 return setError(VBOX_E_OBJECT_NOT_FOUND,
5947 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5948 aDevice, aControllerPort, aName.c_str());
5949
5950 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5951
5952 return S_OK;
5953}
5954
5955
5956HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5957 StorageBus_T aConnectionType,
5958 ComPtr<IStorageController> &aController)
5959{
5960 if ( (aConnectionType <= StorageBus_Null)
5961 || (aConnectionType > StorageBus_PCIe))
5962 return setError(E_INVALIDARG,
5963 tr("Invalid connection type: %d"),
5964 aConnectionType);
5965
5966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5967
5968 HRESULT rc = i_checkStateDependency(MutableStateDep);
5969 if (FAILED(rc)) return rc;
5970
5971 /* try to find one with the name first. */
5972 ComObjPtr<StorageController> ctrl;
5973
5974 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5975 if (SUCCEEDED(rc))
5976 return setError(VBOX_E_OBJECT_IN_USE,
5977 tr("Storage controller named '%s' already exists"),
5978 aName.c_str());
5979
5980 ctrl.createObject();
5981
5982 /* get a new instance number for the storage controller */
5983 ULONG ulInstance = 0;
5984 bool fBootable = true;
5985 for (StorageControllerList::const_iterator
5986 it = mStorageControllers->begin();
5987 it != mStorageControllers->end();
5988 ++it)
5989 {
5990 if ((*it)->i_getStorageBus() == aConnectionType)
5991 {
5992 ULONG ulCurInst = (*it)->i_getInstance();
5993
5994 if (ulCurInst >= ulInstance)
5995 ulInstance = ulCurInst + 1;
5996
5997 /* Only one controller of each type can be marked as bootable. */
5998 if ((*it)->i_getBootable())
5999 fBootable = false;
6000 }
6001 }
6002
6003 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6004 if (FAILED(rc)) return rc;
6005
6006 i_setModified(IsModified_Storage);
6007 mStorageControllers.backup();
6008 mStorageControllers->push_back(ctrl);
6009
6010 ctrl.queryInterfaceTo(aController.asOutParam());
6011
6012 /* inform the direct session if any */
6013 alock.release();
6014 i_onStorageControllerChange();
6015
6016 return S_OK;
6017}
6018
6019HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6020 ComPtr<IStorageController> &aStorageController)
6021{
6022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6023
6024 ComObjPtr<StorageController> ctrl;
6025
6026 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6027 if (SUCCEEDED(rc))
6028 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6029
6030 return rc;
6031}
6032
6033HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6034 ULONG aInstance,
6035 ComPtr<IStorageController> &aStorageController)
6036{
6037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6038
6039 for (StorageControllerList::const_iterator
6040 it = mStorageControllers->begin();
6041 it != mStorageControllers->end();
6042 ++it)
6043 {
6044 if ( (*it)->i_getStorageBus() == aConnectionType
6045 && (*it)->i_getInstance() == aInstance)
6046 {
6047 (*it).queryInterfaceTo(aStorageController.asOutParam());
6048 return S_OK;
6049 }
6050 }
6051
6052 return setError(VBOX_E_OBJECT_NOT_FOUND,
6053 tr("Could not find a storage controller with instance number '%lu'"),
6054 aInstance);
6055}
6056
6057HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6058{
6059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6060
6061 HRESULT rc = i_checkStateDependency(MutableStateDep);
6062 if (FAILED(rc)) return rc;
6063
6064 ComObjPtr<StorageController> ctrl;
6065
6066 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6067 if (SUCCEEDED(rc))
6068 {
6069 /* Ensure that only one controller of each type is marked as bootable. */
6070 if (aBootable == TRUE)
6071 {
6072 for (StorageControllerList::const_iterator
6073 it = mStorageControllers->begin();
6074 it != mStorageControllers->end();
6075 ++it)
6076 {
6077 ComObjPtr<StorageController> aCtrl = (*it);
6078
6079 if ( (aCtrl->i_getName() != aName)
6080 && aCtrl->i_getBootable() == TRUE
6081 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6082 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6083 {
6084 aCtrl->i_setBootable(FALSE);
6085 break;
6086 }
6087 }
6088 }
6089
6090 if (SUCCEEDED(rc))
6091 {
6092 ctrl->i_setBootable(aBootable);
6093 i_setModified(IsModified_Storage);
6094 }
6095 }
6096
6097 if (SUCCEEDED(rc))
6098 {
6099 /* inform the direct session if any */
6100 alock.release();
6101 i_onStorageControllerChange();
6102 }
6103
6104 return rc;
6105}
6106
6107HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6108{
6109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6110
6111 HRESULT rc = i_checkStateDependency(MutableStateDep);
6112 if (FAILED(rc)) return rc;
6113
6114 ComObjPtr<StorageController> ctrl;
6115 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6116 if (FAILED(rc)) return rc;
6117
6118 {
6119 /* find all attached devices to the appropriate storage controller and detach them all */
6120 // make a temporary list because detachDevice invalidates iterators into
6121 // mMediumAttachments
6122 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6123
6124 for (MediumAttachmentList::const_iterator
6125 it = llAttachments2.begin();
6126 it != llAttachments2.end();
6127 ++it)
6128 {
6129 MediumAttachment *pAttachTemp = *it;
6130
6131 AutoCaller localAutoCaller(pAttachTemp);
6132 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6133
6134 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6135
6136 if (pAttachTemp->i_getControllerName() == aName)
6137 {
6138 rc = i_detachDevice(pAttachTemp, alock, NULL);
6139 if (FAILED(rc)) return rc;
6140 }
6141 }
6142 }
6143
6144 /* We can remove it now. */
6145 i_setModified(IsModified_Storage);
6146 mStorageControllers.backup();
6147
6148 ctrl->i_unshare();
6149
6150 mStorageControllers->remove(ctrl);
6151
6152 /* inform the direct session if any */
6153 alock.release();
6154 i_onStorageControllerChange();
6155
6156 return S_OK;
6157}
6158
6159HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6160 ComPtr<IUSBController> &aController)
6161{
6162 if ( (aType <= USBControllerType_Null)
6163 || (aType >= USBControllerType_Last))
6164 return setError(E_INVALIDARG,
6165 tr("Invalid USB controller type: %d"),
6166 aType);
6167
6168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6169
6170 HRESULT rc = i_checkStateDependency(MutableStateDep);
6171 if (FAILED(rc)) return rc;
6172
6173 /* try to find one with the same type first. */
6174 ComObjPtr<USBController> ctrl;
6175
6176 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6177 if (SUCCEEDED(rc))
6178 return setError(VBOX_E_OBJECT_IN_USE,
6179 tr("USB controller named '%s' already exists"),
6180 aName.c_str());
6181
6182 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6183 ULONG maxInstances;
6184 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6185 if (FAILED(rc))
6186 return rc;
6187
6188 ULONG cInstances = i_getUSBControllerCountByType(aType);
6189 if (cInstances >= maxInstances)
6190 return setError(E_INVALIDARG,
6191 tr("Too many USB controllers of this type"));
6192
6193 ctrl.createObject();
6194
6195 rc = ctrl->init(this, aName, aType);
6196 if (FAILED(rc)) return rc;
6197
6198 i_setModified(IsModified_USB);
6199 mUSBControllers.backup();
6200 mUSBControllers->push_back(ctrl);
6201
6202 ctrl.queryInterfaceTo(aController.asOutParam());
6203
6204 /* inform the direct session if any */
6205 alock.release();
6206 i_onUSBControllerChange();
6207
6208 return S_OK;
6209}
6210
6211HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6212{
6213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6214
6215 ComObjPtr<USBController> ctrl;
6216
6217 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6218 if (SUCCEEDED(rc))
6219 ctrl.queryInterfaceTo(aController.asOutParam());
6220
6221 return rc;
6222}
6223
6224HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6225 ULONG *aControllers)
6226{
6227 if ( (aType <= USBControllerType_Null)
6228 || (aType >= USBControllerType_Last))
6229 return setError(E_INVALIDARG,
6230 tr("Invalid USB controller type: %d"),
6231 aType);
6232
6233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6234
6235 ComObjPtr<USBController> ctrl;
6236
6237 *aControllers = i_getUSBControllerCountByType(aType);
6238
6239 return S_OK;
6240}
6241
6242HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6243{
6244
6245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6246
6247 HRESULT rc = i_checkStateDependency(MutableStateDep);
6248 if (FAILED(rc)) return rc;
6249
6250 ComObjPtr<USBController> ctrl;
6251 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6252 if (FAILED(rc)) return rc;
6253
6254 i_setModified(IsModified_USB);
6255 mUSBControllers.backup();
6256
6257 ctrl->i_unshare();
6258
6259 mUSBControllers->remove(ctrl);
6260
6261 /* inform the direct session if any */
6262 alock.release();
6263 i_onUSBControllerChange();
6264
6265 return S_OK;
6266}
6267
6268HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6269 ULONG *aOriginX,
6270 ULONG *aOriginY,
6271 ULONG *aWidth,
6272 ULONG *aHeight,
6273 BOOL *aEnabled)
6274{
6275 uint32_t u32OriginX= 0;
6276 uint32_t u32OriginY= 0;
6277 uint32_t u32Width = 0;
6278 uint32_t u32Height = 0;
6279 uint16_t u16Flags = 0;
6280
6281 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6282 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6283 if (RT_FAILURE(vrc))
6284 {
6285#ifdef RT_OS_WINDOWS
6286 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6287 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6288 * So just assign fEnable to TRUE again.
6289 * The right fix would be to change GUI API wrappers to make sure that parameters
6290 * are changed only if API succeeds.
6291 */
6292 *aEnabled = TRUE;
6293#endif
6294 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6295 tr("Saved guest size is not available (%Rrc)"),
6296 vrc);
6297 }
6298
6299 *aOriginX = u32OriginX;
6300 *aOriginY = u32OriginY;
6301 *aWidth = u32Width;
6302 *aHeight = u32Height;
6303 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6304
6305 return S_OK;
6306}
6307
6308HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6309 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6310{
6311 if (aScreenId != 0)
6312 return E_NOTIMPL;
6313
6314 if ( aBitmapFormat != BitmapFormat_BGR0
6315 && aBitmapFormat != BitmapFormat_BGRA
6316 && aBitmapFormat != BitmapFormat_RGBA
6317 && aBitmapFormat != BitmapFormat_PNG)
6318 return setError(E_NOTIMPL,
6319 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6320
6321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6322
6323 uint8_t *pu8Data = NULL;
6324 uint32_t cbData = 0;
6325 uint32_t u32Width = 0;
6326 uint32_t u32Height = 0;
6327
6328 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6329
6330 if (RT_FAILURE(vrc))
6331 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6332 tr("Saved thumbnail data is not available (%Rrc)"),
6333 vrc);
6334
6335 HRESULT hr = S_OK;
6336
6337 *aWidth = u32Width;
6338 *aHeight = u32Height;
6339
6340 if (cbData > 0)
6341 {
6342 /* Convert pixels to the format expected by the API caller. */
6343 if (aBitmapFormat == BitmapFormat_BGR0)
6344 {
6345 /* [0] B, [1] G, [2] R, [3] 0. */
6346 aData.resize(cbData);
6347 memcpy(&aData.front(), pu8Data, cbData);
6348 }
6349 else if (aBitmapFormat == BitmapFormat_BGRA)
6350 {
6351 /* [0] B, [1] G, [2] R, [3] A. */
6352 aData.resize(cbData);
6353 for (uint32_t i = 0; i < cbData; i += 4)
6354 {
6355 aData[i] = pu8Data[i];
6356 aData[i + 1] = pu8Data[i + 1];
6357 aData[i + 2] = pu8Data[i + 2];
6358 aData[i + 3] = 0xff;
6359 }
6360 }
6361 else if (aBitmapFormat == BitmapFormat_RGBA)
6362 {
6363 /* [0] R, [1] G, [2] B, [3] A. */
6364 aData.resize(cbData);
6365 for (uint32_t i = 0; i < cbData; i += 4)
6366 {
6367 aData[i] = pu8Data[i + 2];
6368 aData[i + 1] = pu8Data[i + 1];
6369 aData[i + 2] = pu8Data[i];
6370 aData[i + 3] = 0xff;
6371 }
6372 }
6373 else if (aBitmapFormat == BitmapFormat_PNG)
6374 {
6375 uint8_t *pu8PNG = NULL;
6376 uint32_t cbPNG = 0;
6377 uint32_t cxPNG = 0;
6378 uint32_t cyPNG = 0;
6379
6380 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6381
6382 if (RT_SUCCESS(vrc))
6383 {
6384 aData.resize(cbPNG);
6385 if (cbPNG)
6386 memcpy(&aData.front(), pu8PNG, cbPNG);
6387 }
6388 else
6389 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6390 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6391 vrc);
6392
6393 RTMemFree(pu8PNG);
6394 }
6395 }
6396
6397 freeSavedDisplayScreenshot(pu8Data);
6398
6399 return hr;
6400}
6401
6402HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6403 ULONG *aWidth,
6404 ULONG *aHeight,
6405 std::vector<BitmapFormat_T> &aBitmapFormats)
6406{
6407 if (aScreenId != 0)
6408 return E_NOTIMPL;
6409
6410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6411
6412 uint8_t *pu8Data = NULL;
6413 uint32_t cbData = 0;
6414 uint32_t u32Width = 0;
6415 uint32_t u32Height = 0;
6416
6417 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6418
6419 if (RT_FAILURE(vrc))
6420 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6421 tr("Saved screenshot data is not available (%Rrc)"),
6422 vrc);
6423
6424 *aWidth = u32Width;
6425 *aHeight = u32Height;
6426 aBitmapFormats.resize(1);
6427 aBitmapFormats[0] = BitmapFormat_PNG;
6428
6429 freeSavedDisplayScreenshot(pu8Data);
6430
6431 return S_OK;
6432}
6433
6434HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6435 BitmapFormat_T aBitmapFormat,
6436 ULONG *aWidth,
6437 ULONG *aHeight,
6438 std::vector<BYTE> &aData)
6439{
6440 if (aScreenId != 0)
6441 return E_NOTIMPL;
6442
6443 if (aBitmapFormat != BitmapFormat_PNG)
6444 return E_NOTIMPL;
6445
6446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6447
6448 uint8_t *pu8Data = NULL;
6449 uint32_t cbData = 0;
6450 uint32_t u32Width = 0;
6451 uint32_t u32Height = 0;
6452
6453 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6454
6455 if (RT_FAILURE(vrc))
6456 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6457 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6458 vrc);
6459
6460 *aWidth = u32Width;
6461 *aHeight = u32Height;
6462
6463 aData.resize(cbData);
6464 if (cbData)
6465 memcpy(&aData.front(), pu8Data, cbData);
6466
6467 freeSavedDisplayScreenshot(pu8Data);
6468
6469 return S_OK;
6470}
6471
6472HRESULT Machine::hotPlugCPU(ULONG aCpu)
6473{
6474 HRESULT rc = S_OK;
6475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6476
6477 if (!mHWData->mCPUHotPlugEnabled)
6478 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6479
6480 if (aCpu >= mHWData->mCPUCount)
6481 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6482
6483 if (mHWData->mCPUAttached[aCpu])
6484 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6485
6486 alock.release();
6487 rc = i_onCPUChange(aCpu, false);
6488 alock.acquire();
6489 if (FAILED(rc)) return rc;
6490
6491 i_setModified(IsModified_MachineData);
6492 mHWData.backup();
6493 mHWData->mCPUAttached[aCpu] = true;
6494
6495 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6496 if (Global::IsOnline(mData->mMachineState))
6497 i_saveSettings(NULL);
6498
6499 return S_OK;
6500}
6501
6502HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6503{
6504 HRESULT rc = S_OK;
6505
6506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6507
6508 if (!mHWData->mCPUHotPlugEnabled)
6509 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6510
6511 if (aCpu >= SchemaDefs::MaxCPUCount)
6512 return setError(E_INVALIDARG,
6513 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6514 SchemaDefs::MaxCPUCount);
6515
6516 if (!mHWData->mCPUAttached[aCpu])
6517 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6518
6519 /* CPU 0 can't be detached */
6520 if (aCpu == 0)
6521 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6522
6523 alock.release();
6524 rc = i_onCPUChange(aCpu, true);
6525 alock.acquire();
6526 if (FAILED(rc)) return rc;
6527
6528 i_setModified(IsModified_MachineData);
6529 mHWData.backup();
6530 mHWData->mCPUAttached[aCpu] = false;
6531
6532 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6533 if (Global::IsOnline(mData->mMachineState))
6534 i_saveSettings(NULL);
6535
6536 return S_OK;
6537}
6538
6539HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6540{
6541 *aAttached = false;
6542
6543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6544
6545 /* If hotplug is enabled the CPU is always enabled. */
6546 if (!mHWData->mCPUHotPlugEnabled)
6547 {
6548 if (aCpu < mHWData->mCPUCount)
6549 *aAttached = true;
6550 }
6551 else
6552 {
6553 if (aCpu < SchemaDefs::MaxCPUCount)
6554 *aAttached = mHWData->mCPUAttached[aCpu];
6555 }
6556
6557 return S_OK;
6558}
6559
6560HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6561{
6562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6563
6564 Utf8Str log = i_getLogFilename(aIdx);
6565 if (!RTFileExists(log.c_str()))
6566 log.setNull();
6567 aFilename = log;
6568
6569 return S_OK;
6570}
6571
6572HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6573{
6574 if (aSize < 0)
6575 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6576
6577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6578
6579 HRESULT rc = S_OK;
6580 Utf8Str log = i_getLogFilename(aIdx);
6581
6582 /* do not unnecessarily hold the lock while doing something which does
6583 * not need the lock and potentially takes a long time. */
6584 alock.release();
6585
6586 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6587 * keeps the SOAP reply size under 1M for the webservice (we're using
6588 * base64 encoded strings for binary data for years now, avoiding the
6589 * expansion of each byte array element to approx. 25 bytes of XML. */
6590 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6591 aData.resize(cbData);
6592
6593 RTFILE LogFile;
6594 int vrc = RTFileOpen(&LogFile, log.c_str(),
6595 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6596 if (RT_SUCCESS(vrc))
6597 {
6598 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6599 if (RT_SUCCESS(vrc))
6600 aData.resize(cbData);
6601 else
6602 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6603 tr("Could not read log file '%s' (%Rrc)"),
6604 log.c_str(), vrc);
6605 RTFileClose(LogFile);
6606 }
6607 else
6608 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6609 tr("Could not open log file '%s' (%Rrc)"),
6610 log.c_str(), vrc);
6611
6612 if (FAILED(rc))
6613 aData.resize(0);
6614
6615 return rc;
6616}
6617
6618
6619/**
6620 * Currently this method doesn't attach device to the running VM,
6621 * just makes sure it's plugged on next VM start.
6622 */
6623HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6624{
6625 // lock scope
6626 {
6627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6628
6629 HRESULT rc = i_checkStateDependency(MutableStateDep);
6630 if (FAILED(rc)) return rc;
6631
6632 ChipsetType_T aChipset = ChipsetType_PIIX3;
6633 COMGETTER(ChipsetType)(&aChipset);
6634
6635 if (aChipset != ChipsetType_ICH9)
6636 {
6637 return setError(E_INVALIDARG,
6638 tr("Host PCI attachment only supported with ICH9 chipset"));
6639 }
6640
6641 // check if device with this host PCI address already attached
6642 for (HWData::PCIDeviceAssignmentList::const_iterator
6643 it = mHWData->mPCIDeviceAssignments.begin();
6644 it != mHWData->mPCIDeviceAssignments.end();
6645 ++it)
6646 {
6647 LONG iHostAddress = -1;
6648 ComPtr<PCIDeviceAttachment> pAttach;
6649 pAttach = *it;
6650 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6651 if (iHostAddress == aHostAddress)
6652 return setError(E_INVALIDARG,
6653 tr("Device with host PCI address already attached to this VM"));
6654 }
6655
6656 ComObjPtr<PCIDeviceAttachment> pda;
6657 char name[32];
6658
6659 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6660 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6661 pda.createObject();
6662 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6663 i_setModified(IsModified_MachineData);
6664 mHWData.backup();
6665 mHWData->mPCIDeviceAssignments.push_back(pda);
6666 }
6667
6668 return S_OK;
6669}
6670
6671/**
6672 * Currently this method doesn't detach device from the running VM,
6673 * just makes sure it's not plugged on next VM start.
6674 */
6675HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6676{
6677 ComObjPtr<PCIDeviceAttachment> pAttach;
6678 bool fRemoved = false;
6679 HRESULT rc;
6680
6681 // lock scope
6682 {
6683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6684
6685 rc = i_checkStateDependency(MutableStateDep);
6686 if (FAILED(rc)) return rc;
6687
6688 for (HWData::PCIDeviceAssignmentList::const_iterator
6689 it = mHWData->mPCIDeviceAssignments.begin();
6690 it != mHWData->mPCIDeviceAssignments.end();
6691 ++it)
6692 {
6693 LONG iHostAddress = -1;
6694 pAttach = *it;
6695 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6696 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6697 {
6698 i_setModified(IsModified_MachineData);
6699 mHWData.backup();
6700 mHWData->mPCIDeviceAssignments.remove(pAttach);
6701 fRemoved = true;
6702 break;
6703 }
6704 }
6705 }
6706
6707
6708 /* Fire event outside of the lock */
6709 if (fRemoved)
6710 {
6711 Assert(!pAttach.isNull());
6712 ComPtr<IEventSource> es;
6713 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6714 Assert(SUCCEEDED(rc));
6715 Bstr mid;
6716 rc = this->COMGETTER(Id)(mid.asOutParam());
6717 Assert(SUCCEEDED(rc));
6718 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6719 }
6720
6721 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6722 tr("No host PCI device %08x attached"),
6723 aHostAddress
6724 );
6725}
6726
6727HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6728{
6729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6730
6731 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6732 size_t i = 0;
6733 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6734 it = mHWData->mPCIDeviceAssignments.begin();
6735 it != mHWData->mPCIDeviceAssignments.end();
6736 ++it, ++i)
6737 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6738
6739 return S_OK;
6740}
6741
6742HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6743{
6744 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6745
6746 return S_OK;
6747}
6748
6749HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6750{
6751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6752
6753 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6754
6755 return S_OK;
6756}
6757
6758HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6759{
6760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6761 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6762 if (SUCCEEDED(hrc))
6763 {
6764 hrc = mHWData.backupEx();
6765 if (SUCCEEDED(hrc))
6766 {
6767 i_setModified(IsModified_MachineData);
6768 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6769 }
6770 }
6771 return hrc;
6772}
6773
6774HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6775{
6776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6777 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6778 return S_OK;
6779}
6780
6781HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6782{
6783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6784 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6785 if (SUCCEEDED(hrc))
6786 {
6787 hrc = mHWData.backupEx();
6788 if (SUCCEEDED(hrc))
6789 {
6790 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6791 if (SUCCEEDED(hrc))
6792 i_setModified(IsModified_MachineData);
6793 }
6794 }
6795 return hrc;
6796}
6797
6798HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6799{
6800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6801
6802 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6803
6804 return S_OK;
6805}
6806
6807HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6808{
6809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6810 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6811 if (SUCCEEDED(hrc))
6812 {
6813 hrc = mHWData.backupEx();
6814 if (SUCCEEDED(hrc))
6815 {
6816 i_setModified(IsModified_MachineData);
6817 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6818 }
6819 }
6820 return hrc;
6821}
6822
6823HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6824{
6825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6826
6827 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6828
6829 return S_OK;
6830}
6831
6832HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6833{
6834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6835
6836 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6837 if ( SUCCEEDED(hrc)
6838 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6839 {
6840 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6841 int vrc;
6842
6843 if (aAutostartEnabled)
6844 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6845 else
6846 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6847
6848 if (RT_SUCCESS(vrc))
6849 {
6850 hrc = mHWData.backupEx();
6851 if (SUCCEEDED(hrc))
6852 {
6853 i_setModified(IsModified_MachineData);
6854 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6855 }
6856 }
6857 else if (vrc == VERR_NOT_SUPPORTED)
6858 hrc = setError(VBOX_E_NOT_SUPPORTED,
6859 tr("The VM autostart feature is not supported on this platform"));
6860 else if (vrc == VERR_PATH_NOT_FOUND)
6861 hrc = setError(E_FAIL,
6862 tr("The path to the autostart database is not set"));
6863 else
6864 hrc = setError(E_UNEXPECTED,
6865 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6866 aAutostartEnabled ? "Adding" : "Removing",
6867 mUserData->s.strName.c_str(), vrc);
6868 }
6869 return hrc;
6870}
6871
6872HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6873{
6874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6875
6876 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6877
6878 return S_OK;
6879}
6880
6881HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6882{
6883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6884 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6885 if (SUCCEEDED(hrc))
6886 {
6887 hrc = mHWData.backupEx();
6888 if (SUCCEEDED(hrc))
6889 {
6890 i_setModified(IsModified_MachineData);
6891 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6892 }
6893 }
6894 return hrc;
6895}
6896
6897HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6898{
6899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6900
6901 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6902
6903 return S_OK;
6904}
6905
6906HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6907{
6908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6909 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6910 if ( SUCCEEDED(hrc)
6911 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6912 {
6913 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6914 int vrc;
6915
6916 if (aAutostopType != AutostopType_Disabled)
6917 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6918 else
6919 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6920
6921 if (RT_SUCCESS(vrc))
6922 {
6923 hrc = mHWData.backupEx();
6924 if (SUCCEEDED(hrc))
6925 {
6926 i_setModified(IsModified_MachineData);
6927 mHWData->mAutostart.enmAutostopType = aAutostopType;
6928 }
6929 }
6930 else if (vrc == VERR_NOT_SUPPORTED)
6931 hrc = setError(VBOX_E_NOT_SUPPORTED,
6932 tr("The VM autostop feature is not supported on this platform"));
6933 else if (vrc == VERR_PATH_NOT_FOUND)
6934 hrc = setError(E_FAIL,
6935 tr("The path to the autostart database is not set"));
6936 else
6937 hrc = setError(E_UNEXPECTED,
6938 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6939 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6940 mUserData->s.strName.c_str(), vrc);
6941 }
6942 return hrc;
6943}
6944
6945HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6946{
6947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6948
6949 aDefaultFrontend = mHWData->mDefaultFrontend;
6950
6951 return S_OK;
6952}
6953
6954HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6955{
6956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6957 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6958 if (SUCCEEDED(hrc))
6959 {
6960 hrc = mHWData.backupEx();
6961 if (SUCCEEDED(hrc))
6962 {
6963 i_setModified(IsModified_MachineData);
6964 mHWData->mDefaultFrontend = aDefaultFrontend;
6965 }
6966 }
6967 return hrc;
6968}
6969
6970HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6971{
6972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6973 size_t cbIcon = mUserData->s.ovIcon.size();
6974 aIcon.resize(cbIcon);
6975 if (cbIcon)
6976 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6977 return S_OK;
6978}
6979
6980HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6981{
6982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6983 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6984 if (SUCCEEDED(hrc))
6985 {
6986 i_setModified(IsModified_MachineData);
6987 mUserData.backup();
6988 size_t cbIcon = aIcon.size();
6989 mUserData->s.ovIcon.resize(cbIcon);
6990 if (cbIcon)
6991 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6992 }
6993 return hrc;
6994}
6995
6996HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6997{
6998#ifdef VBOX_WITH_USB
6999 *aUSBProxyAvailable = true;
7000#else
7001 *aUSBProxyAvailable = false;
7002#endif
7003 return S_OK;
7004}
7005
7006HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7007{
7008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7009
7010 aVMProcessPriority = mUserData->s.strVMPriority;
7011
7012 return S_OK;
7013}
7014
7015HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7016{
7017 RT_NOREF(aVMProcessPriority);
7018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7019 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7020 if (SUCCEEDED(hrc))
7021 {
7022 /** @todo r=klaus: currently this is marked as not implemented, as
7023 * the code for setting the priority of the process is not there
7024 * (neither when starting the VM nor at runtime). */
7025 ReturnComNotImplemented();
7026#if 0
7027 hrc = mUserData.backupEx();
7028 if (SUCCEEDED(hrc))
7029 {
7030 i_setModified(IsModified_MachineData);
7031 mUserData->s.strVMPriority = aVMProcessPriority;
7032 }
7033#endif
7034 }
7035 return hrc;
7036}
7037
7038HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7039 ComPtr<IProgress> &aProgress)
7040{
7041 ComObjPtr<Progress> pP;
7042 Progress *ppP = pP;
7043 IProgress *iP = static_cast<IProgress *>(ppP);
7044 IProgress **pProgress = &iP;
7045
7046 IMachine *pTarget = aTarget;
7047
7048 /* Convert the options. */
7049 RTCList<CloneOptions_T> optList;
7050 if (aOptions.size())
7051 for (size_t i = 0; i < aOptions.size(); ++i)
7052 optList.append(aOptions[i]);
7053
7054 if (optList.contains(CloneOptions_Link))
7055 {
7056 if (!i_isSnapshotMachine())
7057 return setError(E_INVALIDARG,
7058 tr("Linked clone can only be created from a snapshot"));
7059 if (aMode != CloneMode_MachineState)
7060 return setError(E_INVALIDARG,
7061 tr("Linked clone can only be created for a single machine state"));
7062 }
7063 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7064
7065 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7066
7067 HRESULT rc = pWorker->start(pProgress);
7068
7069 pP = static_cast<Progress *>(*pProgress);
7070 pP.queryInterfaceTo(aProgress.asOutParam());
7071
7072 return rc;
7073
7074}
7075
7076HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7077 const com::Utf8Str &aType,
7078 ComPtr<IProgress> &aProgress)
7079{
7080 LogFlowThisFuncEnter();
7081
7082 ComObjPtr<Progress> progress;
7083
7084 progress.createObject();
7085
7086 HRESULT rc = S_OK;
7087 Utf8Str targetPath = aTargetPath;
7088 Utf8Str type = aType;
7089
7090 /* Initialize our worker task */
7091 MachineMoveVM* task = NULL;
7092 try
7093 {
7094 task = new MachineMoveVM(this, targetPath, type, progress);
7095 }
7096 catch(...)
7097 {
7098 delete task;
7099 return rc;
7100 }
7101
7102 /*
7103 * task pointer will be owned by the ThreadTask class.
7104 * There is no need to call operator "delete" in the end.
7105 */
7106 rc = task->init();
7107 if (SUCCEEDED(rc))
7108 {
7109 rc = task->createThread();
7110 if (FAILED(rc))
7111 {
7112 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7113 }
7114
7115 /* Return progress to the caller */
7116 progress.queryInterfaceTo(aProgress.asOutParam());
7117 }
7118
7119 LogFlowThisFuncLeave();
7120 return rc;
7121
7122}
7123
7124HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7125{
7126 NOREF(aProgress);
7127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7128
7129 // This check should always fail.
7130 HRESULT rc = i_checkStateDependency(MutableStateDep);
7131 if (FAILED(rc)) return rc;
7132
7133 AssertFailedReturn(E_NOTIMPL);
7134}
7135
7136HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7137{
7138 NOREF(aSavedStateFile);
7139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7140
7141 // This check should always fail.
7142 HRESULT rc = i_checkStateDependency(MutableStateDep);
7143 if (FAILED(rc)) return rc;
7144
7145 AssertFailedReturn(E_NOTIMPL);
7146}
7147
7148HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7149{
7150 NOREF(aFRemoveFile);
7151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7152
7153 // This check should always fail.
7154 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7155 if (FAILED(rc)) return rc;
7156
7157 AssertFailedReturn(E_NOTIMPL);
7158}
7159
7160// public methods for internal purposes
7161/////////////////////////////////////////////////////////////////////////////
7162
7163/**
7164 * Adds the given IsModified_* flag to the dirty flags of the machine.
7165 * This must be called either during i_loadSettings or under the machine write lock.
7166 * @param fl Flag
7167 * @param fAllowStateModification If state modifications are allowed.
7168 */
7169void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7170{
7171 mData->flModifications |= fl;
7172 if (fAllowStateModification && i_isStateModificationAllowed())
7173 mData->mCurrentStateModified = true;
7174}
7175
7176/**
7177 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7178 * care of the write locking.
7179 *
7180 * @param fModification The flag to add.
7181 * @param fAllowStateModification If state modifications are allowed.
7182 */
7183void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7184{
7185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7186 i_setModified(fModification, fAllowStateModification);
7187}
7188
7189/**
7190 * Saves the registry entry of this machine to the given configuration node.
7191 *
7192 * @param data Machine registry data.
7193 *
7194 * @note locks this object for reading.
7195 */
7196HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7197{
7198 AutoLimitedCaller autoCaller(this);
7199 AssertComRCReturnRC(autoCaller.rc());
7200
7201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7202
7203 data.uuid = mData->mUuid;
7204 data.strSettingsFile = mData->m_strConfigFile;
7205
7206 return S_OK;
7207}
7208
7209/**
7210 * Calculates the absolute path of the given path taking the directory of the
7211 * machine settings file as the current directory.
7212 *
7213 * @param strPath Path to calculate the absolute path for.
7214 * @param aResult Where to put the result (used only on success, can be the
7215 * same Utf8Str instance as passed in @a aPath).
7216 * @return IPRT result.
7217 *
7218 * @note Locks this object for reading.
7219 */
7220int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7221{
7222 AutoCaller autoCaller(this);
7223 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7224
7225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7228
7229 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7230
7231 strSettingsDir.stripFilename();
7232 char folder[RTPATH_MAX];
7233 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7234 if (RT_SUCCESS(vrc))
7235 aResult = folder;
7236
7237 return vrc;
7238}
7239
7240/**
7241 * Copies strSource to strTarget, making it relative to the machine folder
7242 * if it is a subdirectory thereof, or simply copying it otherwise.
7243 *
7244 * @param strSource Path to evaluate and copy.
7245 * @param strTarget Buffer to receive target path.
7246 *
7247 * @note Locks this object for reading.
7248 */
7249void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7250 Utf8Str &strTarget)
7251{
7252 AutoCaller autoCaller(this);
7253 AssertComRCReturn(autoCaller.rc(), (void)0);
7254
7255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7256
7257 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7258 // use strTarget as a temporary buffer to hold the machine settings dir
7259 strTarget = mData->m_strConfigFileFull;
7260 strTarget.stripFilename();
7261 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7262 {
7263 // is relative: then append what's left
7264 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7265 // for empty paths (only possible for subdirs) use "." to avoid
7266 // triggering default settings for not present config attributes.
7267 if (strTarget.isEmpty())
7268 strTarget = ".";
7269 }
7270 else
7271 // is not relative: then overwrite
7272 strTarget = strSource;
7273}
7274
7275/**
7276 * Returns the full path to the machine's log folder in the
7277 * \a aLogFolder argument.
7278 */
7279void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7280{
7281 AutoCaller autoCaller(this);
7282 AssertComRCReturnVoid(autoCaller.rc());
7283
7284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7285
7286 char szTmp[RTPATH_MAX];
7287 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7288 if (RT_SUCCESS(vrc))
7289 {
7290 if (szTmp[0] && !mUserData.isNull())
7291 {
7292 char szTmp2[RTPATH_MAX];
7293 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7294 if (RT_SUCCESS(vrc))
7295 aLogFolder = Utf8StrFmt("%s%c%s",
7296 szTmp2,
7297 RTPATH_DELIMITER,
7298 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7299 }
7300 else
7301 vrc = VERR_PATH_IS_RELATIVE;
7302 }
7303
7304 if (RT_FAILURE(vrc))
7305 {
7306 // fallback if VBOX_USER_LOGHOME is not set or invalid
7307 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7308 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7309 aLogFolder.append(RTPATH_DELIMITER);
7310 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7311 }
7312}
7313
7314/**
7315 * Returns the full path to the machine's log file for an given index.
7316 */
7317Utf8Str Machine::i_getLogFilename(ULONG idx)
7318{
7319 Utf8Str logFolder;
7320 getLogFolder(logFolder);
7321 Assert(logFolder.length());
7322
7323 Utf8Str log;
7324 if (idx == 0)
7325 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7326#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7327 else if (idx == 1)
7328 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7329 else
7330 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7331#else
7332 else
7333 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7334#endif
7335 return log;
7336}
7337
7338/**
7339 * Returns the full path to the machine's hardened log file.
7340 */
7341Utf8Str Machine::i_getHardeningLogFilename(void)
7342{
7343 Utf8Str strFilename;
7344 getLogFolder(strFilename);
7345 Assert(strFilename.length());
7346 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7347 return strFilename;
7348}
7349
7350
7351/**
7352 * Composes a unique saved state filename based on the current system time. The filename is
7353 * granular to the second so this will work so long as no more than one snapshot is taken on
7354 * a machine per second.
7355 *
7356 * Before version 4.1, we used this formula for saved state files:
7357 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7358 * which no longer works because saved state files can now be shared between the saved state of the
7359 * "saved" machine and an online snapshot, and the following would cause problems:
7360 * 1) save machine
7361 * 2) create online snapshot from that machine state --> reusing saved state file
7362 * 3) save machine again --> filename would be reused, breaking the online snapshot
7363 *
7364 * So instead we now use a timestamp.
7365 *
7366 * @param strStateFilePath
7367 */
7368
7369void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7370{
7371 AutoCaller autoCaller(this);
7372 AssertComRCReturnVoid(autoCaller.rc());
7373
7374 {
7375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7376 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7377 }
7378
7379 RTTIMESPEC ts;
7380 RTTimeNow(&ts);
7381 RTTIME time;
7382 RTTimeExplode(&time, &ts);
7383
7384 strStateFilePath += RTPATH_DELIMITER;
7385 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7386 time.i32Year, time.u8Month, time.u8MonthDay,
7387 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7388}
7389
7390/**
7391 * Returns whether at least one USB controller is present for the VM.
7392 */
7393bool Machine::i_isUSBControllerPresent()
7394{
7395 AutoCaller autoCaller(this);
7396 AssertComRCReturn(autoCaller.rc(), false);
7397
7398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7399
7400 return (mUSBControllers->size() > 0);
7401}
7402
7403/**
7404 * @note Locks this object for writing, calls the client process
7405 * (inside the lock).
7406 */
7407HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7408 const Utf8Str &strFrontend,
7409 const Utf8Str &strEnvironment,
7410 ProgressProxy *aProgress)
7411{
7412 LogFlowThisFuncEnter();
7413
7414 AssertReturn(aControl, E_FAIL);
7415 AssertReturn(aProgress, E_FAIL);
7416 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7417
7418 AutoCaller autoCaller(this);
7419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7420
7421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7422
7423 if (!mData->mRegistered)
7424 return setError(E_UNEXPECTED,
7425 tr("The machine '%s' is not registered"),
7426 mUserData->s.strName.c_str());
7427
7428 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7429
7430 /* The process started when launching a VM with separate UI/VM processes is always
7431 * the UI process, i.e. needs special handling as it won't claim the session. */
7432 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7433
7434 if (fSeparate)
7435 {
7436 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7437 return setError(VBOX_E_INVALID_OBJECT_STATE,
7438 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7439 mUserData->s.strName.c_str());
7440 }
7441 else
7442 {
7443 if ( mData->mSession.mState == SessionState_Locked
7444 || mData->mSession.mState == SessionState_Spawning
7445 || mData->mSession.mState == SessionState_Unlocking)
7446 return setError(VBOX_E_INVALID_OBJECT_STATE,
7447 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7448 mUserData->s.strName.c_str());
7449
7450 /* may not be busy */
7451 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7452 }
7453
7454 /* get the path to the executable */
7455 char szPath[RTPATH_MAX];
7456 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7457 size_t cchBufLeft = strlen(szPath);
7458 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7459 szPath[cchBufLeft] = 0;
7460 char *pszNamePart = szPath + cchBufLeft;
7461 cchBufLeft = sizeof(szPath) - cchBufLeft;
7462
7463 int vrc = VINF_SUCCESS;
7464 RTPROCESS pid = NIL_RTPROCESS;
7465
7466 RTENV env = RTENV_DEFAULT;
7467
7468 if (!strEnvironment.isEmpty())
7469 {
7470 char *newEnvStr = NULL;
7471
7472 do
7473 {
7474 /* clone the current environment */
7475 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7476 AssertRCBreakStmt(vrc2, vrc = vrc2);
7477
7478 newEnvStr = RTStrDup(strEnvironment.c_str());
7479 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7480
7481 /* put new variables to the environment
7482 * (ignore empty variable names here since RTEnv API
7483 * intentionally doesn't do that) */
7484 char *var = newEnvStr;
7485 for (char *p = newEnvStr; *p; ++p)
7486 {
7487 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7488 {
7489 *p = '\0';
7490 if (*var)
7491 {
7492 char *val = strchr(var, '=');
7493 if (val)
7494 {
7495 *val++ = '\0';
7496 vrc2 = RTEnvSetEx(env, var, val);
7497 }
7498 else
7499 vrc2 = RTEnvUnsetEx(env, var);
7500 if (RT_FAILURE(vrc2))
7501 break;
7502 }
7503 var = p + 1;
7504 }
7505 }
7506 if (RT_SUCCESS(vrc2) && *var)
7507 vrc2 = RTEnvPutEx(env, var);
7508
7509 AssertRCBreakStmt(vrc2, vrc = vrc2);
7510 }
7511 while (0);
7512
7513 if (newEnvStr != NULL)
7514 RTStrFree(newEnvStr);
7515 }
7516
7517 /* Hardening logging */
7518#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7519 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7520 {
7521 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7522 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7523 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7524 {
7525 Utf8Str strStartupLogDir = strHardeningLogFile;
7526 strStartupLogDir.stripFilename();
7527 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7528 file without stripping the file. */
7529 }
7530 strSupHardeningLogArg.append(strHardeningLogFile);
7531
7532 /* Remove legacy log filename to avoid confusion. */
7533 Utf8Str strOldStartupLogFile;
7534 getLogFolder(strOldStartupLogFile);
7535 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7536 RTFileDelete(strOldStartupLogFile.c_str());
7537 }
7538 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7539#else
7540 const char *pszSupHardeningLogArg = NULL;
7541#endif
7542
7543 Utf8Str strCanonicalName;
7544
7545#ifdef VBOX_WITH_QTGUI
7546 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7547 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7548 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7549 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7550 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7551 {
7552 strCanonicalName = "GUI/Qt";
7553# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7554 /* Modify the base path so that we don't need to use ".." below. */
7555 RTPathStripTrailingSlash(szPath);
7556 RTPathStripFilename(szPath);
7557 cchBufLeft = strlen(szPath);
7558 pszNamePart = szPath + cchBufLeft;
7559 cchBufLeft = sizeof(szPath) - cchBufLeft;
7560
7561# define OSX_APP_NAME "VirtualBoxVM"
7562# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7563
7564 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7565 if ( strAppOverride.contains(".")
7566 || strAppOverride.contains("/")
7567 || strAppOverride.contains("\\")
7568 || strAppOverride.contains(":"))
7569 strAppOverride.setNull();
7570 Utf8Str strAppPath;
7571 if (!strAppOverride.isEmpty())
7572 {
7573 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7574 Utf8Str strFullPath(szPath);
7575 strFullPath.append(strAppPath);
7576 /* there is a race, but people using this deserve the failure */
7577 if (!RTFileExists(strFullPath.c_str()))
7578 strAppOverride.setNull();
7579 }
7580 if (strAppOverride.isEmpty())
7581 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7582 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7583 strcpy(pszNamePart, strAppPath.c_str());
7584# else
7585# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7586 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7587# else
7588 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7589# endif
7590 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7591 strcpy(pszNamePart, s_szVirtualBox_exe);
7592# endif
7593
7594 Utf8Str idStr = mData->mUuid.toString();
7595 const char *apszArgs[] =
7596 {
7597 szPath,
7598 "--comment", mUserData->s.strName.c_str(),
7599 "--startvm", idStr.c_str(),
7600 "--no-startvm-errormsgbox",
7601 NULL, /* For "--separate". */
7602 NULL, /* For "--sup-startup-log". */
7603 NULL
7604 };
7605 unsigned iArg = 6;
7606 if (fSeparate)
7607 apszArgs[iArg++] = "--separate";
7608 apszArgs[iArg++] = pszSupHardeningLogArg;
7609
7610 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7611 }
7612#else /* !VBOX_WITH_QTGUI */
7613 if (0)
7614 ;
7615#endif /* VBOX_WITH_QTGUI */
7616
7617 else
7618
7619#ifdef VBOX_WITH_VBOXSDL
7620 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7621 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7622 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7623 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7624 {
7625 strCanonicalName = "GUI/SDL";
7626 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7627 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7628 strcpy(pszNamePart, s_szVBoxSDL_exe);
7629
7630 Utf8Str idStr = mData->mUuid.toString();
7631 const char *apszArgs[] =
7632 {
7633 szPath,
7634 "--comment", mUserData->s.strName.c_str(),
7635 "--startvm", idStr.c_str(),
7636 NULL, /* For "--separate". */
7637 NULL, /* For "--sup-startup-log". */
7638 NULL
7639 };
7640 unsigned iArg = 5;
7641 if (fSeparate)
7642 apszArgs[iArg++] = "--separate";
7643 apszArgs[iArg++] = pszSupHardeningLogArg;
7644
7645 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7646 }
7647#else /* !VBOX_WITH_VBOXSDL */
7648 if (0)
7649 ;
7650#endif /* !VBOX_WITH_VBOXSDL */
7651
7652 else
7653
7654#ifdef VBOX_WITH_HEADLESS
7655 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7656 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7657 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7658 )
7659 {
7660 strCanonicalName = "headless";
7661 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7662 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7663 * and a VM works even if the server has not been installed.
7664 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7665 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7666 * differently in 4.0 and 3.x.
7667 */
7668 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7669 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7670 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7671
7672 Utf8Str idStr = mData->mUuid.toString();
7673 const char *apszArgs[] =
7674 {
7675 szPath,
7676 "--comment", mUserData->s.strName.c_str(),
7677 "--startvm", idStr.c_str(),
7678 "--vrde", "config",
7679 NULL, /* For "--capture". */
7680 NULL, /* For "--sup-startup-log". */
7681 NULL
7682 };
7683 unsigned iArg = 7;
7684 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7685 apszArgs[iArg++] = "--capture";
7686 apszArgs[iArg++] = pszSupHardeningLogArg;
7687
7688# ifdef RT_OS_WINDOWS
7689 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7690# else
7691 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7692# endif
7693 }
7694#else /* !VBOX_WITH_HEADLESS */
7695 if (0)
7696 ;
7697#endif /* !VBOX_WITH_HEADLESS */
7698 else
7699 {
7700 RTEnvDestroy(env);
7701 return setError(E_INVALIDARG,
7702 tr("Invalid frontend name: '%s'"),
7703 strFrontend.c_str());
7704 }
7705
7706 RTEnvDestroy(env);
7707
7708 if (RT_FAILURE(vrc))
7709 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7710 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7711 mUserData->s.strName.c_str(), vrc);
7712
7713 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7714
7715 if (!fSeparate)
7716 {
7717 /*
7718 * Note that we don't release the lock here before calling the client,
7719 * because it doesn't need to call us back if called with a NULL argument.
7720 * Releasing the lock here is dangerous because we didn't prepare the
7721 * launch data yet, but the client we've just started may happen to be
7722 * too fast and call LockMachine() that will fail (because of PID, etc.),
7723 * so that the Machine will never get out of the Spawning session state.
7724 */
7725
7726 /* inform the session that it will be a remote one */
7727 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7728#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7729 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7730#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7731 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7732#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7733 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7734
7735 if (FAILED(rc))
7736 {
7737 /* restore the session state */
7738 mData->mSession.mState = SessionState_Unlocked;
7739 alock.release();
7740 mParent->i_addProcessToReap(pid);
7741 /* The failure may occur w/o any error info (from RPC), so provide one */
7742 return setError(VBOX_E_VM_ERROR,
7743 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7744 }
7745
7746 /* attach launch data to the machine */
7747 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7748 mData->mSession.mRemoteControls.push_back(aControl);
7749 mData->mSession.mProgress = aProgress;
7750 mData->mSession.mPID = pid;
7751 mData->mSession.mState = SessionState_Spawning;
7752 Assert(strCanonicalName.isNotEmpty());
7753 mData->mSession.mName = strCanonicalName;
7754 }
7755 else
7756 {
7757 /* For separate UI process we declare the launch as completed instantly, as the
7758 * actual headless VM start may or may not come. No point in remembering anything
7759 * yet, as what matters for us is when the headless VM gets started. */
7760 aProgress->i_notifyComplete(S_OK);
7761 }
7762
7763 alock.release();
7764 mParent->i_addProcessToReap(pid);
7765
7766 LogFlowThisFuncLeave();
7767 return S_OK;
7768}
7769
7770/**
7771 * Returns @c true if the given session machine instance has an open direct
7772 * session (and optionally also for direct sessions which are closing) and
7773 * returns the session control machine instance if so.
7774 *
7775 * Note that when the method returns @c false, the arguments remain unchanged.
7776 *
7777 * @param aMachine Session machine object.
7778 * @param aControl Direct session control object (optional).
7779 * @param aRequireVM If true then only allow VM sessions.
7780 * @param aAllowClosing If true then additionally a session which is currently
7781 * being closed will also be allowed.
7782 *
7783 * @note locks this object for reading.
7784 */
7785bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7786 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7787 bool aRequireVM /*= false*/,
7788 bool aAllowClosing /*= false*/)
7789{
7790 AutoLimitedCaller autoCaller(this);
7791 AssertComRCReturn(autoCaller.rc(), false);
7792
7793 /* just return false for inaccessible machines */
7794 if (getObjectState().getState() != ObjectState::Ready)
7795 return false;
7796
7797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7798
7799 if ( ( mData->mSession.mState == SessionState_Locked
7800 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7801 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7802 )
7803 {
7804 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7805
7806 aMachine = mData->mSession.mMachine;
7807
7808 if (aControl != NULL)
7809 *aControl = mData->mSession.mDirectControl;
7810
7811 return true;
7812 }
7813
7814 return false;
7815}
7816
7817/**
7818 * Returns @c true if the given machine has an spawning direct session.
7819 *
7820 * @note locks this object for reading.
7821 */
7822bool Machine::i_isSessionSpawning()
7823{
7824 AutoLimitedCaller autoCaller(this);
7825 AssertComRCReturn(autoCaller.rc(), false);
7826
7827 /* just return false for inaccessible machines */
7828 if (getObjectState().getState() != ObjectState::Ready)
7829 return false;
7830
7831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7832
7833 if (mData->mSession.mState == SessionState_Spawning)
7834 return true;
7835
7836 return false;
7837}
7838
7839/**
7840 * Called from the client watcher thread to check for unexpected client process
7841 * death during Session_Spawning state (e.g. before it successfully opened a
7842 * direct session).
7843 *
7844 * On Win32 and on OS/2, this method is called only when we've got the
7845 * direct client's process termination notification, so it always returns @c
7846 * true.
7847 *
7848 * On other platforms, this method returns @c true if the client process is
7849 * terminated and @c false if it's still alive.
7850 *
7851 * @note Locks this object for writing.
7852 */
7853bool Machine::i_checkForSpawnFailure()
7854{
7855 AutoCaller autoCaller(this);
7856 if (!autoCaller.isOk())
7857 {
7858 /* nothing to do */
7859 LogFlowThisFunc(("Already uninitialized!\n"));
7860 return true;
7861 }
7862
7863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7864
7865 if (mData->mSession.mState != SessionState_Spawning)
7866 {
7867 /* nothing to do */
7868 LogFlowThisFunc(("Not spawning any more!\n"));
7869 return true;
7870 }
7871
7872 HRESULT rc = S_OK;
7873
7874 /* PID not yet initialized, skip check. */
7875 if (mData->mSession.mPID == NIL_RTPROCESS)
7876 return false;
7877
7878 RTPROCSTATUS status;
7879 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7880
7881 if (vrc != VERR_PROCESS_RUNNING)
7882 {
7883 Utf8Str strExtraInfo;
7884
7885#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7886 /* If the startup logfile exists and is of non-zero length, tell the
7887 user to look there for more details to encourage them to attach it
7888 when reporting startup issues. */
7889 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7890 uint64_t cbStartupLogFile = 0;
7891 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7892 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7893 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7894#endif
7895
7896 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7897 rc = setError(E_FAIL,
7898 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7899 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7900 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7901 rc = setError(E_FAIL,
7902 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7903 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7904 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7905 rc = setError(E_FAIL,
7906 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7907 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7908 else
7909 rc = setErrorBoth(E_FAIL, vrc,
7910 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7911 i_getName().c_str(), vrc, strExtraInfo.c_str());
7912 }
7913
7914 if (FAILED(rc))
7915 {
7916 /* Close the remote session, remove the remote control from the list
7917 * and reset session state to Closed (@note keep the code in sync with
7918 * the relevant part in LockMachine()). */
7919
7920 Assert(mData->mSession.mRemoteControls.size() == 1);
7921 if (mData->mSession.mRemoteControls.size() == 1)
7922 {
7923 ErrorInfoKeeper eik;
7924 mData->mSession.mRemoteControls.front()->Uninitialize();
7925 }
7926
7927 mData->mSession.mRemoteControls.clear();
7928 mData->mSession.mState = SessionState_Unlocked;
7929
7930 /* finalize the progress after setting the state */
7931 if (!mData->mSession.mProgress.isNull())
7932 {
7933 mData->mSession.mProgress->notifyComplete(rc);
7934 mData->mSession.mProgress.setNull();
7935 }
7936
7937 mData->mSession.mPID = NIL_RTPROCESS;
7938
7939 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7940 return true;
7941 }
7942
7943 return false;
7944}
7945
7946/**
7947 * Checks whether the machine can be registered. If so, commits and saves
7948 * all settings.
7949 *
7950 * @note Must be called from mParent's write lock. Locks this object and
7951 * children for writing.
7952 */
7953HRESULT Machine::i_prepareRegister()
7954{
7955 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7956
7957 AutoLimitedCaller autoCaller(this);
7958 AssertComRCReturnRC(autoCaller.rc());
7959
7960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7961
7962 /* wait for state dependents to drop to zero */
7963 i_ensureNoStateDependencies();
7964
7965 if (!mData->mAccessible)
7966 return setError(VBOX_E_INVALID_OBJECT_STATE,
7967 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7968 mUserData->s.strName.c_str(),
7969 mData->mUuid.toString().c_str());
7970
7971 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7972
7973 if (mData->mRegistered)
7974 return setError(VBOX_E_INVALID_OBJECT_STATE,
7975 tr("The machine '%s' with UUID {%s} is already registered"),
7976 mUserData->s.strName.c_str(),
7977 mData->mUuid.toString().c_str());
7978
7979 HRESULT rc = S_OK;
7980
7981 // Ensure the settings are saved. If we are going to be registered and
7982 // no config file exists yet, create it by calling i_saveSettings() too.
7983 if ( (mData->flModifications)
7984 || (!mData->pMachineConfigFile->fileExists())
7985 )
7986 {
7987 rc = i_saveSettings(NULL);
7988 // no need to check whether VirtualBox.xml needs saving too since
7989 // we can't have a machine XML file rename pending
7990 if (FAILED(rc)) return rc;
7991 }
7992
7993 /* more config checking goes here */
7994
7995 if (SUCCEEDED(rc))
7996 {
7997 /* we may have had implicit modifications we want to fix on success */
7998 i_commit();
7999
8000 mData->mRegistered = true;
8001 }
8002 else
8003 {
8004 /* we may have had implicit modifications we want to cancel on failure*/
8005 i_rollback(false /* aNotify */);
8006 }
8007
8008 return rc;
8009}
8010
8011/**
8012 * Increases the number of objects dependent on the machine state or on the
8013 * registered state. Guarantees that these two states will not change at least
8014 * until #i_releaseStateDependency() is called.
8015 *
8016 * Depending on the @a aDepType value, additional state checks may be made.
8017 * These checks will set extended error info on failure. See
8018 * #i_checkStateDependency() for more info.
8019 *
8020 * If this method returns a failure, the dependency is not added and the caller
8021 * is not allowed to rely on any particular machine state or registration state
8022 * value and may return the failed result code to the upper level.
8023 *
8024 * @param aDepType Dependency type to add.
8025 * @param aState Current machine state (NULL if not interested).
8026 * @param aRegistered Current registered state (NULL if not interested).
8027 *
8028 * @note Locks this object for writing.
8029 */
8030HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8031 MachineState_T *aState /* = NULL */,
8032 BOOL *aRegistered /* = NULL */)
8033{
8034 AutoCaller autoCaller(this);
8035 AssertComRCReturnRC(autoCaller.rc());
8036
8037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8038
8039 HRESULT rc = i_checkStateDependency(aDepType);
8040 if (FAILED(rc)) return rc;
8041
8042 {
8043 if (mData->mMachineStateChangePending != 0)
8044 {
8045 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8046 * drop to zero so don't add more. It may make sense to wait a bit
8047 * and retry before reporting an error (since the pending state
8048 * transition should be really quick) but let's just assert for
8049 * now to see if it ever happens on practice. */
8050
8051 AssertFailed();
8052
8053 return setError(E_ACCESSDENIED,
8054 tr("Machine state change is in progress. Please retry the operation later."));
8055 }
8056
8057 ++mData->mMachineStateDeps;
8058 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8059 }
8060
8061 if (aState)
8062 *aState = mData->mMachineState;
8063 if (aRegistered)
8064 *aRegistered = mData->mRegistered;
8065
8066 return S_OK;
8067}
8068
8069/**
8070 * Decreases the number of objects dependent on the machine state.
8071 * Must always complete the #i_addStateDependency() call after the state
8072 * dependency is no more necessary.
8073 */
8074void Machine::i_releaseStateDependency()
8075{
8076 AutoCaller autoCaller(this);
8077 AssertComRCReturnVoid(autoCaller.rc());
8078
8079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8080
8081 /* releaseStateDependency() w/o addStateDependency()? */
8082 AssertReturnVoid(mData->mMachineStateDeps != 0);
8083 -- mData->mMachineStateDeps;
8084
8085 if (mData->mMachineStateDeps == 0)
8086 {
8087 /* inform i_ensureNoStateDependencies() that there are no more deps */
8088 if (mData->mMachineStateChangePending != 0)
8089 {
8090 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8091 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8092 }
8093 }
8094}
8095
8096Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8097{
8098 /* start with nothing found */
8099 Utf8Str strResult("");
8100
8101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8102
8103 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8104 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8105 // found:
8106 strResult = it->second; // source is a Utf8Str
8107
8108 return strResult;
8109}
8110
8111// protected methods
8112/////////////////////////////////////////////////////////////////////////////
8113
8114/**
8115 * Performs machine state checks based on the @a aDepType value. If a check
8116 * fails, this method will set extended error info, otherwise it will return
8117 * S_OK. It is supposed, that on failure, the caller will immediately return
8118 * the return value of this method to the upper level.
8119 *
8120 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8121 *
8122 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8123 * current state of this machine object allows to change settings of the
8124 * machine (i.e. the machine is not registered, or registered but not running
8125 * and not saved). It is useful to call this method from Machine setters
8126 * before performing any change.
8127 *
8128 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8129 * as for MutableStateDep except that if the machine is saved, S_OK is also
8130 * returned. This is useful in setters which allow changing machine
8131 * properties when it is in the saved state.
8132 *
8133 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8134 * if the current state of this machine object allows to change runtime
8135 * changeable settings of the machine (i.e. the machine is not registered, or
8136 * registered but either running or not running and not saved). It is useful
8137 * to call this method from Machine setters before performing any changes to
8138 * runtime changeable settings.
8139 *
8140 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8141 * the same as for MutableOrRunningStateDep except that if the machine is
8142 * saved, S_OK is also returned. This is useful in setters which allow
8143 * changing runtime and saved state changeable machine properties.
8144 *
8145 * @param aDepType Dependency type to check.
8146 *
8147 * @note Non Machine based classes should use #i_addStateDependency() and
8148 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8149 * template.
8150 *
8151 * @note This method must be called from under this object's read or write
8152 * lock.
8153 */
8154HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8155{
8156 switch (aDepType)
8157 {
8158 case AnyStateDep:
8159 {
8160 break;
8161 }
8162 case MutableStateDep:
8163 {
8164 if ( mData->mRegistered
8165 && ( !i_isSessionMachine()
8166 || ( mData->mMachineState != MachineState_Aborted
8167 && mData->mMachineState != MachineState_Teleported
8168 && mData->mMachineState != MachineState_PoweredOff
8169 )
8170 )
8171 )
8172 return setError(VBOX_E_INVALID_VM_STATE,
8173 tr("The machine is not mutable (state is %s)"),
8174 Global::stringifyMachineState(mData->mMachineState));
8175 break;
8176 }
8177 case MutableOrSavedStateDep:
8178 {
8179 if ( mData->mRegistered
8180 && ( !i_isSessionMachine()
8181 || ( mData->mMachineState != MachineState_Aborted
8182 && mData->mMachineState != MachineState_Teleported
8183 && mData->mMachineState != MachineState_Saved
8184 && mData->mMachineState != MachineState_PoweredOff
8185 )
8186 )
8187 )
8188 return setError(VBOX_E_INVALID_VM_STATE,
8189 tr("The machine is not mutable or saved (state is %s)"),
8190 Global::stringifyMachineState(mData->mMachineState));
8191 break;
8192 }
8193 case MutableOrRunningStateDep:
8194 {
8195 if ( mData->mRegistered
8196 && ( !i_isSessionMachine()
8197 || ( mData->mMachineState != MachineState_Aborted
8198 && mData->mMachineState != MachineState_Teleported
8199 && mData->mMachineState != MachineState_PoweredOff
8200 && !Global::IsOnline(mData->mMachineState)
8201 )
8202 )
8203 )
8204 return setError(VBOX_E_INVALID_VM_STATE,
8205 tr("The machine is not mutable or running (state is %s)"),
8206 Global::stringifyMachineState(mData->mMachineState));
8207 break;
8208 }
8209 case MutableOrSavedOrRunningStateDep:
8210 {
8211 if ( mData->mRegistered
8212 && ( !i_isSessionMachine()
8213 || ( mData->mMachineState != MachineState_Aborted
8214 && mData->mMachineState != MachineState_Teleported
8215 && mData->mMachineState != MachineState_Saved
8216 && mData->mMachineState != MachineState_PoweredOff
8217 && !Global::IsOnline(mData->mMachineState)
8218 )
8219 )
8220 )
8221 return setError(VBOX_E_INVALID_VM_STATE,
8222 tr("The machine is not mutable, saved or running (state is %s)"),
8223 Global::stringifyMachineState(mData->mMachineState));
8224 break;
8225 }
8226 }
8227
8228 return S_OK;
8229}
8230
8231/**
8232 * Helper to initialize all associated child objects and allocate data
8233 * structures.
8234 *
8235 * This method must be called as a part of the object's initialization procedure
8236 * (usually done in the #init() method).
8237 *
8238 * @note Must be called only from #init() or from #i_registeredInit().
8239 */
8240HRESULT Machine::initDataAndChildObjects()
8241{
8242 AutoCaller autoCaller(this);
8243 AssertComRCReturnRC(autoCaller.rc());
8244 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8245 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8246
8247 AssertReturn(!mData->mAccessible, E_FAIL);
8248
8249 /* allocate data structures */
8250 mSSData.allocate();
8251 mUserData.allocate();
8252 mHWData.allocate();
8253 mMediumAttachments.allocate();
8254 mStorageControllers.allocate();
8255 mUSBControllers.allocate();
8256
8257 /* initialize mOSTypeId */
8258 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8259
8260/** @todo r=bird: init() methods never fails, right? Why don't we make them
8261 * return void then! */
8262
8263 /* create associated BIOS settings object */
8264 unconst(mBIOSSettings).createObject();
8265 mBIOSSettings->init(this);
8266
8267 /* create associated record settings object */
8268 unconst(mRecordingSettings).createObject();
8269 mRecordingSettings->init(this);
8270
8271 /* create an associated VRDE object (default is disabled) */
8272 unconst(mVRDEServer).createObject();
8273 mVRDEServer->init(this);
8274
8275 /* create associated serial port objects */
8276 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8277 {
8278 unconst(mSerialPorts[slot]).createObject();
8279 mSerialPorts[slot]->init(this, slot);
8280 }
8281
8282 /* create associated parallel port objects */
8283 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8284 {
8285 unconst(mParallelPorts[slot]).createObject();
8286 mParallelPorts[slot]->init(this, slot);
8287 }
8288
8289 /* create the audio adapter object (always present, default is disabled) */
8290 unconst(mAudioAdapter).createObject();
8291 mAudioAdapter->init(this);
8292
8293 /* create the USB device filters object (always present) */
8294 unconst(mUSBDeviceFilters).createObject();
8295 mUSBDeviceFilters->init(this);
8296
8297 /* create associated network adapter objects */
8298 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8299 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8300 {
8301 unconst(mNetworkAdapters[slot]).createObject();
8302 mNetworkAdapters[slot]->init(this, slot);
8303 }
8304
8305 /* create the bandwidth control */
8306 unconst(mBandwidthControl).createObject();
8307 mBandwidthControl->init(this);
8308
8309 return S_OK;
8310}
8311
8312/**
8313 * Helper to uninitialize all associated child objects and to free all data
8314 * structures.
8315 *
8316 * This method must be called as a part of the object's uninitialization
8317 * procedure (usually done in the #uninit() method).
8318 *
8319 * @note Must be called only from #uninit() or from #i_registeredInit().
8320 */
8321void Machine::uninitDataAndChildObjects()
8322{
8323 AutoCaller autoCaller(this);
8324 AssertComRCReturnVoid(autoCaller.rc());
8325 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8326 || getObjectState().getState() == ObjectState::Limited);
8327
8328 /* tell all our other child objects we've been uninitialized */
8329 if (mBandwidthControl)
8330 {
8331 mBandwidthControl->uninit();
8332 unconst(mBandwidthControl).setNull();
8333 }
8334
8335 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8336 {
8337 if (mNetworkAdapters[slot])
8338 {
8339 mNetworkAdapters[slot]->uninit();
8340 unconst(mNetworkAdapters[slot]).setNull();
8341 }
8342 }
8343
8344 if (mUSBDeviceFilters)
8345 {
8346 mUSBDeviceFilters->uninit();
8347 unconst(mUSBDeviceFilters).setNull();
8348 }
8349
8350 if (mAudioAdapter)
8351 {
8352 mAudioAdapter->uninit();
8353 unconst(mAudioAdapter).setNull();
8354 }
8355
8356 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8357 {
8358 if (mParallelPorts[slot])
8359 {
8360 mParallelPorts[slot]->uninit();
8361 unconst(mParallelPorts[slot]).setNull();
8362 }
8363 }
8364
8365 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8366 {
8367 if (mSerialPorts[slot])
8368 {
8369 mSerialPorts[slot]->uninit();
8370 unconst(mSerialPorts[slot]).setNull();
8371 }
8372 }
8373
8374 if (mVRDEServer)
8375 {
8376 mVRDEServer->uninit();
8377 unconst(mVRDEServer).setNull();
8378 }
8379
8380 if (mBIOSSettings)
8381 {
8382 mBIOSSettings->uninit();
8383 unconst(mBIOSSettings).setNull();
8384 }
8385
8386 if (mRecordingSettings)
8387 {
8388 mRecordingSettings->uninit();
8389 unconst(mRecordingSettings).setNull();
8390 }
8391
8392 /* Deassociate media (only when a real Machine or a SnapshotMachine
8393 * instance is uninitialized; SessionMachine instances refer to real
8394 * Machine media). This is necessary for a clean re-initialization of
8395 * the VM after successfully re-checking the accessibility state. Note
8396 * that in case of normal Machine or SnapshotMachine uninitialization (as
8397 * a result of unregistering or deleting the snapshot), outdated media
8398 * attachments will already be uninitialized and deleted, so this
8399 * code will not affect them. */
8400 if ( !mMediumAttachments.isNull()
8401 && !i_isSessionMachine()
8402 )
8403 {
8404 for (MediumAttachmentList::const_iterator
8405 it = mMediumAttachments->begin();
8406 it != mMediumAttachments->end();
8407 ++it)
8408 {
8409 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8410 if (pMedium.isNull())
8411 continue;
8412 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8413 AssertComRC(rc);
8414 }
8415 }
8416
8417 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8418 {
8419 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8420 if (mData->mFirstSnapshot)
8421 {
8422 // snapshots tree is protected by machine write lock; strictly
8423 // this isn't necessary here since we're deleting the entire
8424 // machine, but otherwise we assert in Snapshot::uninit()
8425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8426 mData->mFirstSnapshot->uninit();
8427 mData->mFirstSnapshot.setNull();
8428 }
8429
8430 mData->mCurrentSnapshot.setNull();
8431 }
8432
8433 /* free data structures (the essential mData structure is not freed here
8434 * since it may be still in use) */
8435 mMediumAttachments.free();
8436 mStorageControllers.free();
8437 mUSBControllers.free();
8438 mHWData.free();
8439 mUserData.free();
8440 mSSData.free();
8441}
8442
8443/**
8444 * Returns a pointer to the Machine object for this machine that acts like a
8445 * parent for complex machine data objects such as shared folders, etc.
8446 *
8447 * For primary Machine objects and for SnapshotMachine objects, returns this
8448 * object's pointer itself. For SessionMachine objects, returns the peer
8449 * (primary) machine pointer.
8450 */
8451Machine *Machine::i_getMachine()
8452{
8453 if (i_isSessionMachine())
8454 return (Machine*)mPeer;
8455 return this;
8456}
8457
8458/**
8459 * Makes sure that there are no machine state dependents. If necessary, waits
8460 * for the number of dependents to drop to zero.
8461 *
8462 * Make sure this method is called from under this object's write lock to
8463 * guarantee that no new dependents may be added when this method returns
8464 * control to the caller.
8465 *
8466 * @note Locks this object for writing. The lock will be released while waiting
8467 * (if necessary).
8468 *
8469 * @warning To be used only in methods that change the machine state!
8470 */
8471void Machine::i_ensureNoStateDependencies()
8472{
8473 AssertReturnVoid(isWriteLockOnCurrentThread());
8474
8475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8476
8477 /* Wait for all state dependents if necessary */
8478 if (mData->mMachineStateDeps != 0)
8479 {
8480 /* lazy semaphore creation */
8481 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8482 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8483
8484 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8485 mData->mMachineStateDeps));
8486
8487 ++mData->mMachineStateChangePending;
8488
8489 /* reset the semaphore before waiting, the last dependent will signal
8490 * it */
8491 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8492
8493 alock.release();
8494
8495 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8496
8497 alock.acquire();
8498
8499 -- mData->mMachineStateChangePending;
8500 }
8501}
8502
8503/**
8504 * Changes the machine state and informs callbacks.
8505 *
8506 * This method is not intended to fail so it either returns S_OK or asserts (and
8507 * returns a failure).
8508 *
8509 * @note Locks this object for writing.
8510 */
8511HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8512{
8513 LogFlowThisFuncEnter();
8514 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8515 Assert(aMachineState != MachineState_Null);
8516
8517 AutoCaller autoCaller(this);
8518 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8519
8520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8521
8522 /* wait for state dependents to drop to zero */
8523 i_ensureNoStateDependencies();
8524
8525 MachineState_T const enmOldState = mData->mMachineState;
8526 if (enmOldState != aMachineState)
8527 {
8528 mData->mMachineState = aMachineState;
8529 RTTimeNow(&mData->mLastStateChange);
8530
8531#ifdef VBOX_WITH_DTRACE_R3_MAIN
8532 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8533#endif
8534 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8535 }
8536
8537 LogFlowThisFuncLeave();
8538 return S_OK;
8539}
8540
8541/**
8542 * Searches for a shared folder with the given logical name
8543 * in the collection of shared folders.
8544 *
8545 * @param aName logical name of the shared folder
8546 * @param aSharedFolder where to return the found object
8547 * @param aSetError whether to set the error info if the folder is
8548 * not found
8549 * @return
8550 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8551 *
8552 * @note
8553 * must be called from under the object's lock!
8554 */
8555HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8556 ComObjPtr<SharedFolder> &aSharedFolder,
8557 bool aSetError /* = false */)
8558{
8559 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8560 for (HWData::SharedFolderList::const_iterator
8561 it = mHWData->mSharedFolders.begin();
8562 it != mHWData->mSharedFolders.end();
8563 ++it)
8564 {
8565 SharedFolder *pSF = *it;
8566 AutoCaller autoCaller(pSF);
8567 if (pSF->i_getName() == aName)
8568 {
8569 aSharedFolder = pSF;
8570 rc = S_OK;
8571 break;
8572 }
8573 }
8574
8575 if (aSetError && FAILED(rc))
8576 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8577
8578 return rc;
8579}
8580
8581/**
8582 * Initializes all machine instance data from the given settings structures
8583 * from XML. The exception is the machine UUID which needs special handling
8584 * depending on the caller's use case, so the caller needs to set that herself.
8585 *
8586 * This gets called in several contexts during machine initialization:
8587 *
8588 * -- When machine XML exists on disk already and needs to be loaded into memory,
8589 * for example, from #i_registeredInit() to load all registered machines on
8590 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8591 * attached to the machine should be part of some media registry already.
8592 *
8593 * -- During OVF import, when a machine config has been constructed from an
8594 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8595 * ensure that the media listed as attachments in the config (which have
8596 * been imported from the OVF) receive the correct registry ID.
8597 *
8598 * -- During VM cloning.
8599 *
8600 * @param config Machine settings from XML.
8601 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8602 * for each attached medium in the config.
8603 * @return
8604 */
8605HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8606 const Guid *puuidRegistry)
8607{
8608 // copy name, description, OS type, teleporter, UTC etc.
8609 mUserData->s = config.machineUserData;
8610
8611 // look up the object by Id to check it is valid
8612 ComObjPtr<GuestOSType> pGuestOSType;
8613 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8614 if (!pGuestOSType.isNull())
8615 mUserData->s.strOsType = pGuestOSType->i_id();
8616
8617 // stateFile (optional)
8618 if (config.strStateFile.isEmpty())
8619 mSSData->strStateFilePath.setNull();
8620 else
8621 {
8622 Utf8Str stateFilePathFull(config.strStateFile);
8623 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8624 if (RT_FAILURE(vrc))
8625 return setErrorBoth(E_FAIL, vrc,
8626 tr("Invalid saved state file path '%s' (%Rrc)"),
8627 config.strStateFile.c_str(),
8628 vrc);
8629 mSSData->strStateFilePath = stateFilePathFull;
8630 }
8631
8632 // snapshot folder needs special processing so set it again
8633 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8634 if (FAILED(rc)) return rc;
8635
8636 /* Copy the extra data items (config may or may not be the same as
8637 * mData->pMachineConfigFile) if necessary. When loading the XML files
8638 * from disk they are the same, but not for OVF import. */
8639 if (mData->pMachineConfigFile != &config)
8640 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8641
8642 /* currentStateModified (optional, default is true) */
8643 mData->mCurrentStateModified = config.fCurrentStateModified;
8644
8645 mData->mLastStateChange = config.timeLastStateChange;
8646
8647 /*
8648 * note: all mUserData members must be assigned prior this point because
8649 * we need to commit changes in order to let mUserData be shared by all
8650 * snapshot machine instances.
8651 */
8652 mUserData.commitCopy();
8653
8654 // machine registry, if present (must be loaded before snapshots)
8655 if (config.canHaveOwnMediaRegistry())
8656 {
8657 // determine machine folder
8658 Utf8Str strMachineFolder = i_getSettingsFileFull();
8659 strMachineFolder.stripFilename();
8660 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8661 config.mediaRegistry,
8662 strMachineFolder);
8663 if (FAILED(rc)) return rc;
8664 }
8665
8666 /* Snapshot node (optional) */
8667 size_t cRootSnapshots;
8668 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8669 {
8670 // there must be only one root snapshot
8671 Assert(cRootSnapshots == 1);
8672
8673 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8674
8675 rc = i_loadSnapshot(snap,
8676 config.uuidCurrentSnapshot,
8677 NULL); // no parent == first snapshot
8678 if (FAILED(rc)) return rc;
8679 }
8680
8681 // hardware data
8682 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8683 if (FAILED(rc)) return rc;
8684
8685 /*
8686 * NOTE: the assignment below must be the last thing to do,
8687 * otherwise it will be not possible to change the settings
8688 * somewhere in the code above because all setters will be
8689 * blocked by i_checkStateDependency(MutableStateDep).
8690 */
8691
8692 /* set the machine state to Aborted or Saved when appropriate */
8693 if (config.fAborted)
8694 {
8695 mSSData->strStateFilePath.setNull();
8696
8697 /* no need to use i_setMachineState() during init() */
8698 mData->mMachineState = MachineState_Aborted;
8699 }
8700 else if (!mSSData->strStateFilePath.isEmpty())
8701 {
8702 /* no need to use i_setMachineState() during init() */
8703 mData->mMachineState = MachineState_Saved;
8704 }
8705
8706 // after loading settings, we are no longer different from the XML on disk
8707 mData->flModifications = 0;
8708
8709 return S_OK;
8710}
8711
8712/**
8713 * Recursively loads all snapshots starting from the given.
8714 *
8715 * @param data snapshot settings.
8716 * @param aCurSnapshotId Current snapshot ID from the settings file.
8717 * @param aParentSnapshot Parent snapshot.
8718 */
8719HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8720 const Guid &aCurSnapshotId,
8721 Snapshot *aParentSnapshot)
8722{
8723 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8724 AssertReturn(!i_isSessionMachine(), E_FAIL);
8725
8726 HRESULT rc = S_OK;
8727
8728 Utf8Str strStateFile;
8729 if (!data.strStateFile.isEmpty())
8730 {
8731 /* optional */
8732 strStateFile = data.strStateFile;
8733 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8734 if (RT_FAILURE(vrc))
8735 return setErrorBoth(E_FAIL, vrc,
8736 tr("Invalid saved state file path '%s' (%Rrc)"),
8737 strStateFile.c_str(),
8738 vrc);
8739 }
8740
8741 /* create a snapshot machine object */
8742 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8743 pSnapshotMachine.createObject();
8744 rc = pSnapshotMachine->initFromSettings(this,
8745 data.hardware,
8746 &data.debugging,
8747 &data.autostart,
8748 data.uuid.ref(),
8749 strStateFile);
8750 if (FAILED(rc)) return rc;
8751
8752 /* create a snapshot object */
8753 ComObjPtr<Snapshot> pSnapshot;
8754 pSnapshot.createObject();
8755 /* initialize the snapshot */
8756 rc = pSnapshot->init(mParent, // VirtualBox object
8757 data.uuid,
8758 data.strName,
8759 data.strDescription,
8760 data.timestamp,
8761 pSnapshotMachine,
8762 aParentSnapshot);
8763 if (FAILED(rc)) return rc;
8764
8765 /* memorize the first snapshot if necessary */
8766 if (!mData->mFirstSnapshot)
8767 mData->mFirstSnapshot = pSnapshot;
8768
8769 /* memorize the current snapshot when appropriate */
8770 if ( !mData->mCurrentSnapshot
8771 && pSnapshot->i_getId() == aCurSnapshotId
8772 )
8773 mData->mCurrentSnapshot = pSnapshot;
8774
8775 // now create the children
8776 for (settings::SnapshotsList::const_iterator
8777 it = data.llChildSnapshots.begin();
8778 it != data.llChildSnapshots.end();
8779 ++it)
8780 {
8781 const settings::Snapshot &childData = *it;
8782 // recurse
8783 rc = i_loadSnapshot(childData,
8784 aCurSnapshotId,
8785 pSnapshot); // parent = the one we created above
8786 if (FAILED(rc)) return rc;
8787 }
8788
8789 return rc;
8790}
8791
8792/**
8793 * Loads settings into mHWData.
8794 *
8795 * @param puuidRegistry Registry ID.
8796 * @param puuidSnapshot Snapshot ID
8797 * @param data Reference to the hardware settings.
8798 * @param pDbg Pointer to the debugging settings.
8799 * @param pAutostart Pointer to the autostart settings.
8800 */
8801HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8802 const Guid *puuidSnapshot,
8803 const settings::Hardware &data,
8804 const settings::Debugging *pDbg,
8805 const settings::Autostart *pAutostart)
8806{
8807 AssertReturn(!i_isSessionMachine(), E_FAIL);
8808
8809 HRESULT rc = S_OK;
8810
8811 try
8812 {
8813 ComObjPtr<GuestOSType> pGuestOSType;
8814 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8815
8816 /* The hardware version attribute (optional). */
8817 mHWData->mHWVersion = data.strVersion;
8818 mHWData->mHardwareUUID = data.uuid;
8819
8820 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8821 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8822 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8823 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8824 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8825 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8826 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8827 mHWData->mPAEEnabled = data.fPAE;
8828 mHWData->mLongMode = data.enmLongMode;
8829 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8830 mHWData->mAPIC = data.fAPIC;
8831 mHWData->mX2APIC = data.fX2APIC;
8832 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8833 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8834 mHWData->mSpecCtrl = data.fSpecCtrl;
8835 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8836 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8837 mHWData->mCPUCount = data.cCPUs;
8838 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8839 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8840 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8841 mHWData->mCpuProfile = data.strCpuProfile;
8842
8843 // cpu
8844 if (mHWData->mCPUHotPlugEnabled)
8845 {
8846 for (settings::CpuList::const_iterator
8847 it = data.llCpus.begin();
8848 it != data.llCpus.end();
8849 ++it)
8850 {
8851 const settings::Cpu &cpu = *it;
8852
8853 mHWData->mCPUAttached[cpu.ulId] = true;
8854 }
8855 }
8856
8857 // cpuid leafs
8858 for (settings::CpuIdLeafsList::const_iterator
8859 it = data.llCpuIdLeafs.begin();
8860 it != data.llCpuIdLeafs.end();
8861 ++it)
8862 {
8863 const settings::CpuIdLeaf &rLeaf= *it;
8864 if ( rLeaf.idx < UINT32_C(0x20)
8865 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8866 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8867 mHWData->mCpuIdLeafList.push_back(rLeaf);
8868 /* else: just ignore */
8869 }
8870
8871 mHWData->mMemorySize = data.ulMemorySizeMB;
8872 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8873
8874 // boot order
8875 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8876 {
8877 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8878 if (it == data.mapBootOrder.end())
8879 mHWData->mBootOrder[i] = DeviceType_Null;
8880 else
8881 mHWData->mBootOrder[i] = it->second;
8882 }
8883
8884 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8885 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8886 mHWData->mMonitorCount = data.cMonitors;
8887 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8888 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8889 mHWData->mFirmwareType = data.firmwareType;
8890 mHWData->mPointingHIDType = data.pointingHIDType;
8891 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8892 mHWData->mChipsetType = data.chipsetType;
8893 mHWData->mParavirtProvider = data.paravirtProvider;
8894 mHWData->mParavirtDebug = data.strParavirtDebug;
8895 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8896 mHWData->mHPETEnabled = data.fHPETEnabled;
8897
8898 /* VRDEServer */
8899 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8900 if (FAILED(rc)) return rc;
8901
8902 /* BIOS */
8903 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8904 if (FAILED(rc)) return rc;
8905
8906 /* Recording settings */
8907 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8908 if (FAILED(rc)) return rc;
8909
8910 // Bandwidth control (must come before network adapters)
8911 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8912 if (FAILED(rc)) return rc;
8913
8914 /* USB controllers */
8915 for (settings::USBControllerList::const_iterator
8916 it = data.usbSettings.llUSBControllers.begin();
8917 it != data.usbSettings.llUSBControllers.end();
8918 ++it)
8919 {
8920 const settings::USBController &settingsCtrl = *it;
8921 ComObjPtr<USBController> newCtrl;
8922
8923 newCtrl.createObject();
8924 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8925 mUSBControllers->push_back(newCtrl);
8926 }
8927
8928 /* USB device filters */
8929 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8930 if (FAILED(rc)) return rc;
8931
8932 // network adapters (establish array size first and apply defaults, to
8933 // ensure reading the same settings as we saved, since the list skips
8934 // adapters having defaults)
8935 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8936 size_t oldCount = mNetworkAdapters.size();
8937 if (newCount > oldCount)
8938 {
8939 mNetworkAdapters.resize(newCount);
8940 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8941 {
8942 unconst(mNetworkAdapters[slot]).createObject();
8943 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8944 }
8945 }
8946 else if (newCount < oldCount)
8947 mNetworkAdapters.resize(newCount);
8948 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8949 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8950 for (settings::NetworkAdaptersList::const_iterator
8951 it = data.llNetworkAdapters.begin();
8952 it != data.llNetworkAdapters.end();
8953 ++it)
8954 {
8955 const settings::NetworkAdapter &nic = *it;
8956
8957 /* slot uniqueness is guaranteed by XML Schema */
8958 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8959 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8960 if (FAILED(rc)) return rc;
8961 }
8962
8963 // serial ports (establish defaults first, to ensure reading the same
8964 // settings as we saved, since the list skips ports having defaults)
8965 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8966 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8967 for (settings::SerialPortsList::const_iterator
8968 it = data.llSerialPorts.begin();
8969 it != data.llSerialPorts.end();
8970 ++it)
8971 {
8972 const settings::SerialPort &s = *it;
8973
8974 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8975 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8976 if (FAILED(rc)) return rc;
8977 }
8978
8979 // parallel ports (establish defaults first, to ensure reading the same
8980 // settings as we saved, since the list skips ports having defaults)
8981 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8982 mParallelPorts[i]->i_applyDefaults();
8983 for (settings::ParallelPortsList::const_iterator
8984 it = data.llParallelPorts.begin();
8985 it != data.llParallelPorts.end();
8986 ++it)
8987 {
8988 const settings::ParallelPort &p = *it;
8989
8990 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8991 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8992 if (FAILED(rc)) return rc;
8993 }
8994
8995 /* AudioAdapter */
8996 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8997 if (FAILED(rc)) return rc;
8998
8999 /* storage controllers */
9000 rc = i_loadStorageControllers(data.storage,
9001 puuidRegistry,
9002 puuidSnapshot);
9003 if (FAILED(rc)) return rc;
9004
9005 /* Shared folders */
9006 for (settings::SharedFoldersList::const_iterator
9007 it = data.llSharedFolders.begin();
9008 it != data.llSharedFolders.end();
9009 ++it)
9010 {
9011 const settings::SharedFolder &sf = *it;
9012
9013 ComObjPtr<SharedFolder> sharedFolder;
9014 /* Check for double entries. Not allowed! */
9015 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9016 if (SUCCEEDED(rc))
9017 return setError(VBOX_E_OBJECT_IN_USE,
9018 tr("Shared folder named '%s' already exists"),
9019 sf.strName.c_str());
9020
9021 /* Create the new shared folder. Don't break on error. This will be
9022 * reported when the machine starts. */
9023 sharedFolder.createObject();
9024 rc = sharedFolder->init(i_getMachine(),
9025 sf.strName,
9026 sf.strHostPath,
9027 RT_BOOL(sf.fWritable),
9028 RT_BOOL(sf.fAutoMount),
9029 sf.strAutoMountPoint,
9030 false /* fFailOnError */);
9031 if (FAILED(rc)) return rc;
9032 mHWData->mSharedFolders.push_back(sharedFolder);
9033 }
9034
9035 // Clipboard
9036 mHWData->mClipboardMode = data.clipboardMode;
9037
9038 // drag'n'drop
9039 mHWData->mDnDMode = data.dndMode;
9040
9041 // guest settings
9042 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9043
9044 // IO settings
9045 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9046 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9047
9048 // Host PCI devices
9049 for (settings::HostPCIDeviceAttachmentList::const_iterator
9050 it = data.pciAttachments.begin();
9051 it != data.pciAttachments.end();
9052 ++it)
9053 {
9054 const settings::HostPCIDeviceAttachment &hpda = *it;
9055 ComObjPtr<PCIDeviceAttachment> pda;
9056
9057 pda.createObject();
9058 pda->i_loadSettings(this, hpda);
9059 mHWData->mPCIDeviceAssignments.push_back(pda);
9060 }
9061
9062 /*
9063 * (The following isn't really real hardware, but it lives in HWData
9064 * for reasons of convenience.)
9065 */
9066
9067#ifdef VBOX_WITH_GUEST_PROPS
9068 /* Guest properties (optional) */
9069
9070 /* Only load transient guest properties for configs which have saved
9071 * state, because there shouldn't be any for powered off VMs. The same
9072 * logic applies for snapshots, as offline snapshots shouldn't have
9073 * any such properties. They confuse the code in various places.
9074 * Note: can't rely on the machine state, as it isn't set yet. */
9075 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9076 /* apologies for the hacky unconst() usage, but this needs hacking
9077 * actually inconsistent settings into consistency, otherwise there
9078 * will be some corner cases where the inconsistency survives
9079 * surprisingly long without getting fixed, especially for snapshots
9080 * as there are no config changes. */
9081 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9082 for (settings::GuestPropertiesList::iterator
9083 it = llGuestProperties.begin();
9084 it != llGuestProperties.end();
9085 /*nothing*/)
9086 {
9087 const settings::GuestProperty &prop = *it;
9088 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9089 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9090 if ( fSkipTransientGuestProperties
9091 && ( fFlags & GUEST_PROP_F_TRANSIENT
9092 || fFlags & GUEST_PROP_F_TRANSRESET))
9093 {
9094 it = llGuestProperties.erase(it);
9095 continue;
9096 }
9097 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9098 mHWData->mGuestProperties[prop.strName] = property;
9099 ++it;
9100 }
9101#endif /* VBOX_WITH_GUEST_PROPS defined */
9102
9103 rc = i_loadDebugging(pDbg);
9104 if (FAILED(rc))
9105 return rc;
9106
9107 mHWData->mAutostart = *pAutostart;
9108
9109 /* default frontend */
9110 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9111 }
9112 catch (std::bad_alloc &)
9113 {
9114 return E_OUTOFMEMORY;
9115 }
9116
9117 AssertComRC(rc);
9118 return rc;
9119}
9120
9121/**
9122 * Called from i_loadHardware() to load the debugging settings of the
9123 * machine.
9124 *
9125 * @param pDbg Pointer to the settings.
9126 */
9127HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9128{
9129 mHWData->mDebugging = *pDbg;
9130 /* no more processing currently required, this will probably change. */
9131 return S_OK;
9132}
9133
9134/**
9135 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9136 *
9137 * @param data storage settings.
9138 * @param puuidRegistry media registry ID to set media to or NULL;
9139 * see Machine::i_loadMachineDataFromSettings()
9140 * @param puuidSnapshot snapshot ID
9141 * @return
9142 */
9143HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9144 const Guid *puuidRegistry,
9145 const Guid *puuidSnapshot)
9146{
9147 AssertReturn(!i_isSessionMachine(), E_FAIL);
9148
9149 HRESULT rc = S_OK;
9150
9151 for (settings::StorageControllersList::const_iterator
9152 it = data.llStorageControllers.begin();
9153 it != data.llStorageControllers.end();
9154 ++it)
9155 {
9156 const settings::StorageController &ctlData = *it;
9157
9158 ComObjPtr<StorageController> pCtl;
9159 /* Try to find one with the name first. */
9160 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9161 if (SUCCEEDED(rc))
9162 return setError(VBOX_E_OBJECT_IN_USE,
9163 tr("Storage controller named '%s' already exists"),
9164 ctlData.strName.c_str());
9165
9166 pCtl.createObject();
9167 rc = pCtl->init(this,
9168 ctlData.strName,
9169 ctlData.storageBus,
9170 ctlData.ulInstance,
9171 ctlData.fBootable);
9172 if (FAILED(rc)) return rc;
9173
9174 mStorageControllers->push_back(pCtl);
9175
9176 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9177 if (FAILED(rc)) return rc;
9178
9179 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9180 if (FAILED(rc)) return rc;
9181
9182 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9183 if (FAILED(rc)) return rc;
9184
9185 /* Load the attached devices now. */
9186 rc = i_loadStorageDevices(pCtl,
9187 ctlData,
9188 puuidRegistry,
9189 puuidSnapshot);
9190 if (FAILED(rc)) return rc;
9191 }
9192
9193 return S_OK;
9194}
9195
9196/**
9197 * Called from i_loadStorageControllers for a controller's devices.
9198 *
9199 * @param aStorageController
9200 * @param data
9201 * @param puuidRegistry media registry ID to set media to or NULL; see
9202 * Machine::i_loadMachineDataFromSettings()
9203 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9204 * @return
9205 */
9206HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9207 const settings::StorageController &data,
9208 const Guid *puuidRegistry,
9209 const Guid *puuidSnapshot)
9210{
9211 HRESULT rc = S_OK;
9212
9213 /* paranoia: detect duplicate attachments */
9214 for (settings::AttachedDevicesList::const_iterator
9215 it = data.llAttachedDevices.begin();
9216 it != data.llAttachedDevices.end();
9217 ++it)
9218 {
9219 const settings::AttachedDevice &ad = *it;
9220
9221 for (settings::AttachedDevicesList::const_iterator it2 = it;
9222 it2 != data.llAttachedDevices.end();
9223 ++it2)
9224 {
9225 if (it == it2)
9226 continue;
9227
9228 const settings::AttachedDevice &ad2 = *it2;
9229
9230 if ( ad.lPort == ad2.lPort
9231 && ad.lDevice == ad2.lDevice)
9232 {
9233 return setError(E_FAIL,
9234 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9235 aStorageController->i_getName().c_str(),
9236 ad.lPort,
9237 ad.lDevice,
9238 mUserData->s.strName.c_str());
9239 }
9240 }
9241 }
9242
9243 for (settings::AttachedDevicesList::const_iterator
9244 it = data.llAttachedDevices.begin();
9245 it != data.llAttachedDevices.end();
9246 ++it)
9247 {
9248 const settings::AttachedDevice &dev = *it;
9249 ComObjPtr<Medium> medium;
9250
9251 switch (dev.deviceType)
9252 {
9253 case DeviceType_Floppy:
9254 case DeviceType_DVD:
9255 if (dev.strHostDriveSrc.isNotEmpty())
9256 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9257 false /* fRefresh */, medium);
9258 else
9259 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9260 dev.uuid,
9261 false /* fRefresh */,
9262 false /* aSetError */,
9263 medium);
9264 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9265 // This is not an error. The host drive or UUID might have vanished, so just go
9266 // ahead without this removeable medium attachment
9267 rc = S_OK;
9268 break;
9269
9270 case DeviceType_HardDisk:
9271 {
9272 /* find a hard disk by UUID */
9273 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9274 if (FAILED(rc))
9275 {
9276 if (i_isSnapshotMachine())
9277 {
9278 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9279 // so the user knows that the bad disk is in a snapshot somewhere
9280 com::ErrorInfo info;
9281 return setError(E_FAIL,
9282 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9283 puuidSnapshot->raw(),
9284 info.getText().raw());
9285 }
9286 else
9287 return rc;
9288 }
9289
9290 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9291
9292 if (medium->i_getType() == MediumType_Immutable)
9293 {
9294 if (i_isSnapshotMachine())
9295 return setError(E_FAIL,
9296 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9297 "of the virtual machine '%s' ('%s')"),
9298 medium->i_getLocationFull().c_str(),
9299 dev.uuid.raw(),
9300 puuidSnapshot->raw(),
9301 mUserData->s.strName.c_str(),
9302 mData->m_strConfigFileFull.c_str());
9303
9304 return setError(E_FAIL,
9305 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9306 medium->i_getLocationFull().c_str(),
9307 dev.uuid.raw(),
9308 mUserData->s.strName.c_str(),
9309 mData->m_strConfigFileFull.c_str());
9310 }
9311
9312 if (medium->i_getType() == MediumType_MultiAttach)
9313 {
9314 if (i_isSnapshotMachine())
9315 return setError(E_FAIL,
9316 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9317 "of the virtual machine '%s' ('%s')"),
9318 medium->i_getLocationFull().c_str(),
9319 dev.uuid.raw(),
9320 puuidSnapshot->raw(),
9321 mUserData->s.strName.c_str(),
9322 mData->m_strConfigFileFull.c_str());
9323
9324 return setError(E_FAIL,
9325 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9326 medium->i_getLocationFull().c_str(),
9327 dev.uuid.raw(),
9328 mUserData->s.strName.c_str(),
9329 mData->m_strConfigFileFull.c_str());
9330 }
9331
9332 if ( !i_isSnapshotMachine()
9333 && medium->i_getChildren().size() != 0
9334 )
9335 return setError(E_FAIL,
9336 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9337 "because it has %d differencing child hard disks"),
9338 medium->i_getLocationFull().c_str(),
9339 dev.uuid.raw(),
9340 mUserData->s.strName.c_str(),
9341 mData->m_strConfigFileFull.c_str(),
9342 medium->i_getChildren().size());
9343
9344 if (i_findAttachment(*mMediumAttachments.data(),
9345 medium))
9346 return setError(E_FAIL,
9347 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9348 medium->i_getLocationFull().c_str(),
9349 dev.uuid.raw(),
9350 mUserData->s.strName.c_str(),
9351 mData->m_strConfigFileFull.c_str());
9352
9353 break;
9354 }
9355
9356 default:
9357 return setError(E_FAIL,
9358 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9359 medium->i_getLocationFull().c_str(),
9360 mUserData->s.strName.c_str(),
9361 mData->m_strConfigFileFull.c_str());
9362 }
9363
9364 if (FAILED(rc))
9365 break;
9366
9367 /* Bandwidth groups are loaded at this point. */
9368 ComObjPtr<BandwidthGroup> pBwGroup;
9369
9370 if (!dev.strBwGroup.isEmpty())
9371 {
9372 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9373 if (FAILED(rc))
9374 return setError(E_FAIL,
9375 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9376 medium->i_getLocationFull().c_str(),
9377 dev.strBwGroup.c_str(),
9378 mUserData->s.strName.c_str(),
9379 mData->m_strConfigFileFull.c_str());
9380 pBwGroup->i_reference();
9381 }
9382
9383 const Utf8Str controllerName = aStorageController->i_getName();
9384 ComObjPtr<MediumAttachment> pAttachment;
9385 pAttachment.createObject();
9386 rc = pAttachment->init(this,
9387 medium,
9388 controllerName,
9389 dev.lPort,
9390 dev.lDevice,
9391 dev.deviceType,
9392 false,
9393 dev.fPassThrough,
9394 dev.fTempEject,
9395 dev.fNonRotational,
9396 dev.fDiscard,
9397 dev.fHotPluggable,
9398 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9399 if (FAILED(rc)) break;
9400
9401 /* associate the medium with this machine and snapshot */
9402 if (!medium.isNull())
9403 {
9404 AutoCaller medCaller(medium);
9405 if (FAILED(medCaller.rc())) return medCaller.rc();
9406 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9407
9408 if (i_isSnapshotMachine())
9409 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9410 else
9411 rc = medium->i_addBackReference(mData->mUuid);
9412 /* If the medium->addBackReference fails it sets an appropriate
9413 * error message, so no need to do any guesswork here. */
9414
9415 if (puuidRegistry)
9416 // caller wants registry ID to be set on all attached media (OVF import case)
9417 medium->i_addRegistry(*puuidRegistry);
9418 }
9419
9420 if (FAILED(rc))
9421 break;
9422
9423 /* back up mMediumAttachments to let registeredInit() properly rollback
9424 * on failure (= limited accessibility) */
9425 i_setModified(IsModified_Storage);
9426 mMediumAttachments.backup();
9427 mMediumAttachments->push_back(pAttachment);
9428 }
9429
9430 return rc;
9431}
9432
9433/**
9434 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9435 *
9436 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9437 * @param aSnapshot where to return the found snapshot
9438 * @param aSetError true to set extended error info on failure
9439 */
9440HRESULT Machine::i_findSnapshotById(const Guid &aId,
9441 ComObjPtr<Snapshot> &aSnapshot,
9442 bool aSetError /* = false */)
9443{
9444 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9445
9446 if (!mData->mFirstSnapshot)
9447 {
9448 if (aSetError)
9449 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9450 return E_FAIL;
9451 }
9452
9453 if (aId.isZero())
9454 aSnapshot = mData->mFirstSnapshot;
9455 else
9456 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9457
9458 if (!aSnapshot)
9459 {
9460 if (aSetError)
9461 return setError(E_FAIL,
9462 tr("Could not find a snapshot with UUID {%s}"),
9463 aId.toString().c_str());
9464 return E_FAIL;
9465 }
9466
9467 return S_OK;
9468}
9469
9470/**
9471 * Returns the snapshot with the given name or fails of no such snapshot.
9472 *
9473 * @param strName snapshot name to find
9474 * @param aSnapshot where to return the found snapshot
9475 * @param aSetError true to set extended error info on failure
9476 */
9477HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9478 ComObjPtr<Snapshot> &aSnapshot,
9479 bool aSetError /* = false */)
9480{
9481 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9482
9483 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9484
9485 if (!mData->mFirstSnapshot)
9486 {
9487 if (aSetError)
9488 return setError(VBOX_E_OBJECT_NOT_FOUND,
9489 tr("This machine does not have any snapshots"));
9490 return VBOX_E_OBJECT_NOT_FOUND;
9491 }
9492
9493 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9494
9495 if (!aSnapshot)
9496 {
9497 if (aSetError)
9498 return setError(VBOX_E_OBJECT_NOT_FOUND,
9499 tr("Could not find a snapshot named '%s'"), strName.c_str());
9500 return VBOX_E_OBJECT_NOT_FOUND;
9501 }
9502
9503 return S_OK;
9504}
9505
9506/**
9507 * Returns a storage controller object with the given name.
9508 *
9509 * @param aName storage controller name to find
9510 * @param aStorageController where to return the found storage controller
9511 * @param aSetError true to set extended error info on failure
9512 */
9513HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9514 ComObjPtr<StorageController> &aStorageController,
9515 bool aSetError /* = false */)
9516{
9517 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9518
9519 for (StorageControllerList::const_iterator
9520 it = mStorageControllers->begin();
9521 it != mStorageControllers->end();
9522 ++it)
9523 {
9524 if ((*it)->i_getName() == aName)
9525 {
9526 aStorageController = (*it);
9527 return S_OK;
9528 }
9529 }
9530
9531 if (aSetError)
9532 return setError(VBOX_E_OBJECT_NOT_FOUND,
9533 tr("Could not find a storage controller named '%s'"),
9534 aName.c_str());
9535 return VBOX_E_OBJECT_NOT_FOUND;
9536}
9537
9538/**
9539 * Returns a USB controller object with the given name.
9540 *
9541 * @param aName USB controller name to find
9542 * @param aUSBController where to return the found USB controller
9543 * @param aSetError true to set extended error info on failure
9544 */
9545HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9546 ComObjPtr<USBController> &aUSBController,
9547 bool aSetError /* = false */)
9548{
9549 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9550
9551 for (USBControllerList::const_iterator
9552 it = mUSBControllers->begin();
9553 it != mUSBControllers->end();
9554 ++it)
9555 {
9556 if ((*it)->i_getName() == aName)
9557 {
9558 aUSBController = (*it);
9559 return S_OK;
9560 }
9561 }
9562
9563 if (aSetError)
9564 return setError(VBOX_E_OBJECT_NOT_FOUND,
9565 tr("Could not find a storage controller named '%s'"),
9566 aName.c_str());
9567 return VBOX_E_OBJECT_NOT_FOUND;
9568}
9569
9570/**
9571 * Returns the number of USB controller instance of the given type.
9572 *
9573 * @param enmType USB controller type.
9574 */
9575ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9576{
9577 ULONG cCtrls = 0;
9578
9579 for (USBControllerList::const_iterator
9580 it = mUSBControllers->begin();
9581 it != mUSBControllers->end();
9582 ++it)
9583 {
9584 if ((*it)->i_getControllerType() == enmType)
9585 cCtrls++;
9586 }
9587
9588 return cCtrls;
9589}
9590
9591HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9592 MediumAttachmentList &atts)
9593{
9594 AutoCaller autoCaller(this);
9595 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9596
9597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9598
9599 for (MediumAttachmentList::const_iterator
9600 it = mMediumAttachments->begin();
9601 it != mMediumAttachments->end();
9602 ++it)
9603 {
9604 const ComObjPtr<MediumAttachment> &pAtt = *it;
9605 // should never happen, but deal with NULL pointers in the list.
9606 AssertContinue(!pAtt.isNull());
9607
9608 // getControllerName() needs caller+read lock
9609 AutoCaller autoAttCaller(pAtt);
9610 if (FAILED(autoAttCaller.rc()))
9611 {
9612 atts.clear();
9613 return autoAttCaller.rc();
9614 }
9615 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9616
9617 if (pAtt->i_getControllerName() == aName)
9618 atts.push_back(pAtt);
9619 }
9620
9621 return S_OK;
9622}
9623
9624
9625/**
9626 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9627 * file if the machine name was changed and about creating a new settings file
9628 * if this is a new machine.
9629 *
9630 * @note Must be never called directly but only from #saveSettings().
9631 */
9632HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9633{
9634 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9635
9636 HRESULT rc = S_OK;
9637
9638 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9639
9640 /// @todo need to handle primary group change, too
9641
9642 /* attempt to rename the settings file if machine name is changed */
9643 if ( mUserData->s.fNameSync
9644 && mUserData.isBackedUp()
9645 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9646 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9647 )
9648 {
9649 bool dirRenamed = false;
9650 bool fileRenamed = false;
9651
9652 Utf8Str configFile, newConfigFile;
9653 Utf8Str configFilePrev, newConfigFilePrev;
9654 Utf8Str configDir, newConfigDir;
9655
9656 do
9657 {
9658 int vrc = VINF_SUCCESS;
9659
9660 Utf8Str name = mUserData.backedUpData()->s.strName;
9661 Utf8Str newName = mUserData->s.strName;
9662 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9663 if (group == "/")
9664 group.setNull();
9665 Utf8Str newGroup = mUserData->s.llGroups.front();
9666 if (newGroup == "/")
9667 newGroup.setNull();
9668
9669 configFile = mData->m_strConfigFileFull;
9670
9671 /* first, rename the directory if it matches the group and machine name */
9672 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9673 group.c_str(), RTPATH_DELIMITER, name.c_str());
9674 /** @todo hack, make somehow use of ComposeMachineFilename */
9675 if (mUserData->s.fDirectoryIncludesUUID)
9676 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9677 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9678 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9679 /** @todo hack, make somehow use of ComposeMachineFilename */
9680 if (mUserData->s.fDirectoryIncludesUUID)
9681 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9682 configDir = configFile;
9683 configDir.stripFilename();
9684 newConfigDir = configDir;
9685 if ( configDir.length() >= groupPlusName.length()
9686 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9687 groupPlusName.c_str()))
9688 {
9689 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9690 Utf8Str newConfigBaseDir(newConfigDir);
9691 newConfigDir.append(newGroupPlusName);
9692 /* consistency: use \ if appropriate on the platform */
9693 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9694 /* new dir and old dir cannot be equal here because of 'if'
9695 * above and because name != newName */
9696 Assert(configDir != newConfigDir);
9697 if (!fSettingsFileIsNew)
9698 {
9699 /* perform real rename only if the machine is not new */
9700 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9701 if ( vrc == VERR_FILE_NOT_FOUND
9702 || vrc == VERR_PATH_NOT_FOUND)
9703 {
9704 /* create the parent directory, then retry renaming */
9705 Utf8Str parent(newConfigDir);
9706 parent.stripFilename();
9707 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9708 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9709 }
9710 if (RT_FAILURE(vrc))
9711 {
9712 rc = setErrorBoth(E_FAIL, vrc,
9713 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9714 configDir.c_str(),
9715 newConfigDir.c_str(),
9716 vrc);
9717 break;
9718 }
9719 /* delete subdirectories which are no longer needed */
9720 Utf8Str dir(configDir);
9721 dir.stripFilename();
9722 while (dir != newConfigBaseDir && dir != ".")
9723 {
9724 vrc = RTDirRemove(dir.c_str());
9725 if (RT_FAILURE(vrc))
9726 break;
9727 dir.stripFilename();
9728 }
9729 dirRenamed = true;
9730 }
9731 }
9732
9733 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9734 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9735
9736 /* then try to rename the settings file itself */
9737 if (newConfigFile != configFile)
9738 {
9739 /* get the path to old settings file in renamed directory */
9740 configFile = Utf8StrFmt("%s%c%s",
9741 newConfigDir.c_str(),
9742 RTPATH_DELIMITER,
9743 RTPathFilename(configFile.c_str()));
9744 if (!fSettingsFileIsNew)
9745 {
9746 /* perform real rename only if the machine is not new */
9747 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9748 if (RT_FAILURE(vrc))
9749 {
9750 rc = setErrorBoth(E_FAIL, vrc,
9751 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9752 configFile.c_str(),
9753 newConfigFile.c_str(),
9754 vrc);
9755 break;
9756 }
9757 fileRenamed = true;
9758 configFilePrev = configFile;
9759 configFilePrev += "-prev";
9760 newConfigFilePrev = newConfigFile;
9761 newConfigFilePrev += "-prev";
9762 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9763 }
9764 }
9765
9766 // update m_strConfigFileFull amd mConfigFile
9767 mData->m_strConfigFileFull = newConfigFile;
9768 // compute the relative path too
9769 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9770
9771 // store the old and new so that VirtualBox::i_saveSettings() can update
9772 // the media registry
9773 if ( mData->mRegistered
9774 && (configDir != newConfigDir || configFile != newConfigFile))
9775 {
9776 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9777
9778 if (pfNeedsGlobalSaveSettings)
9779 *pfNeedsGlobalSaveSettings = true;
9780 }
9781
9782 // in the saved state file path, replace the old directory with the new directory
9783 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9784 {
9785 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9786 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9787 }
9788
9789 // and do the same thing for the saved state file paths of all the online snapshots
9790 if (mData->mFirstSnapshot)
9791 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9792 newConfigDir.c_str());
9793 }
9794 while (0);
9795
9796 if (FAILED(rc))
9797 {
9798 /* silently try to rename everything back */
9799 if (fileRenamed)
9800 {
9801 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9802 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9803 }
9804 if (dirRenamed)
9805 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9806 }
9807
9808 if (FAILED(rc)) return rc;
9809 }
9810
9811 if (fSettingsFileIsNew)
9812 {
9813 /* create a virgin config file */
9814 int vrc = VINF_SUCCESS;
9815
9816 /* ensure the settings directory exists */
9817 Utf8Str path(mData->m_strConfigFileFull);
9818 path.stripFilename();
9819 if (!RTDirExists(path.c_str()))
9820 {
9821 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9822 if (RT_FAILURE(vrc))
9823 {
9824 return setErrorBoth(E_FAIL, vrc,
9825 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9826 path.c_str(),
9827 vrc);
9828 }
9829 }
9830
9831 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9832 path = Utf8Str(mData->m_strConfigFileFull);
9833 RTFILE f = NIL_RTFILE;
9834 vrc = RTFileOpen(&f, path.c_str(),
9835 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9836 if (RT_FAILURE(vrc))
9837 return setErrorBoth(E_FAIL, vrc,
9838 tr("Could not create the settings file '%s' (%Rrc)"),
9839 path.c_str(),
9840 vrc);
9841 RTFileClose(f);
9842 }
9843
9844 return rc;
9845}
9846
9847/**
9848 * Saves and commits machine data, user data and hardware data.
9849 *
9850 * Note that on failure, the data remains uncommitted.
9851 *
9852 * @a aFlags may combine the following flags:
9853 *
9854 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9855 * Used when saving settings after an operation that makes them 100%
9856 * correspond to the settings from the current snapshot.
9857 * - SaveS_Force: settings will be saved without doing a deep compare of the
9858 * settings structures. This is used when this is called because snapshots
9859 * have changed to avoid the overhead of the deep compare.
9860 *
9861 * @note Must be called from under this object's write lock. Locks children for
9862 * writing.
9863 *
9864 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9865 * initialized to false and that will be set to true by this function if
9866 * the caller must invoke VirtualBox::i_saveSettings() because the global
9867 * settings have changed. This will happen if a machine rename has been
9868 * saved and the global machine and media registries will therefore need
9869 * updating.
9870 * @param aFlags Flags.
9871 */
9872HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9873 int aFlags /*= 0*/)
9874{
9875 LogFlowThisFuncEnter();
9876
9877 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9878
9879 /* make sure child objects are unable to modify the settings while we are
9880 * saving them */
9881 i_ensureNoStateDependencies();
9882
9883 AssertReturn(!i_isSnapshotMachine(),
9884 E_FAIL);
9885
9886 HRESULT rc = S_OK;
9887 bool fNeedsWrite = false;
9888
9889 /* First, prepare to save settings. It will care about renaming the
9890 * settings directory and file if the machine name was changed and about
9891 * creating a new settings file if this is a new machine. */
9892 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9893 if (FAILED(rc)) return rc;
9894
9895 // keep a pointer to the current settings structures
9896 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9897 settings::MachineConfigFile *pNewConfig = NULL;
9898
9899 try
9900 {
9901 // make a fresh one to have everyone write stuff into
9902 pNewConfig = new settings::MachineConfigFile(NULL);
9903 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9904
9905 // now go and copy all the settings data from COM to the settings structures
9906 // (this calls i_saveSettings() on all the COM objects in the machine)
9907 i_copyMachineDataToSettings(*pNewConfig);
9908
9909 if (aFlags & SaveS_ResetCurStateModified)
9910 {
9911 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9912 mData->mCurrentStateModified = FALSE;
9913 fNeedsWrite = true; // always, no need to compare
9914 }
9915 else if (aFlags & SaveS_Force)
9916 {
9917 fNeedsWrite = true; // always, no need to compare
9918 }
9919 else
9920 {
9921 if (!mData->mCurrentStateModified)
9922 {
9923 // do a deep compare of the settings that we just saved with the settings
9924 // previously stored in the config file; this invokes MachineConfigFile::operator==
9925 // which does a deep compare of all the settings, which is expensive but less expensive
9926 // than writing out XML in vain
9927 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9928
9929 // could still be modified if any settings changed
9930 mData->mCurrentStateModified = fAnySettingsChanged;
9931
9932 fNeedsWrite = fAnySettingsChanged;
9933 }
9934 else
9935 fNeedsWrite = true;
9936 }
9937
9938 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9939
9940 if (fNeedsWrite)
9941 // now spit it all out!
9942 pNewConfig->write(mData->m_strConfigFileFull);
9943
9944 mData->pMachineConfigFile = pNewConfig;
9945 delete pOldConfig;
9946 i_commit();
9947
9948 // after saving settings, we are no longer different from the XML on disk
9949 mData->flModifications = 0;
9950 }
9951 catch (HRESULT err)
9952 {
9953 // we assume that error info is set by the thrower
9954 rc = err;
9955
9956 // restore old config
9957 delete pNewConfig;
9958 mData->pMachineConfigFile = pOldConfig;
9959 }
9960 catch (...)
9961 {
9962 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9963 }
9964
9965 if (fNeedsWrite)
9966 {
9967 /* Fire the data change event, even on failure (since we've already
9968 * committed all data). This is done only for SessionMachines because
9969 * mutable Machine instances are always not registered (i.e. private
9970 * to the client process that creates them) and thus don't need to
9971 * inform callbacks. */
9972 if (i_isSessionMachine())
9973 mParent->i_onMachineDataChange(mData->mUuid);
9974 }
9975
9976 LogFlowThisFunc(("rc=%08X\n", rc));
9977 LogFlowThisFuncLeave();
9978 return rc;
9979}
9980
9981/**
9982 * Implementation for saving the machine settings into the given
9983 * settings::MachineConfigFile instance. This copies machine extradata
9984 * from the previous machine config file in the instance data, if any.
9985 *
9986 * This gets called from two locations:
9987 *
9988 * -- Machine::i_saveSettings(), during the regular XML writing;
9989 *
9990 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9991 * exported to OVF and we write the VirtualBox proprietary XML
9992 * into a <vbox:Machine> tag.
9993 *
9994 * This routine fills all the fields in there, including snapshots, *except*
9995 * for the following:
9996 *
9997 * -- fCurrentStateModified. There is some special logic associated with that.
9998 *
9999 * The caller can then call MachineConfigFile::write() or do something else
10000 * with it.
10001 *
10002 * Caller must hold the machine lock!
10003 *
10004 * This throws XML errors and HRESULT, so the caller must have a catch block!
10005 */
10006void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10007{
10008 // deep copy extradata, being extra careful with self assignment (the STL
10009 // map assignment on Mac OS X clang based Xcode isn't checking)
10010 if (&config != mData->pMachineConfigFile)
10011 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10012
10013 config.uuid = mData->mUuid;
10014
10015 // copy name, description, OS type, teleport, UTC etc.
10016 config.machineUserData = mUserData->s;
10017
10018 if ( mData->mMachineState == MachineState_Saved
10019 || mData->mMachineState == MachineState_Restoring
10020 // when doing certain snapshot operations we may or may not have
10021 // a saved state in the current state, so keep everything as is
10022 || ( ( mData->mMachineState == MachineState_Snapshotting
10023 || mData->mMachineState == MachineState_DeletingSnapshot
10024 || mData->mMachineState == MachineState_RestoringSnapshot)
10025 && (!mSSData->strStateFilePath.isEmpty())
10026 )
10027 )
10028 {
10029 Assert(!mSSData->strStateFilePath.isEmpty());
10030 /* try to make the file name relative to the settings file dir */
10031 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10032 }
10033 else
10034 {
10035 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10036 config.strStateFile.setNull();
10037 }
10038
10039 if (mData->mCurrentSnapshot)
10040 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10041 else
10042 config.uuidCurrentSnapshot.clear();
10043
10044 config.timeLastStateChange = mData->mLastStateChange;
10045 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10046 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10047
10048 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10049 if (FAILED(rc)) throw rc;
10050
10051 // save machine's media registry if this is VirtualBox 4.0 or later
10052 if (config.canHaveOwnMediaRegistry())
10053 {
10054 // determine machine folder
10055 Utf8Str strMachineFolder = i_getSettingsFileFull();
10056 strMachineFolder.stripFilename();
10057 mParent->i_saveMediaRegistry(config.mediaRegistry,
10058 i_getId(), // only media with registry ID == machine UUID
10059 strMachineFolder);
10060 // this throws HRESULT
10061 }
10062
10063 // save snapshots
10064 rc = i_saveAllSnapshots(config);
10065 if (FAILED(rc)) throw rc;
10066}
10067
10068/**
10069 * Saves all snapshots of the machine into the given machine config file. Called
10070 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10071 * @param config
10072 * @return
10073 */
10074HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10075{
10076 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10077
10078 HRESULT rc = S_OK;
10079
10080 try
10081 {
10082 config.llFirstSnapshot.clear();
10083
10084 if (mData->mFirstSnapshot)
10085 {
10086 // the settings use a list for "the first snapshot"
10087 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10088
10089 // get reference to the snapshot on the list and work on that
10090 // element straight in the list to avoid excessive copying later
10091 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10092 if (FAILED(rc)) throw rc;
10093 }
10094
10095// if (mType == IsSessionMachine)
10096// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10097
10098 }
10099 catch (HRESULT err)
10100 {
10101 /* we assume that error info is set by the thrower */
10102 rc = err;
10103 }
10104 catch (...)
10105 {
10106 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10107 }
10108
10109 return rc;
10110}
10111
10112/**
10113 * Saves the VM hardware configuration. It is assumed that the
10114 * given node is empty.
10115 *
10116 * @param data Reference to the settings object for the hardware config.
10117 * @param pDbg Pointer to the settings object for the debugging config
10118 * which happens to live in mHWData.
10119 * @param pAutostart Pointer to the settings object for the autostart config
10120 * which happens to live in mHWData.
10121 */
10122HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10123 settings::Autostart *pAutostart)
10124{
10125 HRESULT rc = S_OK;
10126
10127 try
10128 {
10129 /* The hardware version attribute (optional).
10130 Automatically upgrade from 1 to current default hardware version
10131 when there is no saved state. (ugly!) */
10132 if ( mHWData->mHWVersion == "1"
10133 && mSSData->strStateFilePath.isEmpty()
10134 )
10135 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10136
10137 data.strVersion = mHWData->mHWVersion;
10138 data.uuid = mHWData->mHardwareUUID;
10139
10140 // CPU
10141 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10142 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10143 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10144 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10145 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10146 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10147 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10148 data.fPAE = !!mHWData->mPAEEnabled;
10149 data.enmLongMode = mHWData->mLongMode;
10150 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10151 data.fAPIC = !!mHWData->mAPIC;
10152 data.fX2APIC = !!mHWData->mX2APIC;
10153 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10154 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10155 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10156 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10157 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10158 data.cCPUs = mHWData->mCPUCount;
10159 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10160 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10161 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10162 data.strCpuProfile = mHWData->mCpuProfile;
10163
10164 data.llCpus.clear();
10165 if (data.fCpuHotPlug)
10166 {
10167 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10168 {
10169 if (mHWData->mCPUAttached[idx])
10170 {
10171 settings::Cpu cpu;
10172 cpu.ulId = idx;
10173 data.llCpus.push_back(cpu);
10174 }
10175 }
10176 }
10177
10178 /* Standard and Extended CPUID leafs. */
10179 data.llCpuIdLeafs.clear();
10180 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10181
10182 // memory
10183 data.ulMemorySizeMB = mHWData->mMemorySize;
10184 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10185
10186 // firmware
10187 data.firmwareType = mHWData->mFirmwareType;
10188
10189 // HID
10190 data.pointingHIDType = mHWData->mPointingHIDType;
10191 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10192
10193 // chipset
10194 data.chipsetType = mHWData->mChipsetType;
10195
10196 // paravirt
10197 data.paravirtProvider = mHWData->mParavirtProvider;
10198 data.strParavirtDebug = mHWData->mParavirtDebug;
10199
10200 // emulated USB card reader
10201 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10202
10203 // HPET
10204 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10205
10206 // boot order
10207 data.mapBootOrder.clear();
10208 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10209 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10210
10211 // display
10212 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10213 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10214 data.cMonitors = mHWData->mMonitorCount;
10215 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10216 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10217
10218 /* VRDEServer settings (optional) */
10219 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10220 if (FAILED(rc)) throw rc;
10221
10222 /* BIOS settings (required) */
10223 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10224 if (FAILED(rc)) throw rc;
10225
10226 /* Recording settings (required) */
10227 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10228 if (FAILED(rc)) throw rc;
10229
10230 /* USB Controller (required) */
10231 data.usbSettings.llUSBControllers.clear();
10232 for (USBControllerList::const_iterator
10233 it = mUSBControllers->begin();
10234 it != mUSBControllers->end();
10235 ++it)
10236 {
10237 ComObjPtr<USBController> ctrl = *it;
10238 settings::USBController settingsCtrl;
10239
10240 settingsCtrl.strName = ctrl->i_getName();
10241 settingsCtrl.enmType = ctrl->i_getControllerType();
10242
10243 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10244 }
10245
10246 /* USB device filters (required) */
10247 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10248 if (FAILED(rc)) throw rc;
10249
10250 /* Network adapters (required) */
10251 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10252 data.llNetworkAdapters.clear();
10253 /* Write out only the nominal number of network adapters for this
10254 * chipset type. Since Machine::commit() hasn't been called there
10255 * may be extra NIC settings in the vector. */
10256 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10257 {
10258 settings::NetworkAdapter nic;
10259 nic.ulSlot = (uint32_t)slot;
10260 /* paranoia check... must not be NULL, but must not crash either. */
10261 if (mNetworkAdapters[slot])
10262 {
10263 if (mNetworkAdapters[slot]->i_hasDefaults())
10264 continue;
10265
10266 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10267 if (FAILED(rc)) throw rc;
10268
10269 data.llNetworkAdapters.push_back(nic);
10270 }
10271 }
10272
10273 /* Serial ports */
10274 data.llSerialPorts.clear();
10275 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10276 {
10277 if (mSerialPorts[slot]->i_hasDefaults())
10278 continue;
10279
10280 settings::SerialPort s;
10281 s.ulSlot = slot;
10282 rc = mSerialPorts[slot]->i_saveSettings(s);
10283 if (FAILED(rc)) return rc;
10284
10285 data.llSerialPorts.push_back(s);
10286 }
10287
10288 /* Parallel ports */
10289 data.llParallelPorts.clear();
10290 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10291 {
10292 if (mParallelPorts[slot]->i_hasDefaults())
10293 continue;
10294
10295 settings::ParallelPort p;
10296 p.ulSlot = slot;
10297 rc = mParallelPorts[slot]->i_saveSettings(p);
10298 if (FAILED(rc)) return rc;
10299
10300 data.llParallelPorts.push_back(p);
10301 }
10302
10303 /* Audio adapter */
10304 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10305 if (FAILED(rc)) return rc;
10306
10307 rc = i_saveStorageControllers(data.storage);
10308 if (FAILED(rc)) return rc;
10309
10310 /* Shared folders */
10311 data.llSharedFolders.clear();
10312 for (HWData::SharedFolderList::const_iterator
10313 it = mHWData->mSharedFolders.begin();
10314 it != mHWData->mSharedFolders.end();
10315 ++it)
10316 {
10317 SharedFolder *pSF = *it;
10318 AutoCaller sfCaller(pSF);
10319 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10320 settings::SharedFolder sf;
10321 sf.strName = pSF->i_getName();
10322 sf.strHostPath = pSF->i_getHostPath();
10323 sf.fWritable = !!pSF->i_isWritable();
10324 sf.fAutoMount = !!pSF->i_isAutoMounted();
10325 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10326
10327 data.llSharedFolders.push_back(sf);
10328 }
10329
10330 // clipboard
10331 data.clipboardMode = mHWData->mClipboardMode;
10332
10333 // drag'n'drop
10334 data.dndMode = mHWData->mDnDMode;
10335
10336 /* Guest */
10337 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10338
10339 // IO settings
10340 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10341 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10342
10343 /* BandwidthControl (required) */
10344 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10345 if (FAILED(rc)) throw rc;
10346
10347 /* Host PCI devices */
10348 data.pciAttachments.clear();
10349 for (HWData::PCIDeviceAssignmentList::const_iterator
10350 it = mHWData->mPCIDeviceAssignments.begin();
10351 it != mHWData->mPCIDeviceAssignments.end();
10352 ++it)
10353 {
10354 ComObjPtr<PCIDeviceAttachment> pda = *it;
10355 settings::HostPCIDeviceAttachment hpda;
10356
10357 rc = pda->i_saveSettings(hpda);
10358 if (FAILED(rc)) throw rc;
10359
10360 data.pciAttachments.push_back(hpda);
10361 }
10362
10363 // guest properties
10364 data.llGuestProperties.clear();
10365#ifdef VBOX_WITH_GUEST_PROPS
10366 for (HWData::GuestPropertyMap::const_iterator
10367 it = mHWData->mGuestProperties.begin();
10368 it != mHWData->mGuestProperties.end();
10369 ++it)
10370 {
10371 HWData::GuestProperty property = it->second;
10372
10373 /* Remove transient guest properties at shutdown unless we
10374 * are saving state. Note that restoring snapshot intentionally
10375 * keeps them, they will be removed if appropriate once the final
10376 * machine state is set (as crashes etc. need to work). */
10377 if ( ( mData->mMachineState == MachineState_PoweredOff
10378 || mData->mMachineState == MachineState_Aborted
10379 || mData->mMachineState == MachineState_Teleported)
10380 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10381 continue;
10382 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10383 prop.strName = it->first;
10384 prop.strValue = property.strValue;
10385 prop.timestamp = property.mTimestamp;
10386 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10387 GuestPropWriteFlags(property.mFlags, szFlags);
10388 prop.strFlags = szFlags;
10389
10390 data.llGuestProperties.push_back(prop);
10391 }
10392
10393 /* I presume this doesn't require a backup(). */
10394 mData->mGuestPropertiesModified = FALSE;
10395#endif /* VBOX_WITH_GUEST_PROPS defined */
10396
10397 *pDbg = mHWData->mDebugging;
10398 *pAutostart = mHWData->mAutostart;
10399
10400 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10401 }
10402 catch (std::bad_alloc &)
10403 {
10404 return E_OUTOFMEMORY;
10405 }
10406
10407 AssertComRC(rc);
10408 return rc;
10409}
10410
10411/**
10412 * Saves the storage controller configuration.
10413 *
10414 * @param data storage settings.
10415 */
10416HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10417{
10418 data.llStorageControllers.clear();
10419
10420 for (StorageControllerList::const_iterator
10421 it = mStorageControllers->begin();
10422 it != mStorageControllers->end();
10423 ++it)
10424 {
10425 HRESULT rc;
10426 ComObjPtr<StorageController> pCtl = *it;
10427
10428 settings::StorageController ctl;
10429 ctl.strName = pCtl->i_getName();
10430 ctl.controllerType = pCtl->i_getControllerType();
10431 ctl.storageBus = pCtl->i_getStorageBus();
10432 ctl.ulInstance = pCtl->i_getInstance();
10433 ctl.fBootable = pCtl->i_getBootable();
10434
10435 /* Save the port count. */
10436 ULONG portCount;
10437 rc = pCtl->COMGETTER(PortCount)(&portCount);
10438 ComAssertComRCRet(rc, rc);
10439 ctl.ulPortCount = portCount;
10440
10441 /* Save fUseHostIOCache */
10442 BOOL fUseHostIOCache;
10443 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10444 ComAssertComRCRet(rc, rc);
10445 ctl.fUseHostIOCache = !!fUseHostIOCache;
10446
10447 /* save the devices now. */
10448 rc = i_saveStorageDevices(pCtl, ctl);
10449 ComAssertComRCRet(rc, rc);
10450
10451 data.llStorageControllers.push_back(ctl);
10452 }
10453
10454 return S_OK;
10455}
10456
10457/**
10458 * Saves the hard disk configuration.
10459 */
10460HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10461 settings::StorageController &data)
10462{
10463 MediumAttachmentList atts;
10464
10465 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10466 if (FAILED(rc)) return rc;
10467
10468 data.llAttachedDevices.clear();
10469 for (MediumAttachmentList::const_iterator
10470 it = atts.begin();
10471 it != atts.end();
10472 ++it)
10473 {
10474 settings::AttachedDevice dev;
10475 IMediumAttachment *iA = *it;
10476 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10477 Medium *pMedium = pAttach->i_getMedium();
10478
10479 dev.deviceType = pAttach->i_getType();
10480 dev.lPort = pAttach->i_getPort();
10481 dev.lDevice = pAttach->i_getDevice();
10482 dev.fPassThrough = pAttach->i_getPassthrough();
10483 dev.fHotPluggable = pAttach->i_getHotPluggable();
10484 if (pMedium)
10485 {
10486 if (pMedium->i_isHostDrive())
10487 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10488 else
10489 dev.uuid = pMedium->i_getId();
10490 dev.fTempEject = pAttach->i_getTempEject();
10491 dev.fNonRotational = pAttach->i_getNonRotational();
10492 dev.fDiscard = pAttach->i_getDiscard();
10493 }
10494
10495 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10496
10497 data.llAttachedDevices.push_back(dev);
10498 }
10499
10500 return S_OK;
10501}
10502
10503/**
10504 * Saves machine state settings as defined by aFlags
10505 * (SaveSTS_* values).
10506 *
10507 * @param aFlags Combination of SaveSTS_* flags.
10508 *
10509 * @note Locks objects for writing.
10510 */
10511HRESULT Machine::i_saveStateSettings(int aFlags)
10512{
10513 if (aFlags == 0)
10514 return S_OK;
10515
10516 AutoCaller autoCaller(this);
10517 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10518
10519 /* This object's write lock is also necessary to serialize file access
10520 * (prevent concurrent reads and writes) */
10521 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10522
10523 HRESULT rc = S_OK;
10524
10525 Assert(mData->pMachineConfigFile);
10526
10527 try
10528 {
10529 if (aFlags & SaveSTS_CurStateModified)
10530 mData->pMachineConfigFile->fCurrentStateModified = true;
10531
10532 if (aFlags & SaveSTS_StateFilePath)
10533 {
10534 if (!mSSData->strStateFilePath.isEmpty())
10535 /* try to make the file name relative to the settings file dir */
10536 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10537 else
10538 mData->pMachineConfigFile->strStateFile.setNull();
10539 }
10540
10541 if (aFlags & SaveSTS_StateTimeStamp)
10542 {
10543 Assert( mData->mMachineState != MachineState_Aborted
10544 || mSSData->strStateFilePath.isEmpty());
10545
10546 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10547
10548 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10549/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10550 }
10551
10552 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10553 }
10554 catch (...)
10555 {
10556 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10557 }
10558
10559 return rc;
10560}
10561
10562/**
10563 * Ensures that the given medium is added to a media registry. If this machine
10564 * was created with 4.0 or later, then the machine registry is used. Otherwise
10565 * the global VirtualBox media registry is used.
10566 *
10567 * Caller must NOT hold machine lock, media tree or any medium locks!
10568 *
10569 * @param pMedium
10570 */
10571void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10572{
10573 /* Paranoia checks: do not hold machine or media tree locks. */
10574 AssertReturnVoid(!isWriteLockOnCurrentThread());
10575 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10576
10577 ComObjPtr<Medium> pBase;
10578 {
10579 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10580 pBase = pMedium->i_getBase();
10581 }
10582
10583 /* Paranoia checks: do not hold medium locks. */
10584 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10585 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10586
10587 // decide which medium registry to use now that the medium is attached:
10588 Guid uuid;
10589 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10590 if (fCanHaveOwnMediaRegistry)
10591 // machine XML is VirtualBox 4.0 or higher:
10592 uuid = i_getId(); // machine UUID
10593 else
10594 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10595
10596 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10597 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10598 if (pMedium->i_addRegistry(uuid))
10599 mParent->i_markRegistryModified(uuid);
10600
10601 /* For more complex hard disk structures it can happen that the base
10602 * medium isn't yet associated with any medium registry. Do that now. */
10603 if (pMedium != pBase)
10604 {
10605 /* Tree lock needed by Medium::addRegistry when recursing. */
10606 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10607 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10608 {
10609 treeLock.release();
10610 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10611 treeLock.acquire();
10612 }
10613 if (pBase->i_addRegistryRecursive(uuid))
10614 {
10615 treeLock.release();
10616 mParent->i_markRegistryModified(uuid);
10617 }
10618 }
10619}
10620
10621/**
10622 * Creates differencing hard disks for all normal hard disks attached to this
10623 * machine and a new set of attachments to refer to created disks.
10624 *
10625 * Used when taking a snapshot or when deleting the current state. Gets called
10626 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10627 *
10628 * This method assumes that mMediumAttachments contains the original hard disk
10629 * attachments it needs to create diffs for. On success, these attachments will
10630 * be replaced with the created diffs.
10631 *
10632 * Attachments with non-normal hard disks are left as is.
10633 *
10634 * If @a aOnline is @c false then the original hard disks that require implicit
10635 * diffs will be locked for reading. Otherwise it is assumed that they are
10636 * already locked for writing (when the VM was started). Note that in the latter
10637 * case it is responsibility of the caller to lock the newly created diffs for
10638 * writing if this method succeeds.
10639 *
10640 * @param aProgress Progress object to run (must contain at least as
10641 * many operations left as the number of hard disks
10642 * attached).
10643 * @param aWeight Weight of this operation.
10644 * @param aOnline Whether the VM was online prior to this operation.
10645 *
10646 * @note The progress object is not marked as completed, neither on success nor
10647 * on failure. This is a responsibility of the caller.
10648 *
10649 * @note Locks this object and the media tree for writing.
10650 */
10651HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10652 ULONG aWeight,
10653 bool aOnline)
10654{
10655 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10656
10657 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10658 AssertReturn(!!pProgressControl, E_INVALIDARG);
10659
10660 AutoCaller autoCaller(this);
10661 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10662
10663 AutoMultiWriteLock2 alock(this->lockHandle(),
10664 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10665
10666 /* must be in a protective state because we release the lock below */
10667 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10668 || mData->mMachineState == MachineState_OnlineSnapshotting
10669 || mData->mMachineState == MachineState_LiveSnapshotting
10670 || mData->mMachineState == MachineState_RestoringSnapshot
10671 || mData->mMachineState == MachineState_DeletingSnapshot
10672 , E_FAIL);
10673
10674 HRESULT rc = S_OK;
10675
10676 // use appropriate locked media map (online or offline)
10677 MediumLockListMap lockedMediaOffline;
10678 MediumLockListMap *lockedMediaMap;
10679 if (aOnline)
10680 lockedMediaMap = &mData->mSession.mLockedMedia;
10681 else
10682 lockedMediaMap = &lockedMediaOffline;
10683
10684 try
10685 {
10686 if (!aOnline)
10687 {
10688 /* lock all attached hard disks early to detect "in use"
10689 * situations before creating actual diffs */
10690 for (MediumAttachmentList::const_iterator
10691 it = mMediumAttachments->begin();
10692 it != mMediumAttachments->end();
10693 ++it)
10694 {
10695 MediumAttachment *pAtt = *it;
10696 if (pAtt->i_getType() == DeviceType_HardDisk)
10697 {
10698 Medium *pMedium = pAtt->i_getMedium();
10699 Assert(pMedium);
10700
10701 MediumLockList *pMediumLockList(new MediumLockList());
10702 alock.release();
10703 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10704 NULL /* pToLockWrite */,
10705 false /* fMediumLockWriteAll */,
10706 NULL,
10707 *pMediumLockList);
10708 alock.acquire();
10709 if (FAILED(rc))
10710 {
10711 delete pMediumLockList;
10712 throw rc;
10713 }
10714 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10715 if (FAILED(rc))
10716 {
10717 throw setError(rc,
10718 tr("Collecting locking information for all attached media failed"));
10719 }
10720 }
10721 }
10722
10723 /* Now lock all media. If this fails, nothing is locked. */
10724 alock.release();
10725 rc = lockedMediaMap->Lock();
10726 alock.acquire();
10727 if (FAILED(rc))
10728 {
10729 throw setError(rc,
10730 tr("Locking of attached media failed"));
10731 }
10732 }
10733
10734 /* remember the current list (note that we don't use backup() since
10735 * mMediumAttachments may be already backed up) */
10736 MediumAttachmentList atts = *mMediumAttachments.data();
10737
10738 /* start from scratch */
10739 mMediumAttachments->clear();
10740
10741 /* go through remembered attachments and create diffs for normal hard
10742 * disks and attach them */
10743 for (MediumAttachmentList::const_iterator
10744 it = atts.begin();
10745 it != atts.end();
10746 ++it)
10747 {
10748 MediumAttachment *pAtt = *it;
10749
10750 DeviceType_T devType = pAtt->i_getType();
10751 Medium *pMedium = pAtt->i_getMedium();
10752
10753 if ( devType != DeviceType_HardDisk
10754 || pMedium == NULL
10755 || pMedium->i_getType() != MediumType_Normal)
10756 {
10757 /* copy the attachment as is */
10758
10759 /** @todo the progress object created in SessionMachine::TakeSnaphot
10760 * only expects operations for hard disks. Later other
10761 * device types need to show up in the progress as well. */
10762 if (devType == DeviceType_HardDisk)
10763 {
10764 if (pMedium == NULL)
10765 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10766 aWeight); // weight
10767 else
10768 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10769 pMedium->i_getBase()->i_getName().c_str()).raw(),
10770 aWeight); // weight
10771 }
10772
10773 mMediumAttachments->push_back(pAtt);
10774 continue;
10775 }
10776
10777 /* need a diff */
10778 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10779 pMedium->i_getBase()->i_getName().c_str()).raw(),
10780 aWeight); // weight
10781
10782 Utf8Str strFullSnapshotFolder;
10783 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10784
10785 ComObjPtr<Medium> diff;
10786 diff.createObject();
10787 // store the diff in the same registry as the parent
10788 // (this cannot fail here because we can't create implicit diffs for
10789 // unregistered images)
10790 Guid uuidRegistryParent;
10791 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10792 Assert(fInRegistry); NOREF(fInRegistry);
10793 rc = diff->init(mParent,
10794 pMedium->i_getPreferredDiffFormat(),
10795 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10796 uuidRegistryParent,
10797 DeviceType_HardDisk);
10798 if (FAILED(rc)) throw rc;
10799
10800 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10801 * the push_back? Looks like we're going to release medium with the
10802 * wrong kind of lock (general issue with if we fail anywhere at all)
10803 * and an orphaned VDI in the snapshots folder. */
10804
10805 /* update the appropriate lock list */
10806 MediumLockList *pMediumLockList;
10807 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10808 AssertComRCThrowRC(rc);
10809 if (aOnline)
10810 {
10811 alock.release();
10812 /* The currently attached medium will be read-only, change
10813 * the lock type to read. */
10814 rc = pMediumLockList->Update(pMedium, false);
10815 alock.acquire();
10816 AssertComRCThrowRC(rc);
10817 }
10818
10819 /* release the locks before the potentially lengthy operation */
10820 alock.release();
10821 rc = pMedium->i_createDiffStorage(diff,
10822 pMedium->i_getPreferredDiffVariant(),
10823 pMediumLockList,
10824 NULL /* aProgress */,
10825 true /* aWait */,
10826 false /* aNotify */);
10827 alock.acquire();
10828 if (FAILED(rc)) throw rc;
10829
10830 /* actual lock list update is done in Machine::i_commitMedia */
10831
10832 rc = diff->i_addBackReference(mData->mUuid);
10833 AssertComRCThrowRC(rc);
10834
10835 /* add a new attachment */
10836 ComObjPtr<MediumAttachment> attachment;
10837 attachment.createObject();
10838 rc = attachment->init(this,
10839 diff,
10840 pAtt->i_getControllerName(),
10841 pAtt->i_getPort(),
10842 pAtt->i_getDevice(),
10843 DeviceType_HardDisk,
10844 true /* aImplicit */,
10845 false /* aPassthrough */,
10846 false /* aTempEject */,
10847 pAtt->i_getNonRotational(),
10848 pAtt->i_getDiscard(),
10849 pAtt->i_getHotPluggable(),
10850 pAtt->i_getBandwidthGroup());
10851 if (FAILED(rc)) throw rc;
10852
10853 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10854 AssertComRCThrowRC(rc);
10855 mMediumAttachments->push_back(attachment);
10856 }
10857 }
10858 catch (HRESULT aRC) { rc = aRC; }
10859
10860 /* unlock all hard disks we locked when there is no VM */
10861 if (!aOnline)
10862 {
10863 ErrorInfoKeeper eik;
10864
10865 HRESULT rc1 = lockedMediaMap->Clear();
10866 AssertComRC(rc1);
10867 }
10868
10869 return rc;
10870}
10871
10872/**
10873 * Deletes implicit differencing hard disks created either by
10874 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10875 * mMediumAttachments.
10876 *
10877 * Note that to delete hard disks created by #attachDevice() this method is
10878 * called from #i_rollbackMedia() when the changes are rolled back.
10879 *
10880 * @note Locks this object and the media tree for writing.
10881 */
10882HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10883{
10884 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10885
10886 AutoCaller autoCaller(this);
10887 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10888
10889 AutoMultiWriteLock2 alock(this->lockHandle(),
10890 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10891
10892 /* We absolutely must have backed up state. */
10893 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10894
10895 /* Check if there are any implicitly created diff images. */
10896 bool fImplicitDiffs = false;
10897 for (MediumAttachmentList::const_iterator
10898 it = mMediumAttachments->begin();
10899 it != mMediumAttachments->end();
10900 ++it)
10901 {
10902 const ComObjPtr<MediumAttachment> &pAtt = *it;
10903 if (pAtt->i_isImplicit())
10904 {
10905 fImplicitDiffs = true;
10906 break;
10907 }
10908 }
10909 /* If there is nothing to do, leave early. This saves lots of image locking
10910 * effort. It also avoids a MachineStateChanged event without real reason.
10911 * This is important e.g. when loading a VM config, because there should be
10912 * no events. Otherwise API clients can become thoroughly confused for
10913 * inaccessible VMs (the code for loading VM configs uses this method for
10914 * cleanup if the config makes no sense), as they take such events as an
10915 * indication that the VM is alive, and they would force the VM config to
10916 * be reread, leading to an endless loop. */
10917 if (!fImplicitDiffs)
10918 return S_OK;
10919
10920 HRESULT rc = S_OK;
10921 MachineState_T oldState = mData->mMachineState;
10922
10923 /* will release the lock before the potentially lengthy operation,
10924 * so protect with the special state (unless already protected) */
10925 if ( oldState != MachineState_Snapshotting
10926 && oldState != MachineState_OnlineSnapshotting
10927 && oldState != MachineState_LiveSnapshotting
10928 && oldState != MachineState_RestoringSnapshot
10929 && oldState != MachineState_DeletingSnapshot
10930 && oldState != MachineState_DeletingSnapshotOnline
10931 && oldState != MachineState_DeletingSnapshotPaused
10932 )
10933 i_setMachineState(MachineState_SettingUp);
10934
10935 // use appropriate locked media map (online or offline)
10936 MediumLockListMap lockedMediaOffline;
10937 MediumLockListMap *lockedMediaMap;
10938 if (aOnline)
10939 lockedMediaMap = &mData->mSession.mLockedMedia;
10940 else
10941 lockedMediaMap = &lockedMediaOffline;
10942
10943 try
10944 {
10945 if (!aOnline)
10946 {
10947 /* lock all attached hard disks early to detect "in use"
10948 * situations before deleting actual diffs */
10949 for (MediumAttachmentList::const_iterator
10950 it = mMediumAttachments->begin();
10951 it != mMediumAttachments->end();
10952 ++it)
10953 {
10954 MediumAttachment *pAtt = *it;
10955 if (pAtt->i_getType() == DeviceType_HardDisk)
10956 {
10957 Medium *pMedium = pAtt->i_getMedium();
10958 Assert(pMedium);
10959
10960 MediumLockList *pMediumLockList(new MediumLockList());
10961 alock.release();
10962 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10963 NULL /* pToLockWrite */,
10964 false /* fMediumLockWriteAll */,
10965 NULL,
10966 *pMediumLockList);
10967 alock.acquire();
10968
10969 if (FAILED(rc))
10970 {
10971 delete pMediumLockList;
10972 throw rc;
10973 }
10974
10975 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10976 if (FAILED(rc))
10977 throw rc;
10978 }
10979 }
10980
10981 if (FAILED(rc))
10982 throw rc;
10983 } // end of offline
10984
10985 /* Lock lists are now up to date and include implicitly created media */
10986
10987 /* Go through remembered attachments and delete all implicitly created
10988 * diffs and fix up the attachment information */
10989 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10990 MediumAttachmentList implicitAtts;
10991 for (MediumAttachmentList::const_iterator
10992 it = mMediumAttachments->begin();
10993 it != mMediumAttachments->end();
10994 ++it)
10995 {
10996 ComObjPtr<MediumAttachment> pAtt = *it;
10997 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10998 if (pMedium.isNull())
10999 continue;
11000
11001 // Implicit attachments go on the list for deletion and back references are removed.
11002 if (pAtt->i_isImplicit())
11003 {
11004 /* Deassociate and mark for deletion */
11005 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11006 rc = pMedium->i_removeBackReference(mData->mUuid);
11007 if (FAILED(rc))
11008 throw rc;
11009 implicitAtts.push_back(pAtt);
11010 continue;
11011 }
11012
11013 /* Was this medium attached before? */
11014 if (!i_findAttachment(oldAtts, pMedium))
11015 {
11016 /* no: de-associate */
11017 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11018 rc = pMedium->i_removeBackReference(mData->mUuid);
11019 if (FAILED(rc))
11020 throw rc;
11021 continue;
11022 }
11023 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11024 }
11025
11026 /* If there are implicit attachments to delete, throw away the lock
11027 * map contents (which will unlock all media) since the medium
11028 * attachments will be rolled back. Below we need to completely
11029 * recreate the lock map anyway since it is infinitely complex to
11030 * do this incrementally (would need reconstructing each attachment
11031 * change, which would be extremely hairy). */
11032 if (implicitAtts.size() != 0)
11033 {
11034 ErrorInfoKeeper eik;
11035
11036 HRESULT rc1 = lockedMediaMap->Clear();
11037 AssertComRC(rc1);
11038 }
11039
11040 /* rollback hard disk changes */
11041 mMediumAttachments.rollback();
11042
11043 MultiResult mrc(S_OK);
11044
11045 // Delete unused implicit diffs.
11046 if (implicitAtts.size() != 0)
11047 {
11048 alock.release();
11049
11050 for (MediumAttachmentList::const_iterator
11051 it = implicitAtts.begin();
11052 it != implicitAtts.end();
11053 ++it)
11054 {
11055 // Remove medium associated with this attachment.
11056 ComObjPtr<MediumAttachment> pAtt = *it;
11057 Assert(pAtt);
11058 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11059 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11060 Assert(pMedium);
11061
11062 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11063 // continue on delete failure, just collect error messages
11064 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11065 pMedium->i_getLocationFull().c_str() ));
11066 mrc = rc;
11067 }
11068 // Clear the list of deleted implicit attachments now, while not
11069 // holding the lock, as it will ultimately trigger Medium::uninit()
11070 // calls which assume that the media tree lock isn't held.
11071 implicitAtts.clear();
11072
11073 alock.acquire();
11074
11075 /* if there is a VM recreate media lock map as mentioned above,
11076 * otherwise it is a waste of time and we leave things unlocked */
11077 if (aOnline)
11078 {
11079 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11080 /* must never be NULL, but better safe than sorry */
11081 if (!pMachine.isNull())
11082 {
11083 alock.release();
11084 rc = mData->mSession.mMachine->i_lockMedia();
11085 alock.acquire();
11086 if (FAILED(rc))
11087 throw rc;
11088 }
11089 }
11090 }
11091 }
11092 catch (HRESULT aRC) {rc = aRC;}
11093
11094 if (mData->mMachineState == MachineState_SettingUp)
11095 i_setMachineState(oldState);
11096
11097 /* unlock all hard disks we locked when there is no VM */
11098 if (!aOnline)
11099 {
11100 ErrorInfoKeeper eik;
11101
11102 HRESULT rc1 = lockedMediaMap->Clear();
11103 AssertComRC(rc1);
11104 }
11105
11106 return rc;
11107}
11108
11109
11110/**
11111 * Looks through the given list of media attachments for one with the given parameters
11112 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11113 * can be searched as well if needed.
11114 *
11115 * @param ll
11116 * @param aControllerName
11117 * @param aControllerPort
11118 * @param aDevice
11119 * @return
11120 */
11121MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11122 const Utf8Str &aControllerName,
11123 LONG aControllerPort,
11124 LONG aDevice)
11125{
11126 for (MediumAttachmentList::const_iterator
11127 it = ll.begin();
11128 it != ll.end();
11129 ++it)
11130 {
11131 MediumAttachment *pAttach = *it;
11132 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11133 return pAttach;
11134 }
11135
11136 return NULL;
11137}
11138
11139/**
11140 * Looks through the given list of media attachments for one with the given parameters
11141 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11142 * can be searched as well if needed.
11143 *
11144 * @param ll
11145 * @param pMedium
11146 * @return
11147 */
11148MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11149 ComObjPtr<Medium> pMedium)
11150{
11151 for (MediumAttachmentList::const_iterator
11152 it = ll.begin();
11153 it != ll.end();
11154 ++it)
11155 {
11156 MediumAttachment *pAttach = *it;
11157 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11158 if (pMediumThis == pMedium)
11159 return pAttach;
11160 }
11161
11162 return NULL;
11163}
11164
11165/**
11166 * Looks through the given list of media attachments for one with the given parameters
11167 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11168 * can be searched as well if needed.
11169 *
11170 * @param ll
11171 * @param id
11172 * @return
11173 */
11174MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11175 Guid &id)
11176{
11177 for (MediumAttachmentList::const_iterator
11178 it = ll.begin();
11179 it != ll.end();
11180 ++it)
11181 {
11182 MediumAttachment *pAttach = *it;
11183 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11184 if (pMediumThis->i_getId() == id)
11185 return pAttach;
11186 }
11187
11188 return NULL;
11189}
11190
11191/**
11192 * Main implementation for Machine::DetachDevice. This also gets called
11193 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11194 *
11195 * @param pAttach Medium attachment to detach.
11196 * @param writeLock Machine write lock which the caller must have locked once.
11197 * This may be released temporarily in here.
11198 * @param pSnapshot If NULL, then the detachment is for the current machine.
11199 * Otherwise this is for a SnapshotMachine, and this must be
11200 * its snapshot.
11201 * @return
11202 */
11203HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11204 AutoWriteLock &writeLock,
11205 Snapshot *pSnapshot)
11206{
11207 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11208 DeviceType_T mediumType = pAttach->i_getType();
11209
11210 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11211
11212 if (pAttach->i_isImplicit())
11213 {
11214 /* attempt to implicitly delete the implicitly created diff */
11215
11216 /// @todo move the implicit flag from MediumAttachment to Medium
11217 /// and forbid any hard disk operation when it is implicit. Or maybe
11218 /// a special media state for it to make it even more simple.
11219
11220 Assert(mMediumAttachments.isBackedUp());
11221
11222 /* will release the lock before the potentially lengthy operation, so
11223 * protect with the special state */
11224 MachineState_T oldState = mData->mMachineState;
11225 i_setMachineState(MachineState_SettingUp);
11226
11227 writeLock.release();
11228
11229 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11230 true /*aWait*/,
11231 false /*aNotify*/);
11232
11233 writeLock.acquire();
11234
11235 i_setMachineState(oldState);
11236
11237 if (FAILED(rc)) return rc;
11238 }
11239
11240 i_setModified(IsModified_Storage);
11241 mMediumAttachments.backup();
11242 mMediumAttachments->remove(pAttach);
11243
11244 if (!oldmedium.isNull())
11245 {
11246 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11247 if (pSnapshot)
11248 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11249 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11250 else if (mediumType != DeviceType_HardDisk)
11251 oldmedium->i_removeBackReference(mData->mUuid);
11252 }
11253
11254 return S_OK;
11255}
11256
11257/**
11258 * Goes thru all media of the given list and
11259 *
11260 * 1) calls i_detachDevice() on each of them for this machine and
11261 * 2) adds all Medium objects found in the process to the given list,
11262 * depending on cleanupMode.
11263 *
11264 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11265 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11266 * media to the list.
11267 *
11268 * This gets called from Machine::Unregister, both for the actual Machine and
11269 * the SnapshotMachine objects that might be found in the snapshots.
11270 *
11271 * Requires caller and locking. The machine lock must be passed in because it
11272 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11273 *
11274 * @param writeLock Machine lock from top-level caller; this gets passed to
11275 * i_detachDevice.
11276 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11277 * object if called for a SnapshotMachine.
11278 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11279 * added to llMedia; if Full, then all media get added;
11280 * otherwise no media get added.
11281 * @param llMedia Caller's list to receive Medium objects which got detached so
11282 * caller can close() them, depending on cleanupMode.
11283 * @return
11284 */
11285HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11286 Snapshot *pSnapshot,
11287 CleanupMode_T cleanupMode,
11288 MediaList &llMedia)
11289{
11290 Assert(isWriteLockOnCurrentThread());
11291
11292 HRESULT rc;
11293
11294 // make a temporary list because i_detachDevice invalidates iterators into
11295 // mMediumAttachments
11296 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11297
11298 for (MediumAttachmentList::iterator
11299 it = llAttachments2.begin();
11300 it != llAttachments2.end();
11301 ++it)
11302 {
11303 ComObjPtr<MediumAttachment> &pAttach = *it;
11304 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11305
11306 if (!pMedium.isNull())
11307 {
11308 AutoCaller mac(pMedium);
11309 if (FAILED(mac.rc())) return mac.rc();
11310 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11311 DeviceType_T devType = pMedium->i_getDeviceType();
11312 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11313 && devType == DeviceType_HardDisk)
11314 || (cleanupMode == CleanupMode_Full)
11315 )
11316 {
11317 llMedia.push_back(pMedium);
11318 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11319 /* Not allowed to keep this lock as below we need the parent
11320 * medium lock, and the lock order is parent to child. */
11321 lock.release();
11322 /*
11323 * Search for medias which are not attached to any machine, but
11324 * in the chain to an attached disk. Mediums are only consided
11325 * if they are:
11326 * - have only one child
11327 * - no references to any machines
11328 * - are of normal medium type
11329 */
11330 while (!pParent.isNull())
11331 {
11332 AutoCaller mac1(pParent);
11333 if (FAILED(mac1.rc())) return mac1.rc();
11334 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11335 if (pParent->i_getChildren().size() == 1)
11336 {
11337 if ( pParent->i_getMachineBackRefCount() == 0
11338 && pParent->i_getType() == MediumType_Normal
11339 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11340 llMedia.push_back(pParent);
11341 }
11342 else
11343 break;
11344 pParent = pParent->i_getParent();
11345 }
11346 }
11347 }
11348
11349 // real machine: then we need to use the proper method
11350 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11351
11352 if (FAILED(rc))
11353 return rc;
11354 }
11355
11356 return S_OK;
11357}
11358
11359/**
11360 * Perform deferred hard disk detachments.
11361 *
11362 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11363 * changed (not backed up).
11364 *
11365 * If @a aOnline is @c true then this method will also unlock the old hard
11366 * disks for which the new implicit diffs were created and will lock these new
11367 * diffs for writing.
11368 *
11369 * @param aOnline Whether the VM was online prior to this operation.
11370 *
11371 * @note Locks this object for writing!
11372 */
11373void Machine::i_commitMedia(bool aOnline /*= false*/)
11374{
11375 AutoCaller autoCaller(this);
11376 AssertComRCReturnVoid(autoCaller.rc());
11377
11378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11379
11380 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11381
11382 HRESULT rc = S_OK;
11383
11384 /* no attach/detach operations -- nothing to do */
11385 if (!mMediumAttachments.isBackedUp())
11386 return;
11387
11388 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11389 bool fMediaNeedsLocking = false;
11390
11391 /* enumerate new attachments */
11392 for (MediumAttachmentList::const_iterator
11393 it = mMediumAttachments->begin();
11394 it != mMediumAttachments->end();
11395 ++it)
11396 {
11397 MediumAttachment *pAttach = *it;
11398
11399 pAttach->i_commit();
11400
11401 Medium *pMedium = pAttach->i_getMedium();
11402 bool fImplicit = pAttach->i_isImplicit();
11403
11404 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11405 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11406 fImplicit));
11407
11408 /** @todo convert all this Machine-based voodoo to MediumAttachment
11409 * based commit logic. */
11410 if (fImplicit)
11411 {
11412 /* convert implicit attachment to normal */
11413 pAttach->i_setImplicit(false);
11414
11415 if ( aOnline
11416 && pMedium
11417 && pAttach->i_getType() == DeviceType_HardDisk
11418 )
11419 {
11420 /* update the appropriate lock list */
11421 MediumLockList *pMediumLockList;
11422 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11423 AssertComRC(rc);
11424 if (pMediumLockList)
11425 {
11426 /* unlock if there's a need to change the locking */
11427 if (!fMediaNeedsLocking)
11428 {
11429 rc = mData->mSession.mLockedMedia.Unlock();
11430 AssertComRC(rc);
11431 fMediaNeedsLocking = true;
11432 }
11433 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11434 AssertComRC(rc);
11435 rc = pMediumLockList->Append(pMedium, true);
11436 AssertComRC(rc);
11437 }
11438 }
11439
11440 continue;
11441 }
11442
11443 if (pMedium)
11444 {
11445 /* was this medium attached before? */
11446 for (MediumAttachmentList::iterator
11447 oldIt = oldAtts.begin();
11448 oldIt != oldAtts.end();
11449 ++oldIt)
11450 {
11451 MediumAttachment *pOldAttach = *oldIt;
11452 if (pOldAttach->i_getMedium() == pMedium)
11453 {
11454 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11455
11456 /* yes: remove from old to avoid de-association */
11457 oldAtts.erase(oldIt);
11458 break;
11459 }
11460 }
11461 }
11462 }
11463
11464 /* enumerate remaining old attachments and de-associate from the
11465 * current machine state */
11466 for (MediumAttachmentList::const_iterator
11467 it = oldAtts.begin();
11468 it != oldAtts.end();
11469 ++it)
11470 {
11471 MediumAttachment *pAttach = *it;
11472 Medium *pMedium = pAttach->i_getMedium();
11473
11474 /* Detach only hard disks, since DVD/floppy media is detached
11475 * instantly in MountMedium. */
11476 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11477 {
11478 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11479
11480 /* now de-associate from the current machine state */
11481 rc = pMedium->i_removeBackReference(mData->mUuid);
11482 AssertComRC(rc);
11483
11484 if (aOnline)
11485 {
11486 /* unlock since medium is not used anymore */
11487 MediumLockList *pMediumLockList;
11488 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11489 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11490 {
11491 /* this happens for online snapshots, there the attachment
11492 * is changing, but only to a diff image created under
11493 * the old one, so there is no separate lock list */
11494 Assert(!pMediumLockList);
11495 }
11496 else
11497 {
11498 AssertComRC(rc);
11499 if (pMediumLockList)
11500 {
11501 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11502 AssertComRC(rc);
11503 }
11504 }
11505 }
11506 }
11507 }
11508
11509 /* take media locks again so that the locking state is consistent */
11510 if (fMediaNeedsLocking)
11511 {
11512 Assert(aOnline);
11513 rc = mData->mSession.mLockedMedia.Lock();
11514 AssertComRC(rc);
11515 }
11516
11517 /* commit the hard disk changes */
11518 mMediumAttachments.commit();
11519
11520 if (i_isSessionMachine())
11521 {
11522 /*
11523 * Update the parent machine to point to the new owner.
11524 * This is necessary because the stored parent will point to the
11525 * session machine otherwise and cause crashes or errors later
11526 * when the session machine gets invalid.
11527 */
11528 /** @todo Change the MediumAttachment class to behave like any other
11529 * class in this regard by creating peer MediumAttachment
11530 * objects for session machines and share the data with the peer
11531 * machine.
11532 */
11533 for (MediumAttachmentList::const_iterator
11534 it = mMediumAttachments->begin();
11535 it != mMediumAttachments->end();
11536 ++it)
11537 (*it)->i_updateParentMachine(mPeer);
11538
11539 /* attach new data to the primary machine and reshare it */
11540 mPeer->mMediumAttachments.attach(mMediumAttachments);
11541 }
11542
11543 return;
11544}
11545
11546/**
11547 * Perform deferred deletion of implicitly created diffs.
11548 *
11549 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11550 * changed (not backed up).
11551 *
11552 * @note Locks this object for writing!
11553 */
11554void Machine::i_rollbackMedia()
11555{
11556 AutoCaller autoCaller(this);
11557 AssertComRCReturnVoid(autoCaller.rc());
11558
11559 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11560 LogFlowThisFunc(("Entering rollbackMedia\n"));
11561
11562 HRESULT rc = S_OK;
11563
11564 /* no attach/detach operations -- nothing to do */
11565 if (!mMediumAttachments.isBackedUp())
11566 return;
11567
11568 /* enumerate new attachments */
11569 for (MediumAttachmentList::const_iterator
11570 it = mMediumAttachments->begin();
11571 it != mMediumAttachments->end();
11572 ++it)
11573 {
11574 MediumAttachment *pAttach = *it;
11575 /* Fix up the backrefs for DVD/floppy media. */
11576 if (pAttach->i_getType() != DeviceType_HardDisk)
11577 {
11578 Medium *pMedium = pAttach->i_getMedium();
11579 if (pMedium)
11580 {
11581 rc = pMedium->i_removeBackReference(mData->mUuid);
11582 AssertComRC(rc);
11583 }
11584 }
11585
11586 (*it)->i_rollback();
11587
11588 pAttach = *it;
11589 /* Fix up the backrefs for DVD/floppy media. */
11590 if (pAttach->i_getType() != DeviceType_HardDisk)
11591 {
11592 Medium *pMedium = pAttach->i_getMedium();
11593 if (pMedium)
11594 {
11595 rc = pMedium->i_addBackReference(mData->mUuid);
11596 AssertComRC(rc);
11597 }
11598 }
11599 }
11600
11601 /** @todo convert all this Machine-based voodoo to MediumAttachment
11602 * based rollback logic. */
11603 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11604
11605 return;
11606}
11607
11608/**
11609 * Returns true if the settings file is located in the directory named exactly
11610 * as the machine; this means, among other things, that the machine directory
11611 * should be auto-renamed.
11612 *
11613 * @param aSettingsDir if not NULL, the full machine settings file directory
11614 * name will be assigned there.
11615 *
11616 * @note Doesn't lock anything.
11617 * @note Not thread safe (must be called from this object's lock).
11618 */
11619bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11620{
11621 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11622 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11623 if (aSettingsDir)
11624 *aSettingsDir = strMachineDirName;
11625 strMachineDirName.stripPath(); // vmname
11626 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11627 strConfigFileOnly.stripPath() // vmname.vbox
11628 .stripSuffix(); // vmname
11629 /** @todo hack, make somehow use of ComposeMachineFilename */
11630 if (mUserData->s.fDirectoryIncludesUUID)
11631 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11632
11633 AssertReturn(!strMachineDirName.isEmpty(), false);
11634 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11635
11636 return strMachineDirName == strConfigFileOnly;
11637}
11638
11639/**
11640 * Discards all changes to machine settings.
11641 *
11642 * @param aNotify Whether to notify the direct session about changes or not.
11643 *
11644 * @note Locks objects for writing!
11645 */
11646void Machine::i_rollback(bool aNotify)
11647{
11648 AutoCaller autoCaller(this);
11649 AssertComRCReturn(autoCaller.rc(), (void)0);
11650
11651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11652
11653 if (!mStorageControllers.isNull())
11654 {
11655 if (mStorageControllers.isBackedUp())
11656 {
11657 /* unitialize all new devices (absent in the backed up list). */
11658 StorageControllerList *backedList = mStorageControllers.backedUpData();
11659 for (StorageControllerList::const_iterator
11660 it = mStorageControllers->begin();
11661 it != mStorageControllers->end();
11662 ++it)
11663 {
11664 if ( std::find(backedList->begin(), backedList->end(), *it)
11665 == backedList->end()
11666 )
11667 {
11668 (*it)->uninit();
11669 }
11670 }
11671
11672 /* restore the list */
11673 mStorageControllers.rollback();
11674 }
11675
11676 /* rollback any changes to devices after restoring the list */
11677 if (mData->flModifications & IsModified_Storage)
11678 {
11679 for (StorageControllerList::const_iterator
11680 it = mStorageControllers->begin();
11681 it != mStorageControllers->end();
11682 ++it)
11683 {
11684 (*it)->i_rollback();
11685 }
11686 }
11687 }
11688
11689 if (!mUSBControllers.isNull())
11690 {
11691 if (mUSBControllers.isBackedUp())
11692 {
11693 /* unitialize all new devices (absent in the backed up list). */
11694 USBControllerList *backedList = mUSBControllers.backedUpData();
11695 for (USBControllerList::const_iterator
11696 it = mUSBControllers->begin();
11697 it != mUSBControllers->end();
11698 ++it)
11699 {
11700 if ( std::find(backedList->begin(), backedList->end(), *it)
11701 == backedList->end()
11702 )
11703 {
11704 (*it)->uninit();
11705 }
11706 }
11707
11708 /* restore the list */
11709 mUSBControllers.rollback();
11710 }
11711
11712 /* rollback any changes to devices after restoring the list */
11713 if (mData->flModifications & IsModified_USB)
11714 {
11715 for (USBControllerList::const_iterator
11716 it = mUSBControllers->begin();
11717 it != mUSBControllers->end();
11718 ++it)
11719 {
11720 (*it)->i_rollback();
11721 }
11722 }
11723 }
11724
11725 mUserData.rollback();
11726
11727 mHWData.rollback();
11728
11729 if (mData->flModifications & IsModified_Storage)
11730 i_rollbackMedia();
11731
11732 if (mBIOSSettings)
11733 mBIOSSettings->i_rollback();
11734
11735 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11736 mRecordingSettings->i_rollback();
11737
11738 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11739 mVRDEServer->i_rollback();
11740
11741 if (mAudioAdapter)
11742 mAudioAdapter->i_rollback();
11743
11744 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11745 mUSBDeviceFilters->i_rollback();
11746
11747 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11748 mBandwidthControl->i_rollback();
11749
11750 if (!mHWData.isNull())
11751 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11752 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11753 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11754 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11755
11756 if (mData->flModifications & IsModified_NetworkAdapters)
11757 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11758 if ( mNetworkAdapters[slot]
11759 && mNetworkAdapters[slot]->i_isModified())
11760 {
11761 mNetworkAdapters[slot]->i_rollback();
11762 networkAdapters[slot] = mNetworkAdapters[slot];
11763 }
11764
11765 if (mData->flModifications & IsModified_SerialPorts)
11766 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11767 if ( mSerialPorts[slot]
11768 && mSerialPorts[slot]->i_isModified())
11769 {
11770 mSerialPorts[slot]->i_rollback();
11771 serialPorts[slot] = mSerialPorts[slot];
11772 }
11773
11774 if (mData->flModifications & IsModified_ParallelPorts)
11775 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11776 if ( mParallelPorts[slot]
11777 && mParallelPorts[slot]->i_isModified())
11778 {
11779 mParallelPorts[slot]->i_rollback();
11780 parallelPorts[slot] = mParallelPorts[slot];
11781 }
11782
11783 if (aNotify)
11784 {
11785 /* inform the direct session about changes */
11786
11787 ComObjPtr<Machine> that = this;
11788 uint32_t flModifications = mData->flModifications;
11789 alock.release();
11790
11791 if (flModifications & IsModified_SharedFolders)
11792 that->i_onSharedFolderChange();
11793
11794 if (flModifications & IsModified_VRDEServer)
11795 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11796 if (flModifications & IsModified_USB)
11797 that->i_onUSBControllerChange();
11798
11799 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11800 if (networkAdapters[slot])
11801 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11802 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11803 if (serialPorts[slot])
11804 that->i_onSerialPortChange(serialPorts[slot]);
11805 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11806 if (parallelPorts[slot])
11807 that->i_onParallelPortChange(parallelPorts[slot]);
11808
11809 if (flModifications & IsModified_Storage)
11810 that->i_onStorageControllerChange();
11811
11812#if 0
11813 if (flModifications & IsModified_BandwidthControl)
11814 that->onBandwidthControlChange();
11815#endif
11816 }
11817}
11818
11819/**
11820 * Commits all the changes to machine settings.
11821 *
11822 * Note that this operation is supposed to never fail.
11823 *
11824 * @note Locks this object and children for writing.
11825 */
11826void Machine::i_commit()
11827{
11828 AutoCaller autoCaller(this);
11829 AssertComRCReturnVoid(autoCaller.rc());
11830
11831 AutoCaller peerCaller(mPeer);
11832 AssertComRCReturnVoid(peerCaller.rc());
11833
11834 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11835
11836 /*
11837 * use safe commit to ensure Snapshot machines (that share mUserData)
11838 * will still refer to a valid memory location
11839 */
11840 mUserData.commitCopy();
11841
11842 mHWData.commit();
11843
11844 if (mMediumAttachments.isBackedUp())
11845 i_commitMedia(Global::IsOnline(mData->mMachineState));
11846
11847 mBIOSSettings->i_commit();
11848 mRecordingSettings->i_commit();
11849 mVRDEServer->i_commit();
11850 mAudioAdapter->i_commit();
11851 mUSBDeviceFilters->i_commit();
11852 mBandwidthControl->i_commit();
11853
11854 /* Since mNetworkAdapters is a list which might have been changed (resized)
11855 * without using the Backupable<> template we need to handle the copying
11856 * of the list entries manually, including the creation of peers for the
11857 * new objects. */
11858 bool commitNetworkAdapters = false;
11859 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11860 if (mPeer)
11861 {
11862 /* commit everything, even the ones which will go away */
11863 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11864 mNetworkAdapters[slot]->i_commit();
11865 /* copy over the new entries, creating a peer and uninit the original */
11866 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11867 for (size_t slot = 0; slot < newSize; slot++)
11868 {
11869 /* look if this adapter has a peer device */
11870 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11871 if (!peer)
11872 {
11873 /* no peer means the adapter is a newly created one;
11874 * create a peer owning data this data share it with */
11875 peer.createObject();
11876 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11877 }
11878 mPeer->mNetworkAdapters[slot] = peer;
11879 }
11880 /* uninit any no longer needed network adapters */
11881 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11882 mNetworkAdapters[slot]->uninit();
11883 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11884 {
11885 if (mPeer->mNetworkAdapters[slot])
11886 mPeer->mNetworkAdapters[slot]->uninit();
11887 }
11888 /* Keep the original network adapter count until this point, so that
11889 * discarding a chipset type change will not lose settings. */
11890 mNetworkAdapters.resize(newSize);
11891 mPeer->mNetworkAdapters.resize(newSize);
11892 }
11893 else
11894 {
11895 /* we have no peer (our parent is the newly created machine);
11896 * just commit changes to the network adapters */
11897 commitNetworkAdapters = true;
11898 }
11899 if (commitNetworkAdapters)
11900 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11901 mNetworkAdapters[slot]->i_commit();
11902
11903 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11904 mSerialPorts[slot]->i_commit();
11905 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11906 mParallelPorts[slot]->i_commit();
11907
11908 bool commitStorageControllers = false;
11909
11910 if (mStorageControllers.isBackedUp())
11911 {
11912 mStorageControllers.commit();
11913
11914 if (mPeer)
11915 {
11916 /* Commit all changes to new controllers (this will reshare data with
11917 * peers for those who have peers) */
11918 StorageControllerList *newList = new StorageControllerList();
11919 for (StorageControllerList::const_iterator
11920 it = mStorageControllers->begin();
11921 it != mStorageControllers->end();
11922 ++it)
11923 {
11924 (*it)->i_commit();
11925
11926 /* look if this controller has a peer device */
11927 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11928 if (!peer)
11929 {
11930 /* no peer means the device is a newly created one;
11931 * create a peer owning data this device share it with */
11932 peer.createObject();
11933 peer->init(mPeer, *it, true /* aReshare */);
11934 }
11935 else
11936 {
11937 /* remove peer from the old list */
11938 mPeer->mStorageControllers->remove(peer);
11939 }
11940 /* and add it to the new list */
11941 newList->push_back(peer);
11942 }
11943
11944 /* uninit old peer's controllers that are left */
11945 for (StorageControllerList::const_iterator
11946 it = mPeer->mStorageControllers->begin();
11947 it != mPeer->mStorageControllers->end();
11948 ++it)
11949 {
11950 (*it)->uninit();
11951 }
11952
11953 /* attach new list of controllers to our peer */
11954 mPeer->mStorageControllers.attach(newList);
11955 }
11956 else
11957 {
11958 /* we have no peer (our parent is the newly created machine);
11959 * just commit changes to devices */
11960 commitStorageControllers = true;
11961 }
11962 }
11963 else
11964 {
11965 /* the list of controllers itself is not changed,
11966 * just commit changes to controllers themselves */
11967 commitStorageControllers = true;
11968 }
11969
11970 if (commitStorageControllers)
11971 {
11972 for (StorageControllerList::const_iterator
11973 it = mStorageControllers->begin();
11974 it != mStorageControllers->end();
11975 ++it)
11976 {
11977 (*it)->i_commit();
11978 }
11979 }
11980
11981 bool commitUSBControllers = false;
11982
11983 if (mUSBControllers.isBackedUp())
11984 {
11985 mUSBControllers.commit();
11986
11987 if (mPeer)
11988 {
11989 /* Commit all changes to new controllers (this will reshare data with
11990 * peers for those who have peers) */
11991 USBControllerList *newList = new USBControllerList();
11992 for (USBControllerList::const_iterator
11993 it = mUSBControllers->begin();
11994 it != mUSBControllers->end();
11995 ++it)
11996 {
11997 (*it)->i_commit();
11998
11999 /* look if this controller has a peer device */
12000 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12001 if (!peer)
12002 {
12003 /* no peer means the device is a newly created one;
12004 * create a peer owning data this device share it with */
12005 peer.createObject();
12006 peer->init(mPeer, *it, true /* aReshare */);
12007 }
12008 else
12009 {
12010 /* remove peer from the old list */
12011 mPeer->mUSBControllers->remove(peer);
12012 }
12013 /* and add it to the new list */
12014 newList->push_back(peer);
12015 }
12016
12017 /* uninit old peer's controllers that are left */
12018 for (USBControllerList::const_iterator
12019 it = mPeer->mUSBControllers->begin();
12020 it != mPeer->mUSBControllers->end();
12021 ++it)
12022 {
12023 (*it)->uninit();
12024 }
12025
12026 /* attach new list of controllers to our peer */
12027 mPeer->mUSBControllers.attach(newList);
12028 }
12029 else
12030 {
12031 /* we have no peer (our parent is the newly created machine);
12032 * just commit changes to devices */
12033 commitUSBControllers = true;
12034 }
12035 }
12036 else
12037 {
12038 /* the list of controllers itself is not changed,
12039 * just commit changes to controllers themselves */
12040 commitUSBControllers = true;
12041 }
12042
12043 if (commitUSBControllers)
12044 {
12045 for (USBControllerList::const_iterator
12046 it = mUSBControllers->begin();
12047 it != mUSBControllers->end();
12048 ++it)
12049 {
12050 (*it)->i_commit();
12051 }
12052 }
12053
12054 if (i_isSessionMachine())
12055 {
12056 /* attach new data to the primary machine and reshare it */
12057 mPeer->mUserData.attach(mUserData);
12058 mPeer->mHWData.attach(mHWData);
12059 /* mmMediumAttachments is reshared by fixupMedia */
12060 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12061 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12062 }
12063}
12064
12065/**
12066 * Copies all the hardware data from the given machine.
12067 *
12068 * Currently, only called when the VM is being restored from a snapshot. In
12069 * particular, this implies that the VM is not running during this method's
12070 * call.
12071 *
12072 * @note This method must be called from under this object's lock.
12073 *
12074 * @note This method doesn't call #i_commit(), so all data remains backed up and
12075 * unsaved.
12076 */
12077void Machine::i_copyFrom(Machine *aThat)
12078{
12079 AssertReturnVoid(!i_isSnapshotMachine());
12080 AssertReturnVoid(aThat->i_isSnapshotMachine());
12081
12082 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12083
12084 mHWData.assignCopy(aThat->mHWData);
12085
12086 // create copies of all shared folders (mHWData after attaching a copy
12087 // contains just references to original objects)
12088 for (HWData::SharedFolderList::iterator
12089 it = mHWData->mSharedFolders.begin();
12090 it != mHWData->mSharedFolders.end();
12091 ++it)
12092 {
12093 ComObjPtr<SharedFolder> folder;
12094 folder.createObject();
12095 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12096 AssertComRC(rc);
12097 *it = folder;
12098 }
12099
12100 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12101 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12102 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12103 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12104 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12105 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12106
12107 /* create private copies of all controllers */
12108 mStorageControllers.backup();
12109 mStorageControllers->clear();
12110 for (StorageControllerList::const_iterator
12111 it = aThat->mStorageControllers->begin();
12112 it != aThat->mStorageControllers->end();
12113 ++it)
12114 {
12115 ComObjPtr<StorageController> ctrl;
12116 ctrl.createObject();
12117 ctrl->initCopy(this, *it);
12118 mStorageControllers->push_back(ctrl);
12119 }
12120
12121 /* create private copies of all USB controllers */
12122 mUSBControllers.backup();
12123 mUSBControllers->clear();
12124 for (USBControllerList::const_iterator
12125 it = aThat->mUSBControllers->begin();
12126 it != aThat->mUSBControllers->end();
12127 ++it)
12128 {
12129 ComObjPtr<USBController> ctrl;
12130 ctrl.createObject();
12131 ctrl->initCopy(this, *it);
12132 mUSBControllers->push_back(ctrl);
12133 }
12134
12135 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12136 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12137 {
12138 if (mNetworkAdapters[slot].isNotNull())
12139 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12140 else
12141 {
12142 unconst(mNetworkAdapters[slot]).createObject();
12143 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12144 }
12145 }
12146 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12147 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12148 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12149 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12150}
12151
12152/**
12153 * Returns whether the given storage controller is hotplug capable.
12154 *
12155 * @returns true if the controller supports hotplugging
12156 * false otherwise.
12157 * @param enmCtrlType The controller type to check for.
12158 */
12159bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12160{
12161 ComPtr<ISystemProperties> systemProperties;
12162 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12163 if (FAILED(rc))
12164 return false;
12165
12166 BOOL aHotplugCapable = FALSE;
12167 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12168
12169 return RT_BOOL(aHotplugCapable);
12170}
12171
12172#ifdef VBOX_WITH_RESOURCE_USAGE_API
12173
12174void Machine::i_getDiskList(MediaList &list)
12175{
12176 for (MediumAttachmentList::const_iterator
12177 it = mMediumAttachments->begin();
12178 it != mMediumAttachments->end();
12179 ++it)
12180 {
12181 MediumAttachment *pAttach = *it;
12182 /* just in case */
12183 AssertContinue(pAttach);
12184
12185 AutoCaller localAutoCallerA(pAttach);
12186 if (FAILED(localAutoCallerA.rc())) continue;
12187
12188 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12189
12190 if (pAttach->i_getType() == DeviceType_HardDisk)
12191 list.push_back(pAttach->i_getMedium());
12192 }
12193}
12194
12195void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12196{
12197 AssertReturnVoid(isWriteLockOnCurrentThread());
12198 AssertPtrReturnVoid(aCollector);
12199
12200 pm::CollectorHAL *hal = aCollector->getHAL();
12201 /* Create sub metrics */
12202 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12203 "Percentage of processor time spent in user mode by the VM process.");
12204 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12205 "Percentage of processor time spent in kernel mode by the VM process.");
12206 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12207 "Size of resident portion of VM process in memory.");
12208 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12209 "Actual size of all VM disks combined.");
12210 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12211 "Network receive rate.");
12212 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12213 "Network transmit rate.");
12214 /* Create and register base metrics */
12215 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12216 cpuLoadUser, cpuLoadKernel);
12217 aCollector->registerBaseMetric(cpuLoad);
12218 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12219 ramUsageUsed);
12220 aCollector->registerBaseMetric(ramUsage);
12221 MediaList disks;
12222 i_getDiskList(disks);
12223 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12224 diskUsageUsed);
12225 aCollector->registerBaseMetric(diskUsage);
12226
12227 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12228 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12229 new pm::AggregateAvg()));
12230 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12231 new pm::AggregateMin()));
12232 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12233 new pm::AggregateMax()));
12234 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12235 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12236 new pm::AggregateAvg()));
12237 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12238 new pm::AggregateMin()));
12239 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12240 new pm::AggregateMax()));
12241
12242 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12243 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12244 new pm::AggregateAvg()));
12245 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12246 new pm::AggregateMin()));
12247 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12248 new pm::AggregateMax()));
12249
12250 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12251 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12252 new pm::AggregateAvg()));
12253 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12254 new pm::AggregateMin()));
12255 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12256 new pm::AggregateMax()));
12257
12258
12259 /* Guest metrics collector */
12260 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12261 aCollector->registerGuest(mCollectorGuest);
12262 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12263
12264 /* Create sub metrics */
12265 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12266 "Percentage of processor time spent in user mode as seen by the guest.");
12267 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12268 "Percentage of processor time spent in kernel mode as seen by the guest.");
12269 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12270 "Percentage of processor time spent idling as seen by the guest.");
12271
12272 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12273 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12274 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12275 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12276 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12277 pm::SubMetric *guestMemCache = new pm::SubMetric(
12278 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12279
12280 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12281 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12282
12283 /* Create and register base metrics */
12284 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12285 machineNetRx, machineNetTx);
12286 aCollector->registerBaseMetric(machineNetRate);
12287
12288 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12289 guestLoadUser, guestLoadKernel, guestLoadIdle);
12290 aCollector->registerBaseMetric(guestCpuLoad);
12291
12292 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12293 guestMemTotal, guestMemFree,
12294 guestMemBalloon, guestMemShared,
12295 guestMemCache, guestPagedTotal);
12296 aCollector->registerBaseMetric(guestCpuMem);
12297
12298 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12299 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12300 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12301 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12302
12303 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12304 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12305 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12306 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12307
12308 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12309 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12310 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12311 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12312
12313 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12314 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12315 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12316 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12317
12318 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12319 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12320 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12321 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12322
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12324 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12327
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12329 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12332
12333 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12334 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12335 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12336 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12337
12338 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12339 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12340 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12342
12343 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12344 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12345 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12346 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12347
12348 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12349 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12350 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12351 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12352}
12353
12354void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12355{
12356 AssertReturnVoid(isWriteLockOnCurrentThread());
12357
12358 if (aCollector)
12359 {
12360 aCollector->unregisterMetricsFor(aMachine);
12361 aCollector->unregisterBaseMetricsFor(aMachine);
12362 }
12363}
12364
12365#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12366
12367
12368////////////////////////////////////////////////////////////////////////////////
12369
12370DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12371
12372HRESULT SessionMachine::FinalConstruct()
12373{
12374 LogFlowThisFunc(("\n"));
12375
12376 mClientToken = NULL;
12377
12378 return BaseFinalConstruct();
12379}
12380
12381void SessionMachine::FinalRelease()
12382{
12383 LogFlowThisFunc(("\n"));
12384
12385 Assert(!mClientToken);
12386 /* paranoia, should not hang around any more */
12387 if (mClientToken)
12388 {
12389 delete mClientToken;
12390 mClientToken = NULL;
12391 }
12392
12393 uninit(Uninit::Unexpected);
12394
12395 BaseFinalRelease();
12396}
12397
12398/**
12399 * @note Must be called only by Machine::LockMachine() from its own write lock.
12400 */
12401HRESULT SessionMachine::init(Machine *aMachine)
12402{
12403 LogFlowThisFuncEnter();
12404 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12405
12406 AssertReturn(aMachine, E_INVALIDARG);
12407
12408 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12409
12410 /* Enclose the state transition NotReady->InInit->Ready */
12411 AutoInitSpan autoInitSpan(this);
12412 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12413
12414 HRESULT rc = S_OK;
12415
12416 RT_ZERO(mAuthLibCtx);
12417
12418 /* create the machine client token */
12419 try
12420 {
12421 mClientToken = new ClientToken(aMachine, this);
12422 if (!mClientToken->isReady())
12423 {
12424 delete mClientToken;
12425 mClientToken = NULL;
12426 rc = E_FAIL;
12427 }
12428 }
12429 catch (std::bad_alloc &)
12430 {
12431 rc = E_OUTOFMEMORY;
12432 }
12433 if (FAILED(rc))
12434 return rc;
12435
12436 /* memorize the peer Machine */
12437 unconst(mPeer) = aMachine;
12438 /* share the parent pointer */
12439 unconst(mParent) = aMachine->mParent;
12440
12441 /* take the pointers to data to share */
12442 mData.share(aMachine->mData);
12443 mSSData.share(aMachine->mSSData);
12444
12445 mUserData.share(aMachine->mUserData);
12446 mHWData.share(aMachine->mHWData);
12447 mMediumAttachments.share(aMachine->mMediumAttachments);
12448
12449 mStorageControllers.allocate();
12450 for (StorageControllerList::const_iterator
12451 it = aMachine->mStorageControllers->begin();
12452 it != aMachine->mStorageControllers->end();
12453 ++it)
12454 {
12455 ComObjPtr<StorageController> ctl;
12456 ctl.createObject();
12457 ctl->init(this, *it);
12458 mStorageControllers->push_back(ctl);
12459 }
12460
12461 mUSBControllers.allocate();
12462 for (USBControllerList::const_iterator
12463 it = aMachine->mUSBControllers->begin();
12464 it != aMachine->mUSBControllers->end();
12465 ++it)
12466 {
12467 ComObjPtr<USBController> ctl;
12468 ctl.createObject();
12469 ctl->init(this, *it);
12470 mUSBControllers->push_back(ctl);
12471 }
12472
12473 unconst(mBIOSSettings).createObject();
12474 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12475 unconst(mRecordingSettings).createObject();
12476 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12477 /* create another VRDEServer object that will be mutable */
12478 unconst(mVRDEServer).createObject();
12479 mVRDEServer->init(this, aMachine->mVRDEServer);
12480 /* create another audio adapter object that will be mutable */
12481 unconst(mAudioAdapter).createObject();
12482 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12483 /* create a list of serial ports that will be mutable */
12484 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12485 {
12486 unconst(mSerialPorts[slot]).createObject();
12487 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12488 }
12489 /* create a list of parallel ports that will be mutable */
12490 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12491 {
12492 unconst(mParallelPorts[slot]).createObject();
12493 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12494 }
12495
12496 /* create another USB device filters object that will be mutable */
12497 unconst(mUSBDeviceFilters).createObject();
12498 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12499
12500 /* create a list of network adapters that will be mutable */
12501 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12502 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12503 {
12504 unconst(mNetworkAdapters[slot]).createObject();
12505 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12506 }
12507
12508 /* create another bandwidth control object that will be mutable */
12509 unconst(mBandwidthControl).createObject();
12510 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12511
12512 /* default is to delete saved state on Saved -> PoweredOff transition */
12513 mRemoveSavedState = true;
12514
12515 /* Confirm a successful initialization when it's the case */
12516 autoInitSpan.setSucceeded();
12517
12518 miNATNetworksStarted = 0;
12519
12520 LogFlowThisFuncLeave();
12521 return rc;
12522}
12523
12524/**
12525 * Uninitializes this session object. If the reason is other than
12526 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12527 * or the client watcher code.
12528 *
12529 * @param aReason uninitialization reason
12530 *
12531 * @note Locks mParent + this object for writing.
12532 */
12533void SessionMachine::uninit(Uninit::Reason aReason)
12534{
12535 LogFlowThisFuncEnter();
12536 LogFlowThisFunc(("reason=%d\n", aReason));
12537
12538 /*
12539 * Strongly reference ourselves to prevent this object deletion after
12540 * mData->mSession.mMachine.setNull() below (which can release the last
12541 * reference and call the destructor). Important: this must be done before
12542 * accessing any members (and before AutoUninitSpan that does it as well).
12543 * This self reference will be released as the very last step on return.
12544 */
12545 ComObjPtr<SessionMachine> selfRef;
12546 if (aReason != Uninit::Unexpected)
12547 selfRef = this;
12548
12549 /* Enclose the state transition Ready->InUninit->NotReady */
12550 AutoUninitSpan autoUninitSpan(this);
12551 if (autoUninitSpan.uninitDone())
12552 {
12553 LogFlowThisFunc(("Already uninitialized\n"));
12554 LogFlowThisFuncLeave();
12555 return;
12556 }
12557
12558 if (autoUninitSpan.initFailed())
12559 {
12560 /* We've been called by init() because it's failed. It's not really
12561 * necessary (nor it's safe) to perform the regular uninit sequence
12562 * below, the following is enough.
12563 */
12564 LogFlowThisFunc(("Initialization failed.\n"));
12565 /* destroy the machine client token */
12566 if (mClientToken)
12567 {
12568 delete mClientToken;
12569 mClientToken = NULL;
12570 }
12571 uninitDataAndChildObjects();
12572 mData.free();
12573 unconst(mParent) = NULL;
12574 unconst(mPeer) = NULL;
12575 LogFlowThisFuncLeave();
12576 return;
12577 }
12578
12579 MachineState_T lastState;
12580 {
12581 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12582 lastState = mData->mMachineState;
12583 }
12584 NOREF(lastState);
12585
12586#ifdef VBOX_WITH_USB
12587 // release all captured USB devices, but do this before requesting the locks below
12588 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12589 {
12590 /* Console::captureUSBDevices() is called in the VM process only after
12591 * setting the machine state to Starting or Restoring.
12592 * Console::detachAllUSBDevices() will be called upon successful
12593 * termination. So, we need to release USB devices only if there was
12594 * an abnormal termination of a running VM.
12595 *
12596 * This is identical to SessionMachine::DetachAllUSBDevices except
12597 * for the aAbnormal argument. */
12598 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12599 AssertComRC(rc);
12600 NOREF(rc);
12601
12602 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12603 if (service)
12604 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12605 }
12606#endif /* VBOX_WITH_USB */
12607
12608 // we need to lock this object in uninit() because the lock is shared
12609 // with mPeer (as well as data we modify below). mParent lock is needed
12610 // by several calls to it.
12611 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12612
12613#ifdef VBOX_WITH_RESOURCE_USAGE_API
12614 /*
12615 * It is safe to call Machine::i_unregisterMetrics() here because
12616 * PerformanceCollector::samplerCallback no longer accesses guest methods
12617 * holding the lock.
12618 */
12619 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12620 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12621 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12622 if (mCollectorGuest)
12623 {
12624 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12625 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12626 mCollectorGuest = NULL;
12627 }
12628#endif
12629
12630 if (aReason == Uninit::Abnormal)
12631 {
12632 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12633
12634 /* reset the state to Aborted */
12635 if (mData->mMachineState != MachineState_Aborted)
12636 i_setMachineState(MachineState_Aborted);
12637 }
12638
12639 // any machine settings modified?
12640 if (mData->flModifications)
12641 {
12642 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12643 i_rollback(false /* aNotify */);
12644 }
12645
12646 mData->mSession.mPID = NIL_RTPROCESS;
12647
12648 if (aReason == Uninit::Unexpected)
12649 {
12650 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12651 * client watcher thread to update the set of machines that have open
12652 * sessions. */
12653 mParent->i_updateClientWatcher();
12654 }
12655
12656 /* uninitialize all remote controls */
12657 if (mData->mSession.mRemoteControls.size())
12658 {
12659 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12660 mData->mSession.mRemoteControls.size()));
12661
12662 /* Always restart a the beginning, since the iterator is invalidated
12663 * by using erase(). */
12664 for (Data::Session::RemoteControlList::iterator
12665 it = mData->mSession.mRemoteControls.begin();
12666 it != mData->mSession.mRemoteControls.end();
12667 it = mData->mSession.mRemoteControls.begin())
12668 {
12669 ComPtr<IInternalSessionControl> pControl = *it;
12670 mData->mSession.mRemoteControls.erase(it);
12671 multilock.release();
12672 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12673 HRESULT rc = pControl->Uninitialize();
12674 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12675 if (FAILED(rc))
12676 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12677 multilock.acquire();
12678 }
12679 mData->mSession.mRemoteControls.clear();
12680 }
12681
12682 /* Remove all references to the NAT network service. The service will stop
12683 * if all references (also from other VMs) are removed. */
12684 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12685 {
12686 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12687 {
12688 BOOL enabled;
12689 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12690 if ( FAILED(hrc)
12691 || !enabled)
12692 continue;
12693
12694 NetworkAttachmentType_T type;
12695 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12696 if ( SUCCEEDED(hrc)
12697 && type == NetworkAttachmentType_NATNetwork)
12698 {
12699 Bstr name;
12700 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12701 if (SUCCEEDED(hrc))
12702 {
12703 multilock.release();
12704 Utf8Str strName(name);
12705 LogRel(("VM '%s' stops using NAT network '%s'\n",
12706 mUserData->s.strName.c_str(), strName.c_str()));
12707 mParent->i_natNetworkRefDec(strName);
12708 multilock.acquire();
12709 }
12710 }
12711 }
12712 }
12713
12714 /*
12715 * An expected uninitialization can come only from #i_checkForDeath().
12716 * Otherwise it means that something's gone really wrong (for example,
12717 * the Session implementation has released the VirtualBox reference
12718 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12719 * etc). However, it's also possible, that the client releases the IPC
12720 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12721 * but the VirtualBox release event comes first to the server process.
12722 * This case is practically possible, so we should not assert on an
12723 * unexpected uninit, just log a warning.
12724 */
12725
12726 if (aReason == Uninit::Unexpected)
12727 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12728
12729 if (aReason != Uninit::Normal)
12730 {
12731 mData->mSession.mDirectControl.setNull();
12732 }
12733 else
12734 {
12735 /* this must be null here (see #OnSessionEnd()) */
12736 Assert(mData->mSession.mDirectControl.isNull());
12737 Assert(mData->mSession.mState == SessionState_Unlocking);
12738 Assert(!mData->mSession.mProgress.isNull());
12739 }
12740 if (mData->mSession.mProgress)
12741 {
12742 if (aReason == Uninit::Normal)
12743 mData->mSession.mProgress->i_notifyComplete(S_OK);
12744 else
12745 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12746 COM_IIDOF(ISession),
12747 getComponentName(),
12748 tr("The VM session was aborted"));
12749 mData->mSession.mProgress.setNull();
12750 }
12751
12752 if (mConsoleTaskData.mProgress)
12753 {
12754 Assert(aReason == Uninit::Abnormal);
12755 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12756 COM_IIDOF(ISession),
12757 getComponentName(),
12758 tr("The VM session was aborted"));
12759 mConsoleTaskData.mProgress.setNull();
12760 }
12761
12762 /* remove the association between the peer machine and this session machine */
12763 Assert( (SessionMachine*)mData->mSession.mMachine == this
12764 || aReason == Uninit::Unexpected);
12765
12766 /* reset the rest of session data */
12767 mData->mSession.mLockType = LockType_Null;
12768 mData->mSession.mMachine.setNull();
12769 mData->mSession.mState = SessionState_Unlocked;
12770 mData->mSession.mName.setNull();
12771
12772 /* destroy the machine client token before leaving the exclusive lock */
12773 if (mClientToken)
12774 {
12775 delete mClientToken;
12776 mClientToken = NULL;
12777 }
12778
12779 /* fire an event */
12780 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12781
12782 uninitDataAndChildObjects();
12783
12784 /* free the essential data structure last */
12785 mData.free();
12786
12787 /* release the exclusive lock before setting the below two to NULL */
12788 multilock.release();
12789
12790 unconst(mParent) = NULL;
12791 unconst(mPeer) = NULL;
12792
12793 AuthLibUnload(&mAuthLibCtx);
12794
12795 LogFlowThisFuncLeave();
12796}
12797
12798// util::Lockable interface
12799////////////////////////////////////////////////////////////////////////////////
12800
12801/**
12802 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12803 * with the primary Machine instance (mPeer).
12804 */
12805RWLockHandle *SessionMachine::lockHandle() const
12806{
12807 AssertReturn(mPeer != NULL, NULL);
12808 return mPeer->lockHandle();
12809}
12810
12811// IInternalMachineControl methods
12812////////////////////////////////////////////////////////////////////////////////
12813
12814/**
12815 * Passes collected guest statistics to performance collector object
12816 */
12817HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12818 ULONG aCpuKernel, ULONG aCpuIdle,
12819 ULONG aMemTotal, ULONG aMemFree,
12820 ULONG aMemBalloon, ULONG aMemShared,
12821 ULONG aMemCache, ULONG aPageTotal,
12822 ULONG aAllocVMM, ULONG aFreeVMM,
12823 ULONG aBalloonedVMM, ULONG aSharedVMM,
12824 ULONG aVmNetRx, ULONG aVmNetTx)
12825{
12826#ifdef VBOX_WITH_RESOURCE_USAGE_API
12827 if (mCollectorGuest)
12828 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12829 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12830 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12831 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12832
12833 return S_OK;
12834#else
12835 NOREF(aValidStats);
12836 NOREF(aCpuUser);
12837 NOREF(aCpuKernel);
12838 NOREF(aCpuIdle);
12839 NOREF(aMemTotal);
12840 NOREF(aMemFree);
12841 NOREF(aMemBalloon);
12842 NOREF(aMemShared);
12843 NOREF(aMemCache);
12844 NOREF(aPageTotal);
12845 NOREF(aAllocVMM);
12846 NOREF(aFreeVMM);
12847 NOREF(aBalloonedVMM);
12848 NOREF(aSharedVMM);
12849 NOREF(aVmNetRx);
12850 NOREF(aVmNetTx);
12851 return E_NOTIMPL;
12852#endif
12853}
12854
12855////////////////////////////////////////////////////////////////////////////////
12856//
12857// SessionMachine task records
12858//
12859////////////////////////////////////////////////////////////////////////////////
12860
12861/**
12862 * Task record for saving the machine state.
12863 */
12864class SessionMachine::SaveStateTask
12865 : public Machine::Task
12866{
12867public:
12868 SaveStateTask(SessionMachine *m,
12869 Progress *p,
12870 const Utf8Str &t,
12871 Reason_T enmReason,
12872 const Utf8Str &strStateFilePath)
12873 : Task(m, p, t),
12874 m_enmReason(enmReason),
12875 m_strStateFilePath(strStateFilePath)
12876 {}
12877
12878private:
12879 void handler()
12880 {
12881 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12882 }
12883
12884 Reason_T m_enmReason;
12885 Utf8Str m_strStateFilePath;
12886
12887 friend class SessionMachine;
12888};
12889
12890/**
12891 * Task thread implementation for SessionMachine::SaveState(), called from
12892 * SessionMachine::taskHandler().
12893 *
12894 * @note Locks this object for writing.
12895 *
12896 * @param task
12897 * @return
12898 */
12899void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12900{
12901 LogFlowThisFuncEnter();
12902
12903 AutoCaller autoCaller(this);
12904 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12905 if (FAILED(autoCaller.rc()))
12906 {
12907 /* we might have been uninitialized because the session was accidentally
12908 * closed by the client, so don't assert */
12909 HRESULT rc = setError(E_FAIL,
12910 tr("The session has been accidentally closed"));
12911 task.m_pProgress->i_notifyComplete(rc);
12912 LogFlowThisFuncLeave();
12913 return;
12914 }
12915
12916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12917
12918 HRESULT rc = S_OK;
12919
12920 try
12921 {
12922 ComPtr<IInternalSessionControl> directControl;
12923 if (mData->mSession.mLockType == LockType_VM)
12924 directControl = mData->mSession.mDirectControl;
12925 if (directControl.isNull())
12926 throw setError(VBOX_E_INVALID_VM_STATE,
12927 tr("Trying to save state without a running VM"));
12928 alock.release();
12929 BOOL fSuspendedBySave;
12930 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12931 Assert(!fSuspendedBySave);
12932 alock.acquire();
12933
12934 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12935 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12936 throw E_FAIL);
12937
12938 if (SUCCEEDED(rc))
12939 {
12940 mSSData->strStateFilePath = task.m_strStateFilePath;
12941
12942 /* save all VM settings */
12943 rc = i_saveSettings(NULL);
12944 // no need to check whether VirtualBox.xml needs saving also since
12945 // we can't have a name change pending at this point
12946 }
12947 else
12948 {
12949 // On failure, set the state to the state we had at the beginning.
12950 i_setMachineState(task.m_machineStateBackup);
12951 i_updateMachineStateOnClient();
12952
12953 // Delete the saved state file (might have been already created).
12954 // No need to check whether this is shared with a snapshot here
12955 // because we certainly created a fresh saved state file here.
12956 RTFileDelete(task.m_strStateFilePath.c_str());
12957 }
12958 }
12959 catch (HRESULT aRC) { rc = aRC; }
12960
12961 task.m_pProgress->i_notifyComplete(rc);
12962
12963 LogFlowThisFuncLeave();
12964}
12965
12966/**
12967 * @note Locks this object for writing.
12968 */
12969HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12970{
12971 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12972}
12973
12974HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12975{
12976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12977
12978 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12979 if (FAILED(rc)) return rc;
12980
12981 if ( mData->mMachineState != MachineState_Running
12982 && mData->mMachineState != MachineState_Paused
12983 )
12984 return setError(VBOX_E_INVALID_VM_STATE,
12985 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12986 Global::stringifyMachineState(mData->mMachineState));
12987
12988 ComObjPtr<Progress> pProgress;
12989 pProgress.createObject();
12990 rc = pProgress->init(i_getVirtualBox(),
12991 static_cast<IMachine *>(this) /* aInitiator */,
12992 tr("Saving the execution state of the virtual machine"),
12993 FALSE /* aCancelable */);
12994 if (FAILED(rc))
12995 return rc;
12996
12997 Utf8Str strStateFilePath;
12998 i_composeSavedStateFilename(strStateFilePath);
12999
13000 /* create and start the task on a separate thread (note that it will not
13001 * start working until we release alock) */
13002 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13003 rc = pTask->createThread();
13004 if (FAILED(rc))
13005 return rc;
13006
13007 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13008 i_setMachineState(MachineState_Saving);
13009 i_updateMachineStateOnClient();
13010
13011 pProgress.queryInterfaceTo(aProgress.asOutParam());
13012
13013 return S_OK;
13014}
13015
13016/**
13017 * @note Locks this object for writing.
13018 */
13019HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13020{
13021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13022
13023 HRESULT rc = i_checkStateDependency(MutableStateDep);
13024 if (FAILED(rc)) return rc;
13025
13026 if ( mData->mMachineState != MachineState_PoweredOff
13027 && mData->mMachineState != MachineState_Teleported
13028 && mData->mMachineState != MachineState_Aborted
13029 )
13030 return setError(VBOX_E_INVALID_VM_STATE,
13031 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13032 Global::stringifyMachineState(mData->mMachineState));
13033
13034 com::Utf8Str stateFilePathFull;
13035 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13036 if (RT_FAILURE(vrc))
13037 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13038 tr("Invalid saved state file path '%s' (%Rrc)"),
13039 aSavedStateFile.c_str(),
13040 vrc);
13041
13042 mSSData->strStateFilePath = stateFilePathFull;
13043
13044 /* The below i_setMachineState() will detect the state transition and will
13045 * update the settings file */
13046
13047 return i_setMachineState(MachineState_Saved);
13048}
13049
13050/**
13051 * @note Locks this object for writing.
13052 */
13053HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13054{
13055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13056
13057 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13058 if (FAILED(rc)) return rc;
13059
13060 if (mData->mMachineState != MachineState_Saved)
13061 return setError(VBOX_E_INVALID_VM_STATE,
13062 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13063 Global::stringifyMachineState(mData->mMachineState));
13064
13065 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13066
13067 /*
13068 * Saved -> PoweredOff transition will be detected in the SessionMachine
13069 * and properly handled.
13070 */
13071 rc = i_setMachineState(MachineState_PoweredOff);
13072 return rc;
13073}
13074
13075
13076/**
13077 * @note Locks the same as #i_setMachineState() does.
13078 */
13079HRESULT SessionMachine::updateState(MachineState_T aState)
13080{
13081 return i_setMachineState(aState);
13082}
13083
13084/**
13085 * @note Locks this object for writing.
13086 */
13087HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13088{
13089 IProgress *pProgress(aProgress);
13090
13091 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13092
13093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13094
13095 if (mData->mSession.mState != SessionState_Locked)
13096 return VBOX_E_INVALID_OBJECT_STATE;
13097
13098 if (!mData->mSession.mProgress.isNull())
13099 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13100
13101 /* If we didn't reference the NAT network service yet, add a reference to
13102 * force a start */
13103 if (miNATNetworksStarted < 1)
13104 {
13105 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13106 {
13107 BOOL enabled;
13108 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13109 if ( FAILED(hrc)
13110 || !enabled)
13111 continue;
13112
13113 NetworkAttachmentType_T type;
13114 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13115 if ( SUCCEEDED(hrc)
13116 && type == NetworkAttachmentType_NATNetwork)
13117 {
13118 Bstr name;
13119 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13120 if (SUCCEEDED(hrc))
13121 {
13122 Utf8Str strName(name);
13123 LogRel(("VM '%s' starts using NAT network '%s'\n",
13124 mUserData->s.strName.c_str(), strName.c_str()));
13125 mPeer->lockHandle()->unlockWrite();
13126 mParent->i_natNetworkRefInc(strName);
13127#ifdef RT_LOCK_STRICT
13128 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13129#else
13130 mPeer->lockHandle()->lockWrite();
13131#endif
13132 }
13133 }
13134 }
13135 miNATNetworksStarted++;
13136 }
13137
13138 LogFlowThisFunc(("returns S_OK.\n"));
13139 return S_OK;
13140}
13141
13142/**
13143 * @note Locks this object for writing.
13144 */
13145HRESULT SessionMachine::endPowerUp(LONG aResult)
13146{
13147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13148
13149 if (mData->mSession.mState != SessionState_Locked)
13150 return VBOX_E_INVALID_OBJECT_STATE;
13151
13152 /* Finalize the LaunchVMProcess progress object. */
13153 if (mData->mSession.mProgress)
13154 {
13155 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13156 mData->mSession.mProgress.setNull();
13157 }
13158
13159 if (SUCCEEDED((HRESULT)aResult))
13160 {
13161#ifdef VBOX_WITH_RESOURCE_USAGE_API
13162 /* The VM has been powered up successfully, so it makes sense
13163 * now to offer the performance metrics for a running machine
13164 * object. Doing it earlier wouldn't be safe. */
13165 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13166 mData->mSession.mPID);
13167#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13168 }
13169
13170 return S_OK;
13171}
13172
13173/**
13174 * @note Locks this object for writing.
13175 */
13176HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13177{
13178 LogFlowThisFuncEnter();
13179
13180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13181
13182 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13183 E_FAIL);
13184
13185 /* create a progress object to track operation completion */
13186 ComObjPtr<Progress> pProgress;
13187 pProgress.createObject();
13188 pProgress->init(i_getVirtualBox(),
13189 static_cast<IMachine *>(this) /* aInitiator */,
13190 tr("Stopping the virtual machine"),
13191 FALSE /* aCancelable */);
13192
13193 /* fill in the console task data */
13194 mConsoleTaskData.mLastState = mData->mMachineState;
13195 mConsoleTaskData.mProgress = pProgress;
13196
13197 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13198 i_setMachineState(MachineState_Stopping);
13199
13200 pProgress.queryInterfaceTo(aProgress.asOutParam());
13201
13202 return S_OK;
13203}
13204
13205/**
13206 * @note Locks this object for writing.
13207 */
13208HRESULT SessionMachine::endPoweringDown(LONG aResult,
13209 const com::Utf8Str &aErrMsg)
13210{
13211 LogFlowThisFuncEnter();
13212
13213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13214
13215 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13216 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13217 && mConsoleTaskData.mLastState != MachineState_Null,
13218 E_FAIL);
13219
13220 /*
13221 * On failure, set the state to the state we had when BeginPoweringDown()
13222 * was called (this is expected by Console::PowerDown() and the associated
13223 * task). On success the VM process already changed the state to
13224 * MachineState_PoweredOff, so no need to do anything.
13225 */
13226 if (FAILED(aResult))
13227 i_setMachineState(mConsoleTaskData.mLastState);
13228
13229 /* notify the progress object about operation completion */
13230 Assert(mConsoleTaskData.mProgress);
13231 if (SUCCEEDED(aResult))
13232 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13233 else
13234 {
13235 if (aErrMsg.length())
13236 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13237 COM_IIDOF(ISession),
13238 getComponentName(),
13239 aErrMsg.c_str());
13240 else
13241 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13242 }
13243
13244 /* clear out the temporary saved state data */
13245 mConsoleTaskData.mLastState = MachineState_Null;
13246 mConsoleTaskData.mProgress.setNull();
13247
13248 LogFlowThisFuncLeave();
13249 return S_OK;
13250}
13251
13252
13253/**
13254 * Goes through the USB filters of the given machine to see if the given
13255 * device matches any filter or not.
13256 *
13257 * @note Locks the same as USBController::hasMatchingFilter() does.
13258 */
13259HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13260 BOOL *aMatched,
13261 ULONG *aMaskedInterfaces)
13262{
13263 LogFlowThisFunc(("\n"));
13264
13265#ifdef VBOX_WITH_USB
13266 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13267#else
13268 NOREF(aDevice);
13269 NOREF(aMaskedInterfaces);
13270 *aMatched = FALSE;
13271#endif
13272
13273 return S_OK;
13274}
13275
13276/**
13277 * @note Locks the same as Host::captureUSBDevice() does.
13278 */
13279HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13280{
13281 LogFlowThisFunc(("\n"));
13282
13283#ifdef VBOX_WITH_USB
13284 /* if captureDeviceForVM() fails, it must have set extended error info */
13285 clearError();
13286 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13287 if (FAILED(rc)) return rc;
13288
13289 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13290 AssertReturn(service, E_FAIL);
13291 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13292#else
13293 NOREF(aId);
13294 return E_NOTIMPL;
13295#endif
13296}
13297
13298/**
13299 * @note Locks the same as Host::detachUSBDevice() does.
13300 */
13301HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13302 BOOL aDone)
13303{
13304 LogFlowThisFunc(("\n"));
13305
13306#ifdef VBOX_WITH_USB
13307 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13308 AssertReturn(service, E_FAIL);
13309 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13310#else
13311 NOREF(aId);
13312 NOREF(aDone);
13313 return E_NOTIMPL;
13314#endif
13315}
13316
13317/**
13318 * Inserts all machine filters to the USB proxy service and then calls
13319 * Host::autoCaptureUSBDevices().
13320 *
13321 * Called by Console from the VM process upon VM startup.
13322 *
13323 * @note Locks what called methods lock.
13324 */
13325HRESULT SessionMachine::autoCaptureUSBDevices()
13326{
13327 LogFlowThisFunc(("\n"));
13328
13329#ifdef VBOX_WITH_USB
13330 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13331 AssertComRC(rc);
13332 NOREF(rc);
13333
13334 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13335 AssertReturn(service, E_FAIL);
13336 return service->autoCaptureDevicesForVM(this);
13337#else
13338 return S_OK;
13339#endif
13340}
13341
13342/**
13343 * Removes all machine filters from the USB proxy service and then calls
13344 * Host::detachAllUSBDevices().
13345 *
13346 * Called by Console from the VM process upon normal VM termination or by
13347 * SessionMachine::uninit() upon abnormal VM termination (from under the
13348 * Machine/SessionMachine lock).
13349 *
13350 * @note Locks what called methods lock.
13351 */
13352HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13353{
13354 LogFlowThisFunc(("\n"));
13355
13356#ifdef VBOX_WITH_USB
13357 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13358 AssertComRC(rc);
13359 NOREF(rc);
13360
13361 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13362 AssertReturn(service, E_FAIL);
13363 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13364#else
13365 NOREF(aDone);
13366 return S_OK;
13367#endif
13368}
13369
13370/**
13371 * @note Locks this object for writing.
13372 */
13373HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13374 ComPtr<IProgress> &aProgress)
13375{
13376 LogFlowThisFuncEnter();
13377
13378 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13379 /*
13380 * We don't assert below because it might happen that a non-direct session
13381 * informs us it is closed right after we've been uninitialized -- it's ok.
13382 */
13383
13384 /* get IInternalSessionControl interface */
13385 ComPtr<IInternalSessionControl> control(aSession);
13386
13387 ComAssertRet(!control.isNull(), E_INVALIDARG);
13388
13389 /* Creating a Progress object requires the VirtualBox lock, and
13390 * thus locking it here is required by the lock order rules. */
13391 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13392
13393 if (control == mData->mSession.mDirectControl)
13394 {
13395 /* The direct session is being normally closed by the client process
13396 * ----------------------------------------------------------------- */
13397
13398 /* go to the closing state (essential for all open*Session() calls and
13399 * for #i_checkForDeath()) */
13400 Assert(mData->mSession.mState == SessionState_Locked);
13401 mData->mSession.mState = SessionState_Unlocking;
13402
13403 /* set direct control to NULL to release the remote instance */
13404 mData->mSession.mDirectControl.setNull();
13405 LogFlowThisFunc(("Direct control is set to NULL\n"));
13406
13407 if (mData->mSession.mProgress)
13408 {
13409 /* finalize the progress, someone might wait if a frontend
13410 * closes the session before powering on the VM. */
13411 mData->mSession.mProgress->notifyComplete(E_FAIL,
13412 COM_IIDOF(ISession),
13413 getComponentName(),
13414 tr("The VM session was closed before any attempt to power it on"));
13415 mData->mSession.mProgress.setNull();
13416 }
13417
13418 /* Create the progress object the client will use to wait until
13419 * #i_checkForDeath() is called to uninitialize this session object after
13420 * it releases the IPC semaphore.
13421 * Note! Because we're "reusing" mProgress here, this must be a proxy
13422 * object just like for LaunchVMProcess. */
13423 Assert(mData->mSession.mProgress.isNull());
13424 ComObjPtr<ProgressProxy> progress;
13425 progress.createObject();
13426 ComPtr<IUnknown> pPeer(mPeer);
13427 progress->init(mParent, pPeer,
13428 Bstr(tr("Closing session")).raw(),
13429 FALSE /* aCancelable */);
13430 progress.queryInterfaceTo(aProgress.asOutParam());
13431 mData->mSession.mProgress = progress;
13432 }
13433 else
13434 {
13435 /* the remote session is being normally closed */
13436 bool found = false;
13437 for (Data::Session::RemoteControlList::iterator
13438 it = mData->mSession.mRemoteControls.begin();
13439 it != mData->mSession.mRemoteControls.end();
13440 ++it)
13441 {
13442 if (control == *it)
13443 {
13444 found = true;
13445 // This MUST be erase(it), not remove(*it) as the latter
13446 // triggers a very nasty use after free due to the place where
13447 // the value "lives".
13448 mData->mSession.mRemoteControls.erase(it);
13449 break;
13450 }
13451 }
13452 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13453 E_INVALIDARG);
13454 }
13455
13456 /* signal the client watcher thread, because the client is going away */
13457 mParent->i_updateClientWatcher();
13458
13459 LogFlowThisFuncLeave();
13460 return S_OK;
13461}
13462
13463HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13464 std::vector<com::Utf8Str> &aValues,
13465 std::vector<LONG64> &aTimestamps,
13466 std::vector<com::Utf8Str> &aFlags)
13467{
13468 LogFlowThisFunc(("\n"));
13469
13470#ifdef VBOX_WITH_GUEST_PROPS
13471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13472
13473 size_t cEntries = mHWData->mGuestProperties.size();
13474 aNames.resize(cEntries);
13475 aValues.resize(cEntries);
13476 aTimestamps.resize(cEntries);
13477 aFlags.resize(cEntries);
13478
13479 size_t i = 0;
13480 for (HWData::GuestPropertyMap::const_iterator
13481 it = mHWData->mGuestProperties.begin();
13482 it != mHWData->mGuestProperties.end();
13483 ++it, ++i)
13484 {
13485 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13486 aNames[i] = it->first;
13487 aValues[i] = it->second.strValue;
13488 aTimestamps[i] = it->second.mTimestamp;
13489
13490 /* If it is NULL, keep it NULL. */
13491 if (it->second.mFlags)
13492 {
13493 GuestPropWriteFlags(it->second.mFlags, szFlags);
13494 aFlags[i] = szFlags;
13495 }
13496 else
13497 aFlags[i] = "";
13498 }
13499 return S_OK;
13500#else
13501 ReturnComNotImplemented();
13502#endif
13503}
13504
13505HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13506 const com::Utf8Str &aValue,
13507 LONG64 aTimestamp,
13508 const com::Utf8Str &aFlags)
13509{
13510 LogFlowThisFunc(("\n"));
13511
13512#ifdef VBOX_WITH_GUEST_PROPS
13513 try
13514 {
13515 /*
13516 * Convert input up front.
13517 */
13518 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13519 if (aFlags.length())
13520 {
13521 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13522 AssertRCReturn(vrc, E_INVALIDARG);
13523 }
13524
13525 /*
13526 * Now grab the object lock, validate the state and do the update.
13527 */
13528
13529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13530
13531 if (!Global::IsOnline(mData->mMachineState))
13532 {
13533 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13534 VBOX_E_INVALID_VM_STATE);
13535 }
13536
13537 i_setModified(IsModified_MachineData);
13538 mHWData.backup();
13539
13540 bool fDelete = !aValue.length();
13541 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13542 if (it != mHWData->mGuestProperties.end())
13543 {
13544 if (!fDelete)
13545 {
13546 it->second.strValue = aValue;
13547 it->second.mTimestamp = aTimestamp;
13548 it->second.mFlags = fFlags;
13549 }
13550 else
13551 mHWData->mGuestProperties.erase(it);
13552
13553 mData->mGuestPropertiesModified = TRUE;
13554 }
13555 else if (!fDelete)
13556 {
13557 HWData::GuestProperty prop;
13558 prop.strValue = aValue;
13559 prop.mTimestamp = aTimestamp;
13560 prop.mFlags = fFlags;
13561
13562 mHWData->mGuestProperties[aName] = prop;
13563 mData->mGuestPropertiesModified = TRUE;
13564 }
13565
13566 alock.release();
13567
13568 mParent->i_onGuestPropertyChange(mData->mUuid,
13569 Bstr(aName).raw(),
13570 Bstr(aValue).raw(),
13571 Bstr(aFlags).raw());
13572 }
13573 catch (...)
13574 {
13575 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13576 }
13577 return S_OK;
13578#else
13579 ReturnComNotImplemented();
13580#endif
13581}
13582
13583
13584HRESULT SessionMachine::lockMedia()
13585{
13586 AutoMultiWriteLock2 alock(this->lockHandle(),
13587 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13588
13589 AssertReturn( mData->mMachineState == MachineState_Starting
13590 || mData->mMachineState == MachineState_Restoring
13591 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13592
13593 clearError();
13594 alock.release();
13595 return i_lockMedia();
13596}
13597
13598HRESULT SessionMachine::unlockMedia()
13599{
13600 HRESULT hrc = i_unlockMedia();
13601 return hrc;
13602}
13603
13604HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13605 ComPtr<IMediumAttachment> &aNewAttachment)
13606{
13607 // request the host lock first, since might be calling Host methods for getting host drives;
13608 // next, protect the media tree all the while we're in here, as well as our member variables
13609 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13610 this->lockHandle(),
13611 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13612
13613 IMediumAttachment *iAttach = aAttachment;
13614 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13615
13616 Utf8Str ctrlName;
13617 LONG lPort;
13618 LONG lDevice;
13619 bool fTempEject;
13620 {
13621 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13622
13623 /* Need to query the details first, as the IMediumAttachment reference
13624 * might be to the original settings, which we are going to change. */
13625 ctrlName = pAttach->i_getControllerName();
13626 lPort = pAttach->i_getPort();
13627 lDevice = pAttach->i_getDevice();
13628 fTempEject = pAttach->i_getTempEject();
13629 }
13630
13631 if (!fTempEject)
13632 {
13633 /* Remember previously mounted medium. The medium before taking the
13634 * backup is not necessarily the same thing. */
13635 ComObjPtr<Medium> oldmedium;
13636 oldmedium = pAttach->i_getMedium();
13637
13638 i_setModified(IsModified_Storage);
13639 mMediumAttachments.backup();
13640
13641 // The backup operation makes the pAttach reference point to the
13642 // old settings. Re-get the correct reference.
13643 pAttach = i_findAttachment(*mMediumAttachments.data(),
13644 ctrlName,
13645 lPort,
13646 lDevice);
13647
13648 {
13649 AutoCaller autoAttachCaller(this);
13650 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13651
13652 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13653 if (!oldmedium.isNull())
13654 oldmedium->i_removeBackReference(mData->mUuid);
13655
13656 pAttach->i_updateMedium(NULL);
13657 pAttach->i_updateEjected();
13658 }
13659
13660 i_setModified(IsModified_Storage);
13661 }
13662 else
13663 {
13664 {
13665 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13666 pAttach->i_updateEjected();
13667 }
13668 }
13669
13670 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13671
13672 return S_OK;
13673}
13674
13675HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13676 com::Utf8Str &aResult)
13677{
13678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13679
13680 HRESULT hr = S_OK;
13681
13682 if (!mAuthLibCtx.hAuthLibrary)
13683 {
13684 /* Load the external authentication library. */
13685 Bstr authLibrary;
13686 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13687
13688 Utf8Str filename = authLibrary;
13689
13690 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13691 if (RT_FAILURE(vrc))
13692 hr = setErrorBoth(E_FAIL, vrc,
13693 tr("Could not load the external authentication library '%s' (%Rrc)"),
13694 filename.c_str(), vrc);
13695 }
13696
13697 /* The auth library might need the machine lock. */
13698 alock.release();
13699
13700 if (FAILED(hr))
13701 return hr;
13702
13703 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13704 {
13705 enum VRDEAuthParams
13706 {
13707 parmUuid = 1,
13708 parmGuestJudgement,
13709 parmUser,
13710 parmPassword,
13711 parmDomain,
13712 parmClientId
13713 };
13714
13715 AuthResult result = AuthResultAccessDenied;
13716
13717 Guid uuid(aAuthParams[parmUuid]);
13718 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13719 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13720
13721 result = AuthLibAuthenticate(&mAuthLibCtx,
13722 uuid.raw(), guestJudgement,
13723 aAuthParams[parmUser].c_str(),
13724 aAuthParams[parmPassword].c_str(),
13725 aAuthParams[parmDomain].c_str(),
13726 u32ClientId);
13727
13728 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13729 size_t cbPassword = aAuthParams[parmPassword].length();
13730 if (cbPassword)
13731 {
13732 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13733 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13734 }
13735
13736 if (result == AuthResultAccessGranted)
13737 aResult = "granted";
13738 else
13739 aResult = "denied";
13740
13741 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13742 aAuthParams[parmUser].c_str(), aResult.c_str()));
13743 }
13744 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13745 {
13746 enum VRDEAuthDisconnectParams
13747 {
13748 parmUuid = 1,
13749 parmClientId
13750 };
13751
13752 Guid uuid(aAuthParams[parmUuid]);
13753 uint32_t u32ClientId = 0;
13754 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13755 }
13756 else
13757 {
13758 hr = E_INVALIDARG;
13759 }
13760
13761 return hr;
13762}
13763
13764// public methods only for internal purposes
13765/////////////////////////////////////////////////////////////////////////////
13766
13767#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13768/**
13769 * Called from the client watcher thread to check for expected or unexpected
13770 * death of the client process that has a direct session to this machine.
13771 *
13772 * On Win32 and on OS/2, this method is called only when we've got the
13773 * mutex (i.e. the client has either died or terminated normally) so it always
13774 * returns @c true (the client is terminated, the session machine is
13775 * uninitialized).
13776 *
13777 * On other platforms, the method returns @c true if the client process has
13778 * terminated normally or abnormally and the session machine was uninitialized,
13779 * and @c false if the client process is still alive.
13780 *
13781 * @note Locks this object for writing.
13782 */
13783bool SessionMachine::i_checkForDeath()
13784{
13785 Uninit::Reason reason;
13786 bool terminated = false;
13787
13788 /* Enclose autoCaller with a block because calling uninit() from under it
13789 * will deadlock. */
13790 {
13791 AutoCaller autoCaller(this);
13792 if (!autoCaller.isOk())
13793 {
13794 /* return true if not ready, to cause the client watcher to exclude
13795 * the corresponding session from watching */
13796 LogFlowThisFunc(("Already uninitialized!\n"));
13797 return true;
13798 }
13799
13800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13801
13802 /* Determine the reason of death: if the session state is Closing here,
13803 * everything is fine. Otherwise it means that the client did not call
13804 * OnSessionEnd() before it released the IPC semaphore. This may happen
13805 * either because the client process has abnormally terminated, or
13806 * because it simply forgot to call ISession::Close() before exiting. We
13807 * threat the latter also as an abnormal termination (see
13808 * Session::uninit() for details). */
13809 reason = mData->mSession.mState == SessionState_Unlocking ?
13810 Uninit::Normal :
13811 Uninit::Abnormal;
13812
13813 if (mClientToken)
13814 terminated = mClientToken->release();
13815 } /* AutoCaller block */
13816
13817 if (terminated)
13818 uninit(reason);
13819
13820 return terminated;
13821}
13822
13823void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13824{
13825 LogFlowThisFunc(("\n"));
13826
13827 strTokenId.setNull();
13828
13829 AutoCaller autoCaller(this);
13830 AssertComRCReturnVoid(autoCaller.rc());
13831
13832 Assert(mClientToken);
13833 if (mClientToken)
13834 mClientToken->getId(strTokenId);
13835}
13836#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13837IToken *SessionMachine::i_getToken()
13838{
13839 LogFlowThisFunc(("\n"));
13840
13841 AutoCaller autoCaller(this);
13842 AssertComRCReturn(autoCaller.rc(), NULL);
13843
13844 Assert(mClientToken);
13845 if (mClientToken)
13846 return mClientToken->getToken();
13847 else
13848 return NULL;
13849}
13850#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13851
13852Machine::ClientToken *SessionMachine::i_getClientToken()
13853{
13854 LogFlowThisFunc(("\n"));
13855
13856 AutoCaller autoCaller(this);
13857 AssertComRCReturn(autoCaller.rc(), NULL);
13858
13859 return mClientToken;
13860}
13861
13862
13863/**
13864 * @note Locks this object for reading.
13865 */
13866HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13867{
13868 LogFlowThisFunc(("\n"));
13869
13870 AutoCaller autoCaller(this);
13871 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13872
13873 ComPtr<IInternalSessionControl> directControl;
13874 {
13875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13876 if (mData->mSession.mLockType == LockType_VM)
13877 directControl = mData->mSession.mDirectControl;
13878 }
13879
13880 /* ignore notifications sent after #OnSessionEnd() is called */
13881 if (!directControl)
13882 return S_OK;
13883
13884 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13885}
13886
13887/**
13888 * @note Locks this object for reading.
13889 */
13890HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13891 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13892 IN_BSTR aGuestIp, LONG aGuestPort)
13893{
13894 LogFlowThisFunc(("\n"));
13895
13896 AutoCaller autoCaller(this);
13897 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13898
13899 ComPtr<IInternalSessionControl> directControl;
13900 {
13901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13902 if (mData->mSession.mLockType == LockType_VM)
13903 directControl = mData->mSession.mDirectControl;
13904 }
13905
13906 /* ignore notifications sent after #OnSessionEnd() is called */
13907 if (!directControl)
13908 return S_OK;
13909 /*
13910 * instead acting like callback we ask IVirtualBox deliver corresponding event
13911 */
13912
13913 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13914 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13915 return S_OK;
13916}
13917
13918/**
13919 * @note Locks this object for reading.
13920 */
13921HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13922{
13923 LogFlowThisFunc(("\n"));
13924
13925 AutoCaller autoCaller(this);
13926 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13927
13928 ComPtr<IInternalSessionControl> directControl;
13929 {
13930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13931 if (mData->mSession.mLockType == LockType_VM)
13932 directControl = mData->mSession.mDirectControl;
13933 }
13934
13935 /* ignore notifications sent after #OnSessionEnd() is called */
13936 if (!directControl)
13937 return S_OK;
13938
13939 return directControl->OnAudioAdapterChange(audioAdapter);
13940}
13941
13942/**
13943 * @note Locks this object for reading.
13944 */
13945HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13946{
13947 LogFlowThisFunc(("\n"));
13948
13949 AutoCaller autoCaller(this);
13950 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13951
13952 ComPtr<IInternalSessionControl> directControl;
13953 {
13954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13955 if (mData->mSession.mLockType == LockType_VM)
13956 directControl = mData->mSession.mDirectControl;
13957 }
13958
13959 /* ignore notifications sent after #OnSessionEnd() is called */
13960 if (!directControl)
13961 return S_OK;
13962
13963 return directControl->OnSerialPortChange(serialPort);
13964}
13965
13966/**
13967 * @note Locks this object for reading.
13968 */
13969HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13970{
13971 LogFlowThisFunc(("\n"));
13972
13973 AutoCaller autoCaller(this);
13974 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13975
13976 ComPtr<IInternalSessionControl> directControl;
13977 {
13978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13979 if (mData->mSession.mLockType == LockType_VM)
13980 directControl = mData->mSession.mDirectControl;
13981 }
13982
13983 /* ignore notifications sent after #OnSessionEnd() is called */
13984 if (!directControl)
13985 return S_OK;
13986
13987 return directControl->OnParallelPortChange(parallelPort);
13988}
13989
13990/**
13991 * @note Locks this object for reading.
13992 */
13993HRESULT SessionMachine::i_onStorageControllerChange()
13994{
13995 LogFlowThisFunc(("\n"));
13996
13997 AutoCaller autoCaller(this);
13998 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13999
14000 ComPtr<IInternalSessionControl> directControl;
14001 {
14002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14003 if (mData->mSession.mLockType == LockType_VM)
14004 directControl = mData->mSession.mDirectControl;
14005 }
14006
14007 /* ignore notifications sent after #OnSessionEnd() is called */
14008 if (!directControl)
14009 return S_OK;
14010
14011 return directControl->OnStorageControllerChange();
14012}
14013
14014/**
14015 * @note Locks this object for reading.
14016 */
14017HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14018{
14019 LogFlowThisFunc(("\n"));
14020
14021 AutoCaller autoCaller(this);
14022 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14023
14024 ComPtr<IInternalSessionControl> directControl;
14025 {
14026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14027 if (mData->mSession.mLockType == LockType_VM)
14028 directControl = mData->mSession.mDirectControl;
14029 }
14030
14031 mParent->i_onMediumChanged(aAttachment);
14032
14033 /* ignore notifications sent after #OnSessionEnd() is called */
14034 if (!directControl)
14035 return S_OK;
14036
14037 return directControl->OnMediumChange(aAttachment, aForce);
14038}
14039
14040/**
14041 * @note Locks this object for reading.
14042 */
14043HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14044{
14045 LogFlowThisFunc(("\n"));
14046
14047 AutoCaller autoCaller(this);
14048 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14049
14050 ComPtr<IInternalSessionControl> directControl;
14051 {
14052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14053 if (mData->mSession.mLockType == LockType_VM)
14054 directControl = mData->mSession.mDirectControl;
14055 }
14056
14057 /* ignore notifications sent after #OnSessionEnd() is called */
14058 if (!directControl)
14059 return S_OK;
14060
14061 return directControl->OnCPUChange(aCPU, aRemove);
14062}
14063
14064HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14065{
14066 LogFlowThisFunc(("\n"));
14067
14068 AutoCaller autoCaller(this);
14069 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14070
14071 ComPtr<IInternalSessionControl> directControl;
14072 {
14073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14074 if (mData->mSession.mLockType == LockType_VM)
14075 directControl = mData->mSession.mDirectControl;
14076 }
14077
14078 /* ignore notifications sent after #OnSessionEnd() is called */
14079 if (!directControl)
14080 return S_OK;
14081
14082 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14083}
14084
14085/**
14086 * @note Locks this object for reading.
14087 */
14088HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14089{
14090 LogFlowThisFunc(("\n"));
14091
14092 AutoCaller autoCaller(this);
14093 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14094
14095 ComPtr<IInternalSessionControl> directControl;
14096 {
14097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14098 if (mData->mSession.mLockType == LockType_VM)
14099 directControl = mData->mSession.mDirectControl;
14100 }
14101
14102 /* ignore notifications sent after #OnSessionEnd() is called */
14103 if (!directControl)
14104 return S_OK;
14105
14106 return directControl->OnVRDEServerChange(aRestart);
14107}
14108
14109/**
14110 * @note Locks this object for reading.
14111 */
14112HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14113{
14114 LogFlowThisFunc(("\n"));
14115
14116 AutoCaller autoCaller(this);
14117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14118
14119 ComPtr<IInternalSessionControl> directControl;
14120 {
14121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14122 if (mData->mSession.mLockType == LockType_VM)
14123 directControl = mData->mSession.mDirectControl;
14124 }
14125
14126 /* ignore notifications sent after #OnSessionEnd() is called */
14127 if (!directControl)
14128 return S_OK;
14129
14130 return directControl->OnRecordingChange(aEnable);
14131}
14132
14133/**
14134 * @note Locks this object for reading.
14135 */
14136HRESULT SessionMachine::i_onUSBControllerChange()
14137{
14138 LogFlowThisFunc(("\n"));
14139
14140 AutoCaller autoCaller(this);
14141 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14142
14143 ComPtr<IInternalSessionControl> directControl;
14144 {
14145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14146 if (mData->mSession.mLockType == LockType_VM)
14147 directControl = mData->mSession.mDirectControl;
14148 }
14149
14150 /* ignore notifications sent after #OnSessionEnd() is called */
14151 if (!directControl)
14152 return S_OK;
14153
14154 return directControl->OnUSBControllerChange();
14155}
14156
14157/**
14158 * @note Locks this object for reading.
14159 */
14160HRESULT SessionMachine::i_onSharedFolderChange()
14161{
14162 LogFlowThisFunc(("\n"));
14163
14164 AutoCaller autoCaller(this);
14165 AssertComRCReturnRC(autoCaller.rc());
14166
14167 ComPtr<IInternalSessionControl> directControl;
14168 {
14169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14170 if (mData->mSession.mLockType == LockType_VM)
14171 directControl = mData->mSession.mDirectControl;
14172 }
14173
14174 /* ignore notifications sent after #OnSessionEnd() is called */
14175 if (!directControl)
14176 return S_OK;
14177
14178 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14179}
14180
14181/**
14182 * @note Locks this object for reading.
14183 */
14184HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14185{
14186 LogFlowThisFunc(("\n"));
14187
14188 AutoCaller autoCaller(this);
14189 AssertComRCReturnRC(autoCaller.rc());
14190
14191 ComPtr<IInternalSessionControl> directControl;
14192 {
14193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14194 if (mData->mSession.mLockType == LockType_VM)
14195 directControl = mData->mSession.mDirectControl;
14196 }
14197
14198 /* ignore notifications sent after #OnSessionEnd() is called */
14199 if (!directControl)
14200 return S_OK;
14201
14202 return directControl->OnClipboardModeChange(aClipboardMode);
14203}
14204
14205/**
14206 * @note Locks this object for reading.
14207 */
14208HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14209{
14210 LogFlowThisFunc(("\n"));
14211
14212 AutoCaller autoCaller(this);
14213 AssertComRCReturnRC(autoCaller.rc());
14214
14215 ComPtr<IInternalSessionControl> directControl;
14216 {
14217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14218 if (mData->mSession.mLockType == LockType_VM)
14219 directControl = mData->mSession.mDirectControl;
14220 }
14221
14222 /* ignore notifications sent after #OnSessionEnd() is called */
14223 if (!directControl)
14224 return S_OK;
14225
14226 return directControl->OnDnDModeChange(aDnDMode);
14227}
14228
14229/**
14230 * @note Locks this object for reading.
14231 */
14232HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14233{
14234 LogFlowThisFunc(("\n"));
14235
14236 AutoCaller autoCaller(this);
14237 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14238
14239 ComPtr<IInternalSessionControl> directControl;
14240 {
14241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14242 if (mData->mSession.mLockType == LockType_VM)
14243 directControl = mData->mSession.mDirectControl;
14244 }
14245
14246 /* ignore notifications sent after #OnSessionEnd() is called */
14247 if (!directControl)
14248 return S_OK;
14249
14250 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14251}
14252
14253/**
14254 * @note Locks this object for reading.
14255 */
14256HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14257{
14258 LogFlowThisFunc(("\n"));
14259
14260 AutoCaller autoCaller(this);
14261 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14262
14263 ComPtr<IInternalSessionControl> directControl;
14264 {
14265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14266 if (mData->mSession.mLockType == LockType_VM)
14267 directControl = mData->mSession.mDirectControl;
14268 }
14269
14270 mParent->i_onStorageDeviceChanged(aAttachment, aRemove, aSilent);
14271
14272 /* ignore notifications sent after #OnSessionEnd() is called */
14273 if (!directControl)
14274 return S_OK;
14275
14276 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14277}
14278
14279/**
14280 * Returns @c true if this machine's USB controller reports it has a matching
14281 * filter for the given USB device and @c false otherwise.
14282 *
14283 * @note locks this object for reading.
14284 */
14285bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14286{
14287 AutoCaller autoCaller(this);
14288 /* silently return if not ready -- this method may be called after the
14289 * direct machine session has been called */
14290 if (!autoCaller.isOk())
14291 return false;
14292
14293#ifdef VBOX_WITH_USB
14294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14295
14296 switch (mData->mMachineState)
14297 {
14298 case MachineState_Starting:
14299 case MachineState_Restoring:
14300 case MachineState_TeleportingIn:
14301 case MachineState_Paused:
14302 case MachineState_Running:
14303 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14304 * elsewhere... */
14305 alock.release();
14306 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14307 default: break;
14308 }
14309#else
14310 NOREF(aDevice);
14311 NOREF(aMaskedIfs);
14312#endif
14313 return false;
14314}
14315
14316/**
14317 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14318 */
14319HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14320 IVirtualBoxErrorInfo *aError,
14321 ULONG aMaskedIfs,
14322 const com::Utf8Str &aCaptureFilename)
14323{
14324 LogFlowThisFunc(("\n"));
14325
14326 AutoCaller autoCaller(this);
14327
14328 /* This notification may happen after the machine object has been
14329 * uninitialized (the session was closed), so don't assert. */
14330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14331
14332 ComPtr<IInternalSessionControl> directControl;
14333 {
14334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14335 if (mData->mSession.mLockType == LockType_VM)
14336 directControl = mData->mSession.mDirectControl;
14337 }
14338
14339 /* fail on notifications sent after #OnSessionEnd() is called, it is
14340 * expected by the caller */
14341 if (!directControl)
14342 return E_FAIL;
14343
14344 /* No locks should be held at this point. */
14345 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14346 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14347
14348 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14349}
14350
14351/**
14352 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14353 */
14354HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14355 IVirtualBoxErrorInfo *aError)
14356{
14357 LogFlowThisFunc(("\n"));
14358
14359 AutoCaller autoCaller(this);
14360
14361 /* This notification may happen after the machine object has been
14362 * uninitialized (the session was closed), so don't assert. */
14363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14364
14365 ComPtr<IInternalSessionControl> directControl;
14366 {
14367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14368 if (mData->mSession.mLockType == LockType_VM)
14369 directControl = mData->mSession.mDirectControl;
14370 }
14371
14372 /* fail on notifications sent after #OnSessionEnd() is called, it is
14373 * expected by the caller */
14374 if (!directControl)
14375 return E_FAIL;
14376
14377 /* No locks should be held at this point. */
14378 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14379 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14380
14381 return directControl->OnUSBDeviceDetach(aId, aError);
14382}
14383
14384// protected methods
14385/////////////////////////////////////////////////////////////////////////////
14386
14387/**
14388 * Deletes the given file if it is no longer in use by either the current machine state
14389 * (if the machine is "saved") or any of the machine's snapshots.
14390 *
14391 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14392 * but is different for each SnapshotMachine. When calling this, the order of calling this
14393 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14394 * is therefore critical. I know, it's all rather messy.
14395 *
14396 * @param strStateFile
14397 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14398 * the test for whether the saved state file is in use.
14399 */
14400void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14401 Snapshot *pSnapshotToIgnore)
14402{
14403 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14404 if ( (strStateFile.isNotEmpty())
14405 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14406 )
14407 // ... and it must also not be shared with other snapshots
14408 if ( !mData->mFirstSnapshot
14409 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14410 // this checks the SnapshotMachine's state file paths
14411 )
14412 RTFileDelete(strStateFile.c_str());
14413}
14414
14415/**
14416 * Locks the attached media.
14417 *
14418 * All attached hard disks are locked for writing and DVD/floppy are locked for
14419 * reading. Parents of attached hard disks (if any) are locked for reading.
14420 *
14421 * This method also performs accessibility check of all media it locks: if some
14422 * media is inaccessible, the method will return a failure and a bunch of
14423 * extended error info objects per each inaccessible medium.
14424 *
14425 * Note that this method is atomic: if it returns a success, all media are
14426 * locked as described above; on failure no media is locked at all (all
14427 * succeeded individual locks will be undone).
14428 *
14429 * The caller is responsible for doing the necessary state sanity checks.
14430 *
14431 * The locks made by this method must be undone by calling #unlockMedia() when
14432 * no more needed.
14433 */
14434HRESULT SessionMachine::i_lockMedia()
14435{
14436 AutoCaller autoCaller(this);
14437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14438
14439 AutoMultiWriteLock2 alock(this->lockHandle(),
14440 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14441
14442 /* bail out if trying to lock things with already set up locking */
14443 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14444
14445 MultiResult mrc(S_OK);
14446
14447 /* Collect locking information for all medium objects attached to the VM. */
14448 for (MediumAttachmentList::const_iterator
14449 it = mMediumAttachments->begin();
14450 it != mMediumAttachments->end();
14451 ++it)
14452 {
14453 MediumAttachment *pAtt = *it;
14454 DeviceType_T devType = pAtt->i_getType();
14455 Medium *pMedium = pAtt->i_getMedium();
14456
14457 MediumLockList *pMediumLockList(new MediumLockList());
14458 // There can be attachments without a medium (floppy/dvd), and thus
14459 // it's impossible to create a medium lock list. It still makes sense
14460 // to have the empty medium lock list in the map in case a medium is
14461 // attached later.
14462 if (pMedium != NULL)
14463 {
14464 MediumType_T mediumType = pMedium->i_getType();
14465 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14466 || mediumType == MediumType_Shareable;
14467 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14468
14469 alock.release();
14470 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14471 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14472 false /* fMediumLockWriteAll */,
14473 NULL,
14474 *pMediumLockList);
14475 alock.acquire();
14476 if (FAILED(mrc))
14477 {
14478 delete pMediumLockList;
14479 mData->mSession.mLockedMedia.Clear();
14480 break;
14481 }
14482 }
14483
14484 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14485 if (FAILED(rc))
14486 {
14487 mData->mSession.mLockedMedia.Clear();
14488 mrc = setError(rc,
14489 tr("Collecting locking information for all attached media failed"));
14490 break;
14491 }
14492 }
14493
14494 if (SUCCEEDED(mrc))
14495 {
14496 /* Now lock all media. If this fails, nothing is locked. */
14497 alock.release();
14498 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14499 alock.acquire();
14500 if (FAILED(rc))
14501 {
14502 mrc = setError(rc,
14503 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14504 }
14505 }
14506
14507 return mrc;
14508}
14509
14510/**
14511 * Undoes the locks made by by #lockMedia().
14512 */
14513HRESULT SessionMachine::i_unlockMedia()
14514{
14515 AutoCaller autoCaller(this);
14516 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14517
14518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14519
14520 /* we may be holding important error info on the current thread;
14521 * preserve it */
14522 ErrorInfoKeeper eik;
14523
14524 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14525 AssertComRC(rc);
14526 return rc;
14527}
14528
14529/**
14530 * Helper to change the machine state (reimplementation).
14531 *
14532 * @note Locks this object for writing.
14533 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14534 * it can cause crashes in random places due to unexpectedly committing
14535 * the current settings. The caller is responsible for that. The call
14536 * to saveStateSettings is fine, because this method does not commit.
14537 */
14538HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14539{
14540 LogFlowThisFuncEnter();
14541 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14542
14543 AutoCaller autoCaller(this);
14544 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14545
14546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14547
14548 MachineState_T oldMachineState = mData->mMachineState;
14549
14550 AssertMsgReturn(oldMachineState != aMachineState,
14551 ("oldMachineState=%s, aMachineState=%s\n",
14552 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14553 E_FAIL);
14554
14555 HRESULT rc = S_OK;
14556
14557 int stsFlags = 0;
14558 bool deleteSavedState = false;
14559
14560 /* detect some state transitions */
14561
14562 if ( ( oldMachineState == MachineState_Saved
14563 && aMachineState == MachineState_Restoring)
14564 || ( ( oldMachineState == MachineState_PoweredOff
14565 || oldMachineState == MachineState_Teleported
14566 || oldMachineState == MachineState_Aborted
14567 )
14568 && ( aMachineState == MachineState_TeleportingIn
14569 || aMachineState == MachineState_Starting
14570 )
14571 )
14572 )
14573 {
14574 /* The EMT thread is about to start */
14575
14576 /* Nothing to do here for now... */
14577
14578 /// @todo NEWMEDIA don't let mDVDDrive and other children
14579 /// change anything when in the Starting/Restoring state
14580 }
14581 else if ( ( oldMachineState == MachineState_Running
14582 || oldMachineState == MachineState_Paused
14583 || oldMachineState == MachineState_Teleporting
14584 || oldMachineState == MachineState_OnlineSnapshotting
14585 || oldMachineState == MachineState_LiveSnapshotting
14586 || oldMachineState == MachineState_Stuck
14587 || oldMachineState == MachineState_Starting
14588 || oldMachineState == MachineState_Stopping
14589 || oldMachineState == MachineState_Saving
14590 || oldMachineState == MachineState_Restoring
14591 || oldMachineState == MachineState_TeleportingPausedVM
14592 || oldMachineState == MachineState_TeleportingIn
14593 )
14594 && ( aMachineState == MachineState_PoweredOff
14595 || aMachineState == MachineState_Saved
14596 || aMachineState == MachineState_Teleported
14597 || aMachineState == MachineState_Aborted
14598 )
14599 )
14600 {
14601 /* The EMT thread has just stopped, unlock attached media. Note that as
14602 * opposed to locking that is done from Console, we do unlocking here
14603 * because the VM process may have aborted before having a chance to
14604 * properly unlock all media it locked. */
14605
14606 unlockMedia();
14607 }
14608
14609 if (oldMachineState == MachineState_Restoring)
14610 {
14611 if (aMachineState != MachineState_Saved)
14612 {
14613 /*
14614 * delete the saved state file once the machine has finished
14615 * restoring from it (note that Console sets the state from
14616 * Restoring to Saved if the VM couldn't restore successfully,
14617 * to give the user an ability to fix an error and retry --
14618 * we keep the saved state file in this case)
14619 */
14620 deleteSavedState = true;
14621 }
14622 }
14623 else if ( oldMachineState == MachineState_Saved
14624 && ( aMachineState == MachineState_PoweredOff
14625 || aMachineState == MachineState_Aborted
14626 || aMachineState == MachineState_Teleported
14627 )
14628 )
14629 {
14630 /*
14631 * delete the saved state after SessionMachine::ForgetSavedState() is called
14632 * or if the VM process (owning a direct VM session) crashed while the
14633 * VM was Saved
14634 */
14635
14636 /// @todo (dmik)
14637 // Not sure that deleting the saved state file just because of the
14638 // client death before it attempted to restore the VM is a good
14639 // thing. But when it crashes we need to go to the Aborted state
14640 // which cannot have the saved state file associated... The only
14641 // way to fix this is to make the Aborted condition not a VM state
14642 // but a bool flag: i.e., when a crash occurs, set it to true and
14643 // change the state to PoweredOff or Saved depending on the
14644 // saved state presence.
14645
14646 deleteSavedState = true;
14647 mData->mCurrentStateModified = TRUE;
14648 stsFlags |= SaveSTS_CurStateModified;
14649 }
14650
14651 if ( aMachineState == MachineState_Starting
14652 || aMachineState == MachineState_Restoring
14653 || aMachineState == MachineState_TeleportingIn
14654 )
14655 {
14656 /* set the current state modified flag to indicate that the current
14657 * state is no more identical to the state in the
14658 * current snapshot */
14659 if (!mData->mCurrentSnapshot.isNull())
14660 {
14661 mData->mCurrentStateModified = TRUE;
14662 stsFlags |= SaveSTS_CurStateModified;
14663 }
14664 }
14665
14666 if (deleteSavedState)
14667 {
14668 if (mRemoveSavedState)
14669 {
14670 Assert(!mSSData->strStateFilePath.isEmpty());
14671
14672 // it is safe to delete the saved state file if ...
14673 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14674 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14675 // ... none of the snapshots share the saved state file
14676 )
14677 RTFileDelete(mSSData->strStateFilePath.c_str());
14678 }
14679
14680 mSSData->strStateFilePath.setNull();
14681 stsFlags |= SaveSTS_StateFilePath;
14682 }
14683
14684 /* redirect to the underlying peer machine */
14685 mPeer->i_setMachineState(aMachineState);
14686
14687 if ( oldMachineState != MachineState_RestoringSnapshot
14688 && ( aMachineState == MachineState_PoweredOff
14689 || aMachineState == MachineState_Teleported
14690 || aMachineState == MachineState_Aborted
14691 || aMachineState == MachineState_Saved))
14692 {
14693 /* the machine has stopped execution
14694 * (or the saved state file was adopted) */
14695 stsFlags |= SaveSTS_StateTimeStamp;
14696 }
14697
14698 if ( ( oldMachineState == MachineState_PoweredOff
14699 || oldMachineState == MachineState_Aborted
14700 || oldMachineState == MachineState_Teleported
14701 )
14702 && aMachineState == MachineState_Saved)
14703 {
14704 /* the saved state file was adopted */
14705 Assert(!mSSData->strStateFilePath.isEmpty());
14706 stsFlags |= SaveSTS_StateFilePath;
14707 }
14708
14709#ifdef VBOX_WITH_GUEST_PROPS
14710 if ( aMachineState == MachineState_PoweredOff
14711 || aMachineState == MachineState_Aborted
14712 || aMachineState == MachineState_Teleported)
14713 {
14714 /* Make sure any transient guest properties get removed from the
14715 * property store on shutdown. */
14716 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14717
14718 /* remove it from the settings representation */
14719 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14720 for (settings::GuestPropertiesList::iterator
14721 it = llGuestProperties.begin();
14722 it != llGuestProperties.end();
14723 /*nothing*/)
14724 {
14725 const settings::GuestProperty &prop = *it;
14726 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14727 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14728 {
14729 it = llGuestProperties.erase(it);
14730 fNeedsSaving = true;
14731 }
14732 else
14733 {
14734 ++it;
14735 }
14736 }
14737
14738 /* Additionally remove it from the HWData representation. Required to
14739 * keep everything in sync, as this is what the API keeps using. */
14740 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14741 for (HWData::GuestPropertyMap::iterator
14742 it = llHWGuestProperties.begin();
14743 it != llHWGuestProperties.end();
14744 /*nothing*/)
14745 {
14746 uint32_t fFlags = it->second.mFlags;
14747 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14748 {
14749 /* iterator where we need to continue after the erase call
14750 * (C++03 is a fact still, and it doesn't return the iterator
14751 * which would allow continuing) */
14752 HWData::GuestPropertyMap::iterator it2 = it;
14753 ++it2;
14754 llHWGuestProperties.erase(it);
14755 it = it2;
14756 fNeedsSaving = true;
14757 }
14758 else
14759 {
14760 ++it;
14761 }
14762 }
14763
14764 if (fNeedsSaving)
14765 {
14766 mData->mCurrentStateModified = TRUE;
14767 stsFlags |= SaveSTS_CurStateModified;
14768 }
14769 }
14770#endif /* VBOX_WITH_GUEST_PROPS */
14771
14772 rc = i_saveStateSettings(stsFlags);
14773
14774 if ( ( oldMachineState != MachineState_PoweredOff
14775 && oldMachineState != MachineState_Aborted
14776 && oldMachineState != MachineState_Teleported
14777 )
14778 && ( aMachineState == MachineState_PoweredOff
14779 || aMachineState == MachineState_Aborted
14780 || aMachineState == MachineState_Teleported
14781 )
14782 )
14783 {
14784 /* we've been shut down for any reason */
14785 /* no special action so far */
14786 }
14787
14788 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14789 LogFlowThisFuncLeave();
14790 return rc;
14791}
14792
14793/**
14794 * Sends the current machine state value to the VM process.
14795 *
14796 * @note Locks this object for reading, then calls a client process.
14797 */
14798HRESULT SessionMachine::i_updateMachineStateOnClient()
14799{
14800 AutoCaller autoCaller(this);
14801 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14802
14803 ComPtr<IInternalSessionControl> directControl;
14804 {
14805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14806 AssertReturn(!!mData, E_FAIL);
14807 if (mData->mSession.mLockType == LockType_VM)
14808 directControl = mData->mSession.mDirectControl;
14809
14810 /* directControl may be already set to NULL here in #OnSessionEnd()
14811 * called too early by the direct session process while there is still
14812 * some operation (like deleting the snapshot) in progress. The client
14813 * process in this case is waiting inside Session::close() for the
14814 * "end session" process object to complete, while #uninit() called by
14815 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14816 * operation to complete. For now, we accept this inconsistent behavior
14817 * and simply do nothing here. */
14818
14819 if (mData->mSession.mState == SessionState_Unlocking)
14820 return S_OK;
14821 }
14822
14823 /* ignore notifications sent after #OnSessionEnd() is called */
14824 if (!directControl)
14825 return S_OK;
14826
14827 return directControl->UpdateMachineState(mData->mMachineState);
14828}
14829
14830
14831/*static*/
14832HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14833{
14834 va_list args;
14835 va_start(args, pcszMsg);
14836 HRESULT rc = setErrorInternal(aResultCode,
14837 getStaticClassIID(),
14838 getStaticComponentName(),
14839 Utf8Str(pcszMsg, args),
14840 false /* aWarning */,
14841 true /* aLogIt */);
14842 va_end(args);
14843 return rc;
14844}
14845
14846
14847HRESULT Machine::updateState(MachineState_T aState)
14848{
14849 NOREF(aState);
14850 ReturnComNotImplemented();
14851}
14852
14853HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14854{
14855 NOREF(aProgress);
14856 ReturnComNotImplemented();
14857}
14858
14859HRESULT Machine::endPowerUp(LONG aResult)
14860{
14861 NOREF(aResult);
14862 ReturnComNotImplemented();
14863}
14864
14865HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14866{
14867 NOREF(aProgress);
14868 ReturnComNotImplemented();
14869}
14870
14871HRESULT Machine::endPoweringDown(LONG aResult,
14872 const com::Utf8Str &aErrMsg)
14873{
14874 NOREF(aResult);
14875 NOREF(aErrMsg);
14876 ReturnComNotImplemented();
14877}
14878
14879HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14880 BOOL *aMatched,
14881 ULONG *aMaskedInterfaces)
14882{
14883 NOREF(aDevice);
14884 NOREF(aMatched);
14885 NOREF(aMaskedInterfaces);
14886 ReturnComNotImplemented();
14887
14888}
14889
14890HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14891{
14892 NOREF(aId); NOREF(aCaptureFilename);
14893 ReturnComNotImplemented();
14894}
14895
14896HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14897 BOOL aDone)
14898{
14899 NOREF(aId);
14900 NOREF(aDone);
14901 ReturnComNotImplemented();
14902}
14903
14904HRESULT Machine::autoCaptureUSBDevices()
14905{
14906 ReturnComNotImplemented();
14907}
14908
14909HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14910{
14911 NOREF(aDone);
14912 ReturnComNotImplemented();
14913}
14914
14915HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14916 ComPtr<IProgress> &aProgress)
14917{
14918 NOREF(aSession);
14919 NOREF(aProgress);
14920 ReturnComNotImplemented();
14921}
14922
14923HRESULT Machine::finishOnlineMergeMedium()
14924{
14925 ReturnComNotImplemented();
14926}
14927
14928HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14929 std::vector<com::Utf8Str> &aValues,
14930 std::vector<LONG64> &aTimestamps,
14931 std::vector<com::Utf8Str> &aFlags)
14932{
14933 NOREF(aNames);
14934 NOREF(aValues);
14935 NOREF(aTimestamps);
14936 NOREF(aFlags);
14937 ReturnComNotImplemented();
14938}
14939
14940HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14941 const com::Utf8Str &aValue,
14942 LONG64 aTimestamp,
14943 const com::Utf8Str &aFlags)
14944{
14945 NOREF(aName);
14946 NOREF(aValue);
14947 NOREF(aTimestamp);
14948 NOREF(aFlags);
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::lockMedia()
14953{
14954 ReturnComNotImplemented();
14955}
14956
14957HRESULT Machine::unlockMedia()
14958{
14959 ReturnComNotImplemented();
14960}
14961
14962HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14963 ComPtr<IMediumAttachment> &aNewAttachment)
14964{
14965 NOREF(aAttachment);
14966 NOREF(aNewAttachment);
14967 ReturnComNotImplemented();
14968}
14969
14970HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14971 ULONG aCpuUser,
14972 ULONG aCpuKernel,
14973 ULONG aCpuIdle,
14974 ULONG aMemTotal,
14975 ULONG aMemFree,
14976 ULONG aMemBalloon,
14977 ULONG aMemShared,
14978 ULONG aMemCache,
14979 ULONG aPagedTotal,
14980 ULONG aMemAllocTotal,
14981 ULONG aMemFreeTotal,
14982 ULONG aMemBalloonTotal,
14983 ULONG aMemSharedTotal,
14984 ULONG aVmNetRx,
14985 ULONG aVmNetTx)
14986{
14987 NOREF(aValidStats);
14988 NOREF(aCpuUser);
14989 NOREF(aCpuKernel);
14990 NOREF(aCpuIdle);
14991 NOREF(aMemTotal);
14992 NOREF(aMemFree);
14993 NOREF(aMemBalloon);
14994 NOREF(aMemShared);
14995 NOREF(aMemCache);
14996 NOREF(aPagedTotal);
14997 NOREF(aMemAllocTotal);
14998 NOREF(aMemFreeTotal);
14999 NOREF(aMemBalloonTotal);
15000 NOREF(aMemSharedTotal);
15001 NOREF(aVmNetRx);
15002 NOREF(aVmNetTx);
15003 ReturnComNotImplemented();
15004}
15005
15006HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15007 com::Utf8Str &aResult)
15008{
15009 NOREF(aAuthParams);
15010 NOREF(aResult);
15011 ReturnComNotImplemented();
15012}
15013
15014HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15015{
15016 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15017
15018 AutoCaller autoCaller(this);
15019 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15020
15021 HRESULT rc = S_OK;
15022
15023 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15024 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15025 rc = getUSBDeviceFilters(usbDeviceFilters);
15026 if (FAILED(rc)) return rc;
15027
15028 NOREF(aFlags);
15029 com::Utf8Str osTypeId;
15030 ComObjPtr<GuestOSType> osType = NULL;
15031
15032 /* Get the guest os type as a string from the VB. */
15033 rc = getOSTypeId(osTypeId);
15034 if (FAILED(rc)) return rc;
15035
15036 /* Get the os type obj that coresponds, can be used to get
15037 * the defaults for this guest OS. */
15038 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15039 if (FAILED(rc)) return rc;
15040
15041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15042
15043 /* Let the OS type select 64-bit ness. */
15044 mHWData->mLongMode = osType->i_is64Bit()
15045 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15046
15047 /* Apply network adapters defaults */
15048 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15049 mNetworkAdapters[slot]->i_applyDefaults(osType);
15050
15051 /* Apply serial port defaults */
15052 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15053 mSerialPorts[slot]->i_applyDefaults(osType);
15054
15055 /* Apply parallel port defaults - not OS dependent*/
15056 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15057 mParallelPorts[slot]->i_applyDefaults();
15058
15059
15060 /* Let the OS type enable the X2APIC */
15061 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15062
15063 /* This one covers IOAPICEnabled. */
15064 mBIOSSettings->i_applyDefaults(osType);
15065
15066 /* Initialize default record settings. */
15067 mRecordingSettings->i_applyDefaults();
15068
15069 /* Initialize default BIOS settings here */
15070 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15071 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15072
15073 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15074 if (FAILED(rc)) return rc;
15075
15076 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15077 if (FAILED(rc)) return rc;
15078
15079 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15080 if (FAILED(rc)) return rc;
15081
15082 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15083 if (FAILED(rc)) return rc;
15084
15085 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15086 if (FAILED(rc)) return rc;
15087
15088 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15089 if (FAILED(rc)) return rc;
15090
15091 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15092 if (FAILED(rc)) return rc;
15093
15094 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15095 if (FAILED(rc)) return rc;
15096
15097 BOOL mRTCUseUTC;
15098 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15099 if (FAILED(rc)) return rc;
15100
15101 setRTCUseUTC(mRTCUseUTC);
15102 if (FAILED(rc)) return rc;
15103
15104 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15105 if (FAILED(rc)) return rc;
15106
15107 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15108 if (FAILED(rc)) return rc;
15109
15110 /* Audio stuff. */
15111 AudioCodecType_T audioCodec;
15112 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15113 if (FAILED(rc)) return rc;
15114
15115 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15116 if (FAILED(rc)) return rc;
15117
15118 AudioControllerType_T audioController;
15119 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15120 if (FAILED(rc)) return rc;
15121
15122 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15123 if (FAILED(rc)) return rc;
15124
15125 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15126 if (FAILED(rc)) return rc;
15127
15128 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15129 if (FAILED(rc)) return rc;
15130
15131 /* Storage Controllers */
15132 StorageControllerType_T hdStorageControllerType;
15133 StorageBus_T hdStorageBusType;
15134 StorageControllerType_T dvdStorageControllerType;
15135 StorageBus_T dvdStorageBusType;
15136 BOOL recommendedFloppy;
15137 ComPtr<IStorageController> floppyController;
15138 ComPtr<IStorageController> hdController;
15139 ComPtr<IStorageController> dvdController;
15140 Utf8Str strFloppyName, strDVDName, strHDName;
15141
15142 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15143 strFloppyName = Bstr("Floppy 1").raw();
15144 strDVDName = Bstr("DVD 1").raw();
15145 strHDName = Bstr("HDD 1").raw();
15146
15147 /* Floppy recommended? add one. */
15148 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15149 if (FAILED(rc)) return rc;
15150 if (recommendedFloppy)
15151 {
15152 rc = addStorageController(strFloppyName,
15153 StorageBus_Floppy,
15154 floppyController);
15155 if (FAILED(rc)) return rc;
15156 }
15157
15158 /* Setup one DVD storage controller. */
15159 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15160 if (FAILED(rc)) return rc;
15161
15162 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15163 if (FAILED(rc)) return rc;
15164
15165 rc = addStorageController(strDVDName,
15166 dvdStorageBusType,
15167 dvdController);
15168 if (FAILED(rc)) return rc;
15169
15170 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15171 if (FAILED(rc)) return rc;
15172
15173 /* Setup one HDD storage controller. */
15174 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15175 if (FAILED(rc)) return rc;
15176
15177 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15178 if (FAILED(rc)) return rc;
15179
15180 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15181 {
15182 rc = addStorageController(strHDName,
15183 hdStorageBusType,
15184 hdController);
15185 if (FAILED(rc)) return rc;
15186
15187 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15188 if (FAILED(rc)) return rc;
15189 }
15190 else
15191 {
15192 /* The HD controller is the same as DVD: */
15193 hdController = dvdController;
15194 strHDName = Bstr("DVD 1").raw();
15195 }
15196
15197 /* Limit the AHCI port count if it's used because windows has trouble with
15198 * too many ports and other guest (OS X in particular) may take extra long
15199 * boot: */
15200
15201 // pParent = static_cast<Medium*>(aP)
15202 IStorageController *temp = hdController;
15203 ComObjPtr<StorageController> storageController;
15204 storageController = static_cast<StorageController *>(temp);
15205
15206 // tempHDController = aHDController;
15207 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15208 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15209 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15210 storageController->COMSETTER(PortCount)(1);
15211
15212 /* USB stuff */
15213
15214 bool ohciEnabled = false;
15215
15216 ComPtr<IUSBController> usbController;
15217 BOOL recommendedUSB3;
15218 BOOL recommendedUSB;
15219 BOOL usbProxyAvailable;
15220
15221 getUSBProxyAvailable(&usbProxyAvailable);
15222 if (FAILED(rc)) return rc;
15223
15224 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15225 if (FAILED(rc)) return rc;
15226 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15227 if (FAILED(rc)) return rc;
15228
15229 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15230 {
15231#ifdef VBOX_WITH_EXTPACK
15232 /* USB 3.0 is only available if the proper ExtPack is installed. */
15233 ExtPackManager *aManager = mParent->i_getExtPackManager();
15234 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15235 {
15236 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15237 if (FAILED(rc)) return rc;
15238
15239 /* xHci includes OHCI */
15240 ohciEnabled = true;
15241 }
15242#endif
15243 }
15244 if ( !ohciEnabled
15245 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15246 {
15247 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15248 if (FAILED(rc)) return rc;
15249 ohciEnabled = true;
15250
15251#ifdef VBOX_WITH_EXTPACK
15252 /* USB 2.0 is only available if the proper ExtPack is installed.
15253 * Note. Configuring EHCI here and providing messages about
15254 * the missing extpack isn't exactly clean, but it is a
15255 * necessary evil to patch over legacy compatability issues
15256 * introduced by the new distribution model. */
15257 ExtPackManager *manager = mParent->i_getExtPackManager();
15258 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15259 {
15260 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15261 if (FAILED(rc)) return rc;
15262 }
15263#endif
15264 }
15265
15266 /* Set recommended human interface device types: */
15267 BOOL recommendedUSBHID;
15268 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15269 if (FAILED(rc)) return rc;
15270
15271 if (recommendedUSBHID)
15272 {
15273 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15274 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15275 if (!ohciEnabled && !usbDeviceFilters.isNull())
15276 {
15277 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15278 if (FAILED(rc)) return rc;
15279 }
15280 }
15281
15282 BOOL recommendedUSBTablet;
15283 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15284 if (FAILED(rc)) return rc;
15285
15286 if (recommendedUSBTablet)
15287 {
15288 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15289 if (!ohciEnabled && !usbDeviceFilters.isNull())
15290 {
15291 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15292 if (FAILED(rc)) return rc;
15293 }
15294 }
15295 return S_OK;
15296}
15297
15298/* This isn't handled entirely by the wrapper generator yet. */
15299#ifdef VBOX_WITH_XPCOM
15300NS_DECL_CLASSINFO(SessionMachine)
15301NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15302
15303NS_DECL_CLASSINFO(SnapshotMachine)
15304NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15305#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